diff --git a/src/cbptc19696.cpp b/src/cbptc19696.cpp
new file mode 100644
index 0000000..fb55052
--- /dev/null
+++ b/src/cbptc19696.cpp
@@ -0,0 +1,341 @@
+/*
+ *	 Copyright (C) 2012 by Ian Wraith
+ *   Copyright (C) 2015 by Jonathan Naylor G4KLX
+ *
+ *   This program is free software; you can redistribute it and/or modify
+ *   it under the terms of the GNU General Public License as published by
+ *   the Free Software Foundation; either version 2 of the License, or
+ *   (at your option) any later version.
+ *
+ *   This program is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *   GNU General Public License for more details.
+ *
+ *   You should have received a copy of the GNU General Public License
+ *   along with this program; if not, write to the Free Software
+ *   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include "cbptc19696.h"
+
+#include "chamming.h"
+#include "cutils.h"
+
+#include <cstdio>
+#include <cassert>
+#include <cstring>
+
+CBPTC19696::CBPTC19696()
+{
+}
+
+CBPTC19696::~CBPTC19696()
+{
+}
+
+// The main decode function
+void CBPTC19696::decode(const unsigned char* in, unsigned char* out)
+{
+    assert(in != NULL);
+    assert(out != NULL);
+    
+    //  Get the raw binary
+    decodeExtractBinary(in);
+    
+    // Deinterleave
+    decodeDeInterleave();
+    
+    // Error check
+    decodeErrorCheck();
+    
+    // Extract Data
+    decodeExtractData(out);
+}
+
+// The main encode function
+void CBPTC19696::encode(const unsigned char* in, unsigned char* out)
+{
+    assert(in != NULL);
+    assert(out != NULL);
+    
+    // Extract Data
+    encodeExtractData(in);
+    
+    // Error check
+    encodeErrorCheck();
+    
+    // Deinterleave
+    encodeInterleave();
+    
+    //  Get the raw binary
+    encodeExtractBinary(out);
+}
+
+void CBPTC19696::decodeExtractBinary(const unsigned char* in)
+{
+    // First block
+    CUtils::byteToBitsBE(in[0U],  m_rawData + 0U);
+    CUtils::byteToBitsBE(in[1U],  m_rawData + 8U);
+    CUtils::byteToBitsBE(in[2U],  m_rawData + 16U);
+    CUtils::byteToBitsBE(in[3U],  m_rawData + 24U);
+    CUtils::byteToBitsBE(in[4U],  m_rawData + 32U);
+    CUtils::byteToBitsBE(in[5U],  m_rawData + 40U);
+    CUtils::byteToBitsBE(in[6U],  m_rawData + 48U);
+    CUtils::byteToBitsBE(in[7U],  m_rawData + 56U);
+    CUtils::byteToBitsBE(in[8U],  m_rawData + 64U);
+    CUtils::byteToBitsBE(in[9U],  m_rawData + 72U);
+    CUtils::byteToBitsBE(in[10U], m_rawData + 80U);
+    CUtils::byteToBitsBE(in[11U], m_rawData + 88U);
+    CUtils::byteToBitsBE(in[12U], m_rawData + 96U);
+    
+    // Handle the two bits
+    bool bits[8U];
+    CUtils::byteToBitsBE(in[20U], bits);
+    m_rawData[98U] = bits[6U];
+    m_rawData[99U] = bits[7U];
+    
+    // Second block
+    CUtils::byteToBitsBE(in[21U], m_rawData + 100U);
+    CUtils::byteToBitsBE(in[22U], m_rawData + 108U);
+    CUtils::byteToBitsBE(in[23U], m_rawData + 116U);
+    CUtils::byteToBitsBE(in[24U], m_rawData + 124U);
+    CUtils::byteToBitsBE(in[25U], m_rawData + 132U);
+    CUtils::byteToBitsBE(in[26U], m_rawData + 140U);
+    CUtils::byteToBitsBE(in[27U], m_rawData + 148U);
+    CUtils::byteToBitsBE(in[28U], m_rawData + 156U);
+    CUtils::byteToBitsBE(in[29U], m_rawData + 164U);
+    CUtils::byteToBitsBE(in[30U], m_rawData + 172U);
+    CUtils::byteToBitsBE(in[31U], m_rawData + 180U);
+    CUtils::byteToBitsBE(in[32U], m_rawData + 188U);
+}
+
+// Deinterleave the raw data
+void CBPTC19696::decodeDeInterleave()
+{
+    for (unsigned int i = 0U; i < 196U; i++)
+        m_deInterData[i] = false;
+    
+    // The first bit is R(3) which is not used so can be ignored
+    for (unsigned int a = 0U; a < 196U; a++)	{
+        // Calculate the interleave sequence
+        unsigned int interleaveSequence = (a * 181U) % 196U;
+        // Shuffle the data
+        m_deInterData[a] = m_rawData[interleaveSequence];
+    }
+}
+
+// Check each row with a Hamming (15,11,3) code and each column with a Hamming (13,9,3) code
+void CBPTC19696::decodeErrorCheck()
+{
+    bool fixing;
+    unsigned int count = 0U;
+    do {
+        fixing = false;
+        
+        // Run through each of the 15 columns
+        bool col[13U];
+        for (unsigned int c = 0U; c < 15U; c++) {
+            unsigned int pos = c + 1U;
+            for (unsigned int a = 0U; a < 13U; a++) {
+                col[a] = m_deInterData[pos];
+                pos = pos + 15U;
+            }
+            
+            if (CHamming::decode1393(col)) {
+                unsigned int pos = c + 1U;
+                for (unsigned int a = 0U; a < 13U; a++) {
+                    m_deInterData[pos] = col[a];
+                    pos = pos + 15U;
+                }
+                
+                fixing = true;
+            }
+        }
+        
+        // Run through each of the 9 rows containing data
+        for (unsigned int r = 0U; r < 9U; r++) {
+            unsigned int pos = (r * 15U) + 1U;
+            if (CHamming::decode15113_2(m_deInterData + pos))
+                fixing = true;
+        }
+        
+        count++;
+    } while (fixing && count < 5U);
+}
+
+// Extract the 96 bits of payload
+void CBPTC19696::decodeExtractData(unsigned char* data) const
+{
+    bool bData[96U];
+    unsigned int pos = 0U;
+    for (unsigned int a = 4U; a <= 11U; a++, pos++)
+        bData[pos] = m_deInterData[a];
+    
+    for (unsigned int a = 16U; a <= 26U; a++, pos++)
+        bData[pos] = m_deInterData[a];
+    
+    for (unsigned int a = 31U; a <= 41U; a++, pos++)
+        bData[pos] = m_deInterData[a];
+    
+    for (unsigned int a = 46U; a <= 56U; a++, pos++)
+        bData[pos] = m_deInterData[a];
+    
+    for (unsigned int a = 61U; a <= 71U; a++, pos++)
+        bData[pos] = m_deInterData[a];
+    
+    for (unsigned int a = 76U; a <= 86U; a++, pos++)
+        bData[pos] = m_deInterData[a];
+    
+    for (unsigned int a = 91U; a <= 101U; a++, pos++)
+        bData[pos] = m_deInterData[a];
+    
+    for (unsigned int a = 106U; a <= 116U; a++, pos++)
+        bData[pos] = m_deInterData[a];
+    
+    for (unsigned int a = 121U; a <= 131U; a++, pos++)
+        bData[pos] = m_deInterData[a];
+    
+    CUtils::bitsToByteBE(bData + 0U,  data[0U]);
+    CUtils::bitsToByteBE(bData + 8U,  data[1U]);
+    CUtils::bitsToByteBE(bData + 16U, data[2U]);
+    CUtils::bitsToByteBE(bData + 24U, data[3U]);
+    CUtils::bitsToByteBE(bData + 32U, data[4U]);
+    CUtils::bitsToByteBE(bData + 40U, data[5U]);
+    CUtils::bitsToByteBE(bData + 48U, data[6U]);
+    CUtils::bitsToByteBE(bData + 56U, data[7U]);
+    CUtils::bitsToByteBE(bData + 64U, data[8U]);
+    CUtils::bitsToByteBE(bData + 72U, data[9U]);
+    CUtils::bitsToByteBE(bData + 80U, data[10U]);
+    CUtils::bitsToByteBE(bData + 88U, data[11U]);
+}
+
+// Extract the 96 bits of payload
+void CBPTC19696::encodeExtractData(const unsigned char* in)
+{
+    bool bData[96U];
+    CUtils::byteToBitsBE(in[0U],  bData + 0U);
+    CUtils::byteToBitsBE(in[1U],  bData + 8U);
+    CUtils::byteToBitsBE(in[2U],  bData + 16U);
+    CUtils::byteToBitsBE(in[3U],  bData + 24U);
+    CUtils::byteToBitsBE(in[4U],  bData + 32U);
+    CUtils::byteToBitsBE(in[5U],  bData + 40U);
+    CUtils::byteToBitsBE(in[6U],  bData + 48U);
+    CUtils::byteToBitsBE(in[7U],  bData + 56U);
+    CUtils::byteToBitsBE(in[8U],  bData + 64U);
+    CUtils::byteToBitsBE(in[9U],  bData + 72U);
+    CUtils::byteToBitsBE(in[10U], bData + 80U);
+    CUtils::byteToBitsBE(in[11U], bData + 88U);
+    
+    for (unsigned int i = 0U; i < 196U; i++)
+        m_deInterData[i] = false;
+    
+    unsigned int pos = 0U;
+    for (unsigned int a = 4U; a <= 11U; a++, pos++)
+        m_deInterData[a] = bData[pos];
+    
+    for (unsigned int a = 16U; a <= 26U; a++, pos++)
+        m_deInterData[a] = bData[pos];
+    
+    for (unsigned int a = 31U; a <= 41U; a++, pos++)
+        m_deInterData[a] = bData[pos];
+    
+    for (unsigned int a = 46U; a <= 56U; a++, pos++)
+        m_deInterData[a] = bData[pos];
+    
+    for (unsigned int a = 61U; a <= 71U; a++, pos++)
+        m_deInterData[a] = bData[pos];
+    
+    for (unsigned int a = 76U; a <= 86U; a++, pos++)
+        m_deInterData[a] = bData[pos];
+    
+    for (unsigned int a = 91U; a <= 101U; a++, pos++)
+        m_deInterData[a] = bData[pos];
+    
+    for (unsigned int a = 106U; a <= 116U; a++, pos++)
+        m_deInterData[a] = bData[pos];
+    
+    for (unsigned int a = 121U; a <= 131U; a++, pos++)
+        m_deInterData[a] = bData[pos];
+}
+
+// Check each row with a Hamming (15,11,3) code and each column with a Hamming (13,9,3) code
+void CBPTC19696::encodeErrorCheck()
+{
+    
+    // Run through each of the 9 rows containing data
+    for (unsigned int r = 0U; r < 9U; r++) {
+        unsigned int pos = (r * 15U) + 1U;
+        CHamming::encode15113_2(m_deInterData + pos);
+    }
+    
+    // Run through each of the 15 columns
+    bool col[13U];
+    for (unsigned int c = 0U; c < 15U; c++) {
+        unsigned int pos = c + 1U;
+        for (unsigned int a = 0U; a < 13U; a++) {
+            col[a] = m_deInterData[pos];
+            pos = pos + 15U;
+        }
+        
+        CHamming::encode1393(col);
+        
+        pos = c + 1U;
+        for (unsigned int a = 0U; a < 13U; a++) {
+            m_deInterData[pos] = col[a];
+            pos = pos + 15U;
+        }
+    }
+}
+
+// Interleave the raw data
+void CBPTC19696::encodeInterleave()
+{
+    for (unsigned int i = 0U; i < 196U; i++)
+        m_rawData[i] = false;
+    
+    // The first bit is R(3) which is not used so can be ignored
+    for (unsigned int a = 0U; a < 196U; a++)	{
+        // Calculate the interleave sequence
+        unsigned int interleaveSequence = (a * 181U) % 196U;
+        // Unshuffle the data
+        m_rawData[interleaveSequence] = m_deInterData[a];
+    }
+}
+
+void CBPTC19696::encodeExtractBinary(unsigned char* data)
+{
+    // First block
+    CUtils::bitsToByteBE(m_rawData + 0U,  data[0U]);
+    CUtils::bitsToByteBE(m_rawData + 8U,  data[1U]);
+    CUtils::bitsToByteBE(m_rawData + 16U, data[2U]);
+    CUtils::bitsToByteBE(m_rawData + 24U, data[3U]);
+    CUtils::bitsToByteBE(m_rawData + 32U, data[4U]);
+    CUtils::bitsToByteBE(m_rawData + 40U, data[5U]);
+    CUtils::bitsToByteBE(m_rawData + 48U, data[6U]);
+    CUtils::bitsToByteBE(m_rawData + 56U, data[7U]);
+    CUtils::bitsToByteBE(m_rawData + 64U, data[8U]);
+    CUtils::bitsToByteBE(m_rawData + 72U, data[9U]);
+    CUtils::bitsToByteBE(m_rawData + 80U, data[10U]);
+    CUtils::bitsToByteBE(m_rawData + 88U, data[11U]);
+    
+    // Handle the two bits
+    unsigned char byte;
+    CUtils::bitsToByteBE(m_rawData + 96U, byte);
+    data[12U] = (data[12U] & 0x3FU) | ((byte >> 0) & 0xC0U);
+    data[20U] = (data[20U] & 0xFCU) | ((byte >> 4) & 0x03U);
+    
+    // Second block
+    CUtils::bitsToByteBE(m_rawData + 100U,  data[21U]);
+    CUtils::bitsToByteBE(m_rawData + 108U,  data[22U]);
+    CUtils::bitsToByteBE(m_rawData + 116U,  data[23U]);
+    CUtils::bitsToByteBE(m_rawData + 124U,  data[24U]);
+    CUtils::bitsToByteBE(m_rawData + 132U,  data[25U]);
+    CUtils::bitsToByteBE(m_rawData + 140U,  data[26U]);
+    CUtils::bitsToByteBE(m_rawData + 148U,  data[27U]);
+    CUtils::bitsToByteBE(m_rawData + 156U,  data[28U]);
+    CUtils::bitsToByteBE(m_rawData + 164U,  data[29U]);
+    CUtils::bitsToByteBE(m_rawData + 172U,  data[30U]);
+    CUtils::bitsToByteBE(m_rawData + 180U,  data[31U]);
+    CUtils::bitsToByteBE(m_rawData + 188U,  data[32U]);
+}
diff --git a/src/cbptc19696.h b/src/cbptc19696.h
new file mode 100644
index 0000000..f1b690c
--- /dev/null
+++ b/src/cbptc19696.h
@@ -0,0 +1,47 @@
+/*
+ *   Copyright (C) 2015 by Jonathan Naylor G4KLX
+ *
+ *   This program is free software; you can redistribute it and/or modify
+ *   it under the terms of the GNU General Public License as published by
+ *   the Free Software Foundation; either version 2 of the License, or
+ *   (at your option) any later version.
+ *
+ *   This program is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *   GNU General Public License for more details.
+ *
+ *   You should have received a copy of the GNU General Public License
+ *   along with this program; if not, write to the Free Software
+ *   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#if !defined(BPTC19696_H)
+#define	BPTC19696_H
+
+class CBPTC19696
+{
+public:
+    CBPTC19696();
+    ~CBPTC19696();
+    
+    void decode(const unsigned char* in, unsigned char* out);
+    
+    void encode(const unsigned char* in, unsigned char* out);
+    
+private:
+    bool m_rawData[196];
+    bool m_deInterData[196];
+    
+    void decodeExtractBinary(const unsigned char* in);
+    void decodeErrorCheck();
+    void decodeDeInterleave();
+    void decodeExtractData(unsigned char* data) const;
+    
+    void encodeExtractData(const unsigned char* in);
+    void encodeInterleave();
+    void encodeErrorCheck();
+    void encodeExtractBinary(unsigned char* data);
+};
+
+#endif
diff --git a/src/cbuffer.cpp b/src/cbuffer.cpp
index 097267b..5213155 100644
--- a/src/cbuffer.cpp
+++ b/src/cbuffer.cpp
@@ -78,12 +78,54 @@ void CBuffer::Append(uint16 ui)
     ::memcpy(&(data()[n]), &ui, sizeof(uint16));
 }
 
+void CBuffer::Append(uint32 ui)
+{
+    int n = (int)size();
+    resize(n+sizeof(uint32));
+    ::memcpy(&(data()[n]), &ui, sizeof(uint32));
+}
+
 void CBuffer::Append(const char *sz)
 {
     Append((uint8 *)sz, (int)strlen(sz));
     Append((uint8)0x00);
 }
 
+void CBuffer::ReplaceAt(int i, uint8 ui)
+{
+    if ( size() < (i+sizeof(uint8)) )
+    {
+        resize(i+sizeof(uint8));
+    }
+    *(uint8 *)(&(data()[i])) = ui;
+}
+
+void CBuffer::ReplaceAt(int i, uint16 ui)
+{
+    if ( size() < (i+sizeof(uint16)) )
+    {
+        resize(i+sizeof(uint16));
+    }
+    *(uint16 *)(&(data()[i])) = ui;
+}
+
+void CBuffer::ReplaceAt(int i, uint32 ui)
+{
+    if ( size() < (i+sizeof(uint32)) )
+    {
+        resize(i+sizeof(uint32));
+    }
+    *(uint32 *)(&(data()[i])) = ui;
+}
+
+void CBuffer::ReplaceAt(int i, const uint8 *ptr, int len)
+{
+    if ( size() < (i+len) )
+    {
+        resize(i+len);
+    }
+    ::memcpy(&(data()[i]), ptr, len);
+}
 
 ////////////////////////////////////////////////////////////////////////////////////////
 // operation
@@ -129,3 +171,31 @@ bool CBuffer::operator ==(const char *sz) const
     }
     return false;
 }
+
+CBuffer::operator const char *() const
+{
+    return (const char *)data();
+}
+
+////////////////////////////////////////////////////////////////////////////////////////
+// debug
+
+void CBuffer::DebugDump(std::ofstream &debugout) const
+{
+    for ( int i = 0; i < size(); i++ )
+    {
+        char sz[16];
+        //sprintf(sz, "%02X", data()[i]);
+        sprintf(sz, "0x%02X", data()[i]);
+        debugout << sz;
+        if ( i == size()-1 )
+        {
+            debugout << std::endl;
+        }
+        else
+        {
+            debugout << ',';
+        }
+    }
+}
+
diff --git a/src/cbuffer.h b/src/cbuffer.h
index 8b6b574..95bbae9 100644
--- a/src/cbuffer.h
+++ b/src/cbuffer.h
@@ -44,16 +44,24 @@ public:
     void Append(uint8, int);
     void Append(uint8);
     void Append(uint16);
+    void Append(uint32);
     void Append(const char *);
+    void ReplaceAt(int, uint8);
+    void ReplaceAt(int, uint16);
+    void ReplaceAt(int, uint32);
+    void ReplaceAt(int, const uint8 *, int);
     
     // operation
     int Compare(uint8 *, int) const;
     int Compare(uint8 *, int, int) const;
     
-    
     // operator
     bool operator ==(const CBuffer &) const;
     bool operator ==(const char *) const;
+    operator const char *() const;
+    
+    // debug
+    void DebugDump(std::ofstream &) const;
 };
 
 ////////////////////////////////////////////////////////////////////////////////////////
diff --git a/src/ccallsign.cpp b/src/ccallsign.cpp
index a928b94..af04e58 100644
--- a/src/ccallsign.cpp
+++ b/src/ccallsign.cpp
@@ -25,6 +25,7 @@
 #include "main.h"
 #include <string.h>
 #include <cctype>
+#include "cdmriddir.h"
 #include "ccallsign.h"
 
 ////////////////////////////////////////////////////////////////////////////////////////
@@ -36,19 +37,39 @@ CCallsign::CCallsign()
     ::memset(m_Callsign, ' ', sizeof(m_Callsign));
     ::memset(m_Suffix, ' ', sizeof(m_Suffix));
     m_Module = ' ';
+    m_uiDmrid = 0;
 }
 
-CCallsign::CCallsign(const char *sz)
+CCallsign::CCallsign(const char *sz, uint32 dmrid)
 {
     // blank all
     ::memset(m_Callsign, ' ', sizeof(m_Callsign));
     ::memset(m_Suffix, ' ', sizeof(m_Suffix));
     m_Module = ' ';
-    // and copy
-    ::memcpy(m_Callsign, sz, MIN(strlen(sz), sizeof(m_Callsign)-1));
-    if ( strlen(sz) >= sizeof(m_Callsign) )
+    m_uiDmrid = dmrid;
+    
+    // and populate
+    if ( ::strlen(sz) > 0 )
     {
-        m_Module = sz[sizeof(m_Callsign)-1];
+        // callsign valid
+        ::memcpy(m_Callsign, sz, MIN(strlen(sz), sizeof(m_Callsign)-1));
+        if ( strlen(sz) >= sizeof(m_Callsign) )
+        {
+            m_Module = sz[sizeof(m_Callsign)-1];
+        }
+        // dmrid ok ?
+        if ( m_uiDmrid == 0 )
+        {
+            m_uiDmrid = g_DmridDir.FindDmrid(*this);
+        }
+    }
+    else if ( m_uiDmrid != 0 )
+    {
+        const CCallsign *callsign = g_DmridDir.FindCallsign(m_uiDmrid);
+        if ( callsign != NULL )
+        {
+            ::memcpy(m_Callsign, callsign->m_Callsign, sizeof(m_Callsign));
+        }
     }
 }
 
@@ -57,9 +78,9 @@ CCallsign::CCallsign(const CCallsign &callsign)
     ::memcpy(m_Callsign, callsign.m_Callsign, sizeof(m_Callsign));
     ::memcpy(m_Suffix, callsign.m_Suffix, sizeof(m_Suffix));
     m_Module = callsign.m_Module;
+    m_uiDmrid = callsign.m_uiDmrid;
 }
 
-
 ////////////////////////////////////////////////////////////////////////////////////////
 // status
 
@@ -97,6 +118,9 @@ bool CCallsign::IsValid(void) const
     // is an letter or space
     valid &= IsLetter(m_Module) || IsSpace(m_Module);
     
+    // dmrid is not tested, as it can be NULL
+    // if station does is not dmr registered
+    
     // done
     return valid;
 }
@@ -114,8 +138,9 @@ bool CCallsign::HasSuffix(void) const
 ////////////////////////////////////////////////////////////////////////////////////////
 // set
 
-void CCallsign::SetCallsign(const char *sz)
+void CCallsign::SetCallsign(const char *sz, bool UpdateDmrid)
 {
+    // set callsign
     ::memset(m_Callsign, ' ', sizeof(m_Callsign));
     m_Module = ' ';
     ::memcpy(m_Callsign, sz, MIN(strlen(sz), sizeof(m_Callsign)-1));
@@ -123,10 +148,16 @@ void CCallsign::SetCallsign(const char *sz)
     {
         m_Module = sz[sizeof(m_Callsign)-1];
     }
+    // and update dmrid
+    if ( UpdateDmrid )
+    {
+        m_uiDmrid = g_DmridDir.FindDmrid(*this);
+    }
 }
 
-void CCallsign::SetCallsign(const uint8 *buffer, int len)
+void CCallsign::SetCallsign(const uint8 *buffer, int len, bool UpdateDmrid)
 {
+    // set callsign
     ::memset(m_Callsign, ' ', sizeof(m_Callsign));
     m_Module = ' ';
     ::memcpy(m_Callsign, buffer, MIN(len, sizeof(m_Callsign)-1));
@@ -141,6 +172,31 @@ void CCallsign::SetCallsign(const uint8 *buffer, int len)
     {
         m_Module = (char)buffer[sizeof(m_Callsign)-1];
     }
+    if ( UpdateDmrid )
+    {
+        m_uiDmrid = g_DmridDir.FindDmrid(*this);
+    }
+}
+
+void CCallsign::SetDmrid(uint32 dmrid, bool UpdateCallsign)
+{
+    m_uiDmrid = dmrid;
+    if ( UpdateCallsign )
+    {
+        const CCallsign *callsign = g_DmridDir.FindCallsign(dmrid);
+        if ( callsign != NULL )
+        {
+            ::memcpy(m_Callsign, callsign->m_Callsign, sizeof(m_Callsign));
+        }
+    }
+}
+
+void CCallsign::SetDmrid(const uint8 *buffer, bool UpdateCallsign)
+{
+    char sz[9];
+    ::memcpy(sz, buffer, 8);
+    sz[8] = 0;
+    SetDmrid((uint32)::strtol(sz, NULL, 16), UpdateCallsign);
 }
 
 void CCallsign::SetModule(char c)
@@ -224,11 +280,17 @@ bool CCallsign::HasSameCallsignWithWildcard(const CCallsign &callsign) const
     return same;
 }
 
+bool CCallsign::HasLowerCallsign(const CCallsign &Callsign) const
+{
+    return (::memcmp(m_Callsign, Callsign.m_Callsign, sizeof(m_Callsign)) < 0);
+}
+
 bool CCallsign::HasSameModule(const CCallsign &Callsign) const
 {
     return (m_Module == Callsign.m_Module);
 }
 
+
 ////////////////////////////////////////////////////////////////////////////////////////
 // operators
 
@@ -236,7 +298,8 @@ bool CCallsign::operator ==(const CCallsign &callsign) const
 {
     return ((::memcmp(callsign.m_Callsign, m_Callsign, sizeof(m_Callsign)) == 0) &&
             (m_Module == callsign.m_Module) &&
-            (::memcmp(callsign.m_Suffix, m_Suffix, sizeof(m_Suffix)) == 0));
+            (::memcmp(callsign.m_Suffix, m_Suffix, sizeof(m_Suffix)) == 0) &&
+            (m_uiDmrid == callsign.m_uiDmrid) );
 }
 
 CCallsign::operator const char *() const
diff --git a/src/ccallsign.h b/src/ccallsign.h
index e1c7f3f..bd5f118 100644
--- a/src/ccallsign.h
+++ b/src/ccallsign.h
@@ -40,7 +40,7 @@ class CCallsign
 public:
     // contructors
     CCallsign();
-    CCallsign(const char *);
+    CCallsign(const char *, uint32 = 0);
     CCallsign(const CCallsign &);
     
     // destructor
@@ -49,11 +49,13 @@ public:
     // status
     bool IsValid(void) const;
     bool HasSuffix(void) const;
-    bool HasModule(void) const      { return m_Module != ' '; }
+    bool HasModule(void) const                { return m_Module != ' '; }
     
     // set
-    void SetCallsign(const char *);
-    void SetCallsign(const uint8 *, int);
+    void SetCallsign(const char *, bool = true);
+    void SetCallsign(const uint8 *, int, bool = true);
+    void SetDmrid(uint32, bool = true);
+    void SetDmrid(const uint8 *, bool = true);
     void SetModule(char);
     void SetSuffix(const char *);
     void SetSuffix(const uint8 *, int);
@@ -64,12 +66,14 @@ public:
     // get
     void GetCallsign(uint8 *) const;
     void GetCallsignString(char *) const;
+    uint32 GetDmrid(void) const             { return m_uiDmrid; }
     void GetSuffix(uint8 *) const;
-    char GetModule(void) const     { return m_Module; }
+    char GetModule(void) const              { return m_Module; }
     
     // compare
     bool HasSameCallsign(const CCallsign &) const;
     bool HasSameCallsignWithWildcard(const CCallsign &) const;
+    bool HasLowerCallsign(const CCallsign &) const;
     bool HasSameModule(const CCallsign &) const;
    
     // operators
@@ -88,6 +92,7 @@ protected:
     char    m_Suffix[CALLSUFFIX_LEN];
     char    m_Module;
     char	m_sz[CALLSIGN_LEN+CALLSUFFIX_LEN+5];
+    uint32  m_uiDmrid;
 };
 
 ////////////////////////////////////////////////////////////////////////////////////////
diff --git a/src/cclient.h b/src/cclient.h
index 15f74fa..3fa2590 100644
--- a/src/cclient.h
+++ b/src/cclient.h
@@ -55,6 +55,7 @@ public:
     const CIp &GetIp(void) const                        { return m_Ip; }
     bool HasModule(void) const                          { return m_Callsign.HasModule(); }
     char GetModule(void) const                          { return m_Callsign.GetModule(); }
+    bool HasReflectorModule(void) const                 { return m_ReflectorModule != ' '; }
     char GetReflectorModule(void) const                 { return m_ReflectorModule; }
    
     // set
@@ -64,6 +65,7 @@ public:
     // identity
     virtual int GetProtocol(void) const                 { return PROTOCOL_NONE; }
     virtual int GetProtocolRevision(void) const         { return 0; }
+    virtual int GetCodec(void) const                    { return CODEC_NONE; }
     virtual const char *GetProtocolName(void) const     { return "none"; }
     virtual bool IsNode(void) const                     { return false; }
     virtual bool IsPeer(void) const                     { return false; }
diff --git a/src/cclients.cpp b/src/cclients.cpp
index 32315e5..3818865 100644
--- a/src/cclients.cpp
+++ b/src/cclients.cpp
@@ -85,8 +85,12 @@ void CClients::AddClient(CClient *client)
         // and append
         m_Clients.push_back(client);
         std::cout << "New client " << client->GetCallsign() << " at " << client->GetIp() 
-                  << " added with protocol " << client->GetProtocolName()
-                  << " on module " << client->GetReflectorModule() << std::endl;
+                  << " added with protocol " << client->GetProtocol();
+        if ( client->GetReflectorModule() != ' ' )
+        {
+            std::cout << " on module " << client->GetReflectorModule();
+        }
+        std::cout << std::endl;
         // notify
         g_Reflector.OnClientsChanged();
     }
@@ -106,8 +110,12 @@ void CClients::RemoveClient(CClient *client)
             {
                 // remove it
                 std::cout << "Client " << m_Clients[i]->GetCallsign() << " at " << m_Clients[i]->GetIp()
-                          << " removed with protocol " << client->GetProtocolName()
-                          << " on module " << client->GetReflectorModule() << std::endl;
+                          << " removed with protocol " << client->GetProtocol();
+                if ( client->GetReflectorModule() != ' ' )
+                {
+                    std::cout << " on module " << client->GetReflectorModule();
+                }
+                std::cout << std::endl;
                 delete m_Clients[i];
                 m_Clients.erase(m_Clients.begin()+i);
                 found = true;
diff --git a/src/ccodecstream.cpp b/src/ccodecstream.cpp
new file mode 100644
index 0000000..ca4ac1e
--- /dev/null
+++ b/src/ccodecstream.cpp
@@ -0,0 +1,264 @@
+//
+//  ccodecstream.cpp
+//  xlxd
+//
+//  Created by Jean-Luc Deltombe (LX3JL) on 13/04/2017.
+//  Copyright © 2015 Jean-Luc Deltombe (LX3JL). All rights reserved.
+//
+// ----------------------------------------------------------------------------
+//    This file is part of xlxd.
+//
+//    xlxd is free software: you can redistribute it and/or modify
+//    it under the terms of the GNU General Public License as published by
+//    the Free Software Foundation, either version 3 of the License, or
+//    (at your option) any later version.
+//
+//    xlxd is distributed in the hope that it will be useful,
+//    but WITHOUT ANY WARRANTY; without even the implied warranty of
+//    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+//    GNU General Public License for more details.
+//
+//    You should have received a copy of the GNU General Public License
+//    along with Foobar.  If not, see <http://www.gnu.org/licenses/>.
+// ----------------------------------------------------------------------------
+
+#include "main.h"
+#include <string.h>
+#include "ccodecstream.h"
+#include "cdvframepacket.h"
+#include "creflector.h"
+
+////////////////////////////////////////////////////////////////////////////////////////
+// define
+
+
+
+////////////////////////////////////////////////////////////////////////////////////////
+// constructor
+
+CCodecStream::CCodecStream(CPacketStream *PacketStream, uint16 uiId, uint8 uiCodecIn, uint8 uiCodecOut)
+{
+    m_bStopThread = false;
+    m_pThread = NULL;
+    m_uiStreamId = uiId;
+    m_uiPid = 0;
+    m_uiCodecIn = uiCodecIn;
+    m_uiCodecOut = uiCodecOut;
+    m_bConnected = false;
+    m_fPingMin = -1;
+    m_fPingMax = -1;
+    m_fPingSum = 0;
+    m_fPingCount = 0;
+    m_uiTotalPackets = 0;
+    m_uiTimeoutPackets = 0;
+    m_PacketStream = PacketStream;
+}
+
+////////////////////////////////////////////////////////////////////////////////////////
+// destructor
+
+CCodecStream::~CCodecStream()
+{
+    // close socket
+    m_Socket.Close();
+    
+    // kill threads
+    m_bStopThread = true;
+    if ( m_pThread != NULL )
+    {
+        m_pThread->join();
+        delete m_pThread;
+    }
+    
+    // empty local queue
+    while ( !m_LocalQueue.empty() )
+    {
+        delete m_LocalQueue.front();
+        m_LocalQueue.pop();
+    }
+    // empty ourselves
+    while ( !empty() )
+    {
+        delete front();
+        pop();
+    }
+}
+
+////////////////////////////////////////////////////////////////////////////////////////
+// initialization
+
+bool CCodecStream::Init(uint16 uiPort)
+{
+    bool ok;
+    
+    // reset stop flag
+    m_bStopThread = false;
+    
+    // create server's IP
+    m_Ip = g_Reflector.GetTrasncoderIp();
+    m_uiPort = uiPort;
+    
+    // create our socket
+    ok = m_Socket.Open(uiPort);
+    if ( ok )
+    {
+        // start  thread;
+        m_pThread = new std::thread(CCodecStream::Thread, this);
+        m_bConnected = true;
+    }
+    else
+    {
+        std::cout << "Error opening socket on port UDP" << uiPort << " on ip " << g_Reflector.GetListenIp() << std::endl;
+        m_bConnected = false;
+    }
+    
+    // done
+    return ok;
+}
+
+void CCodecStream::Close(void)
+{
+    // close socket
+    m_bConnected = false;
+    m_Socket.Close();
+    
+    // kill threads
+    m_bStopThread = true;
+    if ( m_pThread != NULL )
+    {
+        m_pThread->join();
+        delete m_pThread;
+        m_pThread = NULL;
+    }
+}
+
+////////////////////////////////////////////////////////////////////////////////////////
+// get
+
+bool CCodecStream::IsEmpty(void) const
+{
+    return (m_LocalQueue.empty() && m_PacketStream->empty());
+}
+
+////////////////////////////////////////////////////////////////////////////////////////
+// thread
+
+void CCodecStream::Thread(CCodecStream *This)
+{
+    while ( !This->m_bStopThread )
+    {
+        This->Task();
+    }
+}
+
+void CCodecStream::Task(void)
+{
+    CBuffer Buffer;
+    CIp     Ip;
+    uint8   Ambe[AMBE_SIZE];
+    uint8   DStarSync[] = { 0x55,0x2D,0x16 };
+    
+    // any packet from transcoder
+    if ( m_Socket.Receive(&Buffer, &Ip, 5) != -1 )
+    {
+        // crack
+        if ( IsValidAmbePacket(Buffer, Ambe) )
+        {
+            // tickle
+            m_TimeoutTimer.Now();
+            
+            // update statistics
+            double ping = m_StatsTimer.DurationSinceNow();
+            if ( m_fPingMin == -1 )
+            {
+                m_fPingMin = ping;
+                m_fPingMax = ping;
+                
+            }
+            else
+            {
+                m_fPingMin = MIN(m_fPingMin, ping);
+                m_fPingMax = MAX(m_fPingMax, ping);
+                
+            }
+            m_fPingSum += ping;
+            m_fPingCount += 1;
+            
+            // pop the original packet
+            if ( !m_LocalQueue.empty() )
+            {
+                CDvFramePacket *Packet = (CDvFramePacket *)m_LocalQueue.front();
+                m_LocalQueue.pop();
+                // todo: check the PID
+                // update content with transcoded ambe
+                Packet->SetAmbe(m_uiCodecOut, Ambe);
+                // tag syncs in DvData
+                if ( (m_uiCodecOut == CODEC_AMBEPLUS) && (Packet->GetPacketId() % 21) == 0 )
+                {
+                    Packet->SetDvData(DStarSync);
+                }
+                // and push it back to client
+                m_PacketStream->Lock();
+                m_PacketStream->push(Packet);
+                m_PacketStream->Unlock();
+            }
+            else
+            {
+                std::cout << "Unexpected transcoded packet received from ambed" << std::endl;
+            }
+         }
+    }
+    
+    // anything in our queue
+    while ( !empty() )
+    {
+        // yes, pop it from queue
+        CPacket *Packet = front();
+        pop();
+        
+        // yes, send to ambed
+        // this assume that thread pushing the Packet
+        // have verified that the CodecStream is connected
+        // and that the packet needs transcoding
+        m_StatsTimer.Now();
+        m_uiTotalPackets++;
+        EncodeAmbePacket(&Buffer, ((CDvFramePacket *)Packet)->GetAmbe(m_uiCodecIn));
+        m_Socket.Send(Buffer, m_Ip, m_uiPort);
+       
+        // and push to our local queue
+        m_LocalQueue.push(Packet);
+    }
+    
+    // handle timeout
+    if ( !m_LocalQueue.empty() && (m_TimeoutTimer.DurationSinceNow() >= (TRANSCODER_AMBEPACKET_TIMEOUT/1000.0f)) )
+    {
+        //std::cout << "ambed packet timeout" << std::endl;
+        m_uiTimeoutPackets++;
+    }
+}
+
+////////////////////////////////////////////////////////////////////////////////////////
+/// packet decoding helpers
+
+bool CCodecStream::IsValidAmbePacket(const CBuffer &Buffer, uint8 *Ambe)
+{
+    bool valid = false;
+    
+    if ( (Buffer.size() == 11) && (Buffer.data()[0] == m_uiCodecOut) )
+    {
+        ::memcpy(Ambe, &(Buffer.data()[2]), 9);
+        valid = true;
+    }
+    return valid;
+}
+
+////////////////////////////////////////////////////////////////////////////////////////
+/// packet encoding helpers
+
+void CCodecStream::EncodeAmbePacket(CBuffer *Buffer, const uint8 *Ambe)
+{
+    Buffer->clear();
+    Buffer->Append(m_uiCodecIn);
+    Buffer->Append(m_uiPid);
+    Buffer->Append((uint8 *)Ambe, 9);
+}
diff --git a/src/ccodecstream.h b/src/ccodecstream.h
new file mode 100644
index 0000000..c22e864
--- /dev/null
+++ b/src/ccodecstream.h
@@ -0,0 +1,116 @@
+//
+//  ccodecstream.h
+//  xlxd
+//
+//  Created by Jean-Luc Deltombe (LX3JL) on 13/04/2017.
+//  Copyright © 2015 Jean-Luc Deltombe (LX3JL). All rights reserved.
+//
+// ----------------------------------------------------------------------------
+//    This file is part of xlxd.
+//
+//    xlxd is free software: you can redistribute it and/or modify
+//    it under the terms of the GNU General Public License as published by
+//    the Free Software Foundation, either version 3 of the License, or
+//    (at your option) any later version.
+//
+//    xlxd is distributed in the hope that it will be useful,
+//    but WITHOUT ANY WARRANTY; without even the implied warranty of
+//    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+//    GNU General Public License for more details.
+//
+//    You should have received a copy of the GNU General Public License
+//    along with Foobar.  If not, see <http://www.gnu.org/licenses/>.
+// ----------------------------------------------------------------------------
+
+#ifndef ccodecstream_h
+#define ccodecstream_h
+
+#include "csemaphore.h"
+#include "cudpsocket.h"
+#include "cpacketqueue.h"
+
+
+////////////////////////////////////////////////////////////////////////////////////////
+// define
+
+// frame sizes
+#define AMBE_SIZE           9
+#define AMBEPLUS_SIZE       9
+
+
+////////////////////////////////////////////////////////////////////////////////////////
+// class
+
+class CPacketStream;
+
+class CCodecStream : public CPacketQueue
+{
+public:
+    // constructor
+    CCodecStream(CPacketStream *, uint16, uint8, uint8);
+    
+    // destructor
+    virtual ~CCodecStream();
+    
+    // initialization
+    bool Init(uint16);
+    void Close(void);
+    
+    // get
+    bool   IsConnected(void) const          { return m_bConnected; }
+    uint16 GetStreamId(void) const          { return m_uiStreamId; }
+    double GetPingMin(void) const           { return m_fPingMin; }
+    double GetPingMax(void) const           { return m_fPingMax; }
+    double GetPingAve(void) const           { return (m_fPingCount != 0) ? m_fPingSum/m_fPingCount : 0; }
+    uint32 GetTotalPackets(void) const      { return m_uiTotalPackets; }
+    uint32 GetTimeoutPackets(void) const    { return m_uiTimeoutPackets; }
+    bool   IsEmpty(void) const;
+
+    // task
+    static void Thread(CCodecStream *);
+    void Task(void);
+    
+
+protected:
+    // packet decoding helpers
+    bool IsValidAmbePacket(const CBuffer &, uint8 *);
+    
+    // packet encoding helpers
+    void EncodeAmbePacket(CBuffer *, const uint8 *);
+
+    
+protected:
+    // data
+    uint16          m_uiStreamId;
+    uint16          m_uiPort;
+    uint8           m_uiPid;
+    uint8           m_uiCodecIn;
+    uint8           m_uiCodecOut;
+
+    // socket
+    CIp             m_Ip;
+    CUdpSocket      m_Socket;
+    bool            m_bConnected;
+    
+    // associated packet stream
+    CPacketStream   *m_PacketStream;
+    CPacketQueue    m_LocalQueue;
+
+    // thread
+    bool            m_bStopThread;
+    std::thread     *m_pThread;
+    CTimePoint      m_TimeoutTimer;
+    CTimePoint      m_StatsTimer;
+    
+    // statistics
+    double          m_fPingMin;
+    double          m_fPingMax;
+    double          m_fPingSum;
+    double          m_fPingCount;
+    uint32          m_uiTotalPackets;
+    uint32          m_uiTimeoutPackets;
+};
+
+
+////////////////////////////////////////////////////////////////////////////////////////
+#endif /* ccodecstream_h */
diff --git a/src/ccrc.cpp b/src/ccrc.cpp
new file mode 100644
index 0000000..c09ad1c
--- /dev/null
+++ b/src/ccrc.cpp
@@ -0,0 +1,239 @@
+/*
+ *   Copyright (C) 2015,2016 by Jonathan Naylor G4KLX
+ *
+ *   This program is free software; you can redistribute it and/or modify
+ *   it under the terms of the GNU General Public License as published by
+ *   the Free Software Foundation; either version 2 of the License, or
+ *   (at your option) any later version.
+ *
+ *   This program is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *   GNU General Public License for more details.
+ *
+ *   You should have received a copy of the GNU General Public License
+ *   along with this program; if not, write to the Free Software
+ *   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include "ccrc.h"
+
+#include "cutils.h"
+
+#include <cstdint>
+#include <cstdio>
+#include <cassert>
+#include <cmath>
+
+const uint8_t CRC8_TABLE[] = {
+    0x00, 0x07, 0x0E, 0x09, 0x1C, 0x1B, 0x12, 0x15, 0x38, 0x3F, 0x36, 0x31,
+    0x24, 0x23, 0x2A, 0x2D, 0x70, 0x77, 0x7E, 0x79, 0x6C, 0x6B, 0x62, 0x65,
+    0x48, 0x4F, 0x46, 0x41, 0x54, 0x53, 0x5A, 0x5D, 0xE0, 0xE7, 0xEE, 0xE9,
+    0xFC, 0xFB, 0xF2, 0xF5, 0xD8, 0xDF, 0xD6, 0xD1, 0xC4, 0xC3, 0xCA, 0xCD,
+    0x90, 0x97, 0x9E, 0x99, 0x8C, 0x8B, 0x82, 0x85, 0xA8, 0xAF, 0xA6, 0xA1,
+    0xB4, 0xB3, 0xBA, 0xBD, 0xC7, 0xC0, 0xC9, 0xCE, 0xDB, 0xDC, 0xD5, 0xD2,
+    0xFF, 0xF8, 0xF1, 0xF6, 0xE3, 0xE4, 0xED, 0xEA, 0xB7, 0xB0, 0xB9, 0xBE,
+    0xAB, 0xAC, 0xA5, 0xA2, 0x8F, 0x88, 0x81, 0x86, 0x93, 0x94, 0x9D, 0x9A,
+    0x27, 0x20, 0x29, 0x2E, 0x3B, 0x3C, 0x35, 0x32, 0x1F, 0x18, 0x11, 0x16,
+    0x03, 0x04, 0x0D, 0x0A, 0x57, 0x50, 0x59, 0x5E, 0x4B, 0x4C, 0x45, 0x42,
+    0x6F, 0x68, 0x61, 0x66, 0x73, 0x74, 0x7D, 0x7A, 0x89, 0x8E, 0x87, 0x80,
+    0x95, 0x92, 0x9B, 0x9C, 0xB1, 0xB6, 0xBF, 0xB8, 0xAD, 0xAA, 0xA3, 0xA4,
+    0xF9, 0xFE, 0xF7, 0xF0, 0xE5, 0xE2, 0xEB, 0xEC, 0xC1, 0xC6, 0xCF, 0xC8,
+    0xDD, 0xDA, 0xD3, 0xD4, 0x69, 0x6E, 0x67, 0x60, 0x75, 0x72, 0x7B, 0x7C,
+    0x51, 0x56, 0x5F, 0x58, 0x4D, 0x4A, 0x43, 0x44, 0x19, 0x1E, 0x17, 0x10,
+    0x05, 0x02, 0x0B, 0x0C, 0x21, 0x26, 0x2F, 0x28, 0x3D, 0x3A, 0x33, 0x34,
+    0x4E, 0x49, 0x40, 0x47, 0x52, 0x55, 0x5C, 0x5B, 0x76, 0x71, 0x78, 0x7F,
+    0x6A, 0x6D, 0x64, 0x63, 0x3E, 0x39, 0x30, 0x37, 0x22, 0x25, 0x2C, 0x2B,
+    0x06, 0x01, 0x08, 0x0F, 0x1A, 0x1D, 0x14, 0x13, 0xAE, 0xA9, 0xA0, 0xA7,
+    0xB2, 0xB5, 0xBC, 0xBB, 0x96, 0x91, 0x98, 0x9F, 0x8A, 0x8D, 0x84, 0x83,
+    0xDE, 0xD9, 0xD0, 0xD7, 0xC2, 0xC5, 0xCC, 0xCB, 0xE6, 0xE1, 0xE8, 0xEF,
+    0xFA, 0xFD, 0xF4, 0xF3, 0x01 };
+
+const uint16_t CCITT16_TABLE1[] = {
+    0x0000U, 0x1189U, 0x2312U, 0x329bU, 0x4624U, 0x57adU, 0x6536U, 0x74bfU,
+    0x8c48U, 0x9dc1U, 0xaf5aU, 0xbed3U, 0xca6cU, 0xdbe5U, 0xe97eU, 0xf8f7U,
+    0x1081U, 0x0108U, 0x3393U, 0x221aU, 0x56a5U, 0x472cU, 0x75b7U, 0x643eU,
+    0x9cc9U, 0x8d40U, 0xbfdbU, 0xae52U, 0xdaedU, 0xcb64U, 0xf9ffU, 0xe876U,
+    0x2102U, 0x308bU, 0x0210U, 0x1399U, 0x6726U, 0x76afU, 0x4434U, 0x55bdU,
+    0xad4aU, 0xbcc3U, 0x8e58U, 0x9fd1U, 0xeb6eU, 0xfae7U, 0xc87cU, 0xd9f5U,
+    0x3183U, 0x200aU, 0x1291U, 0x0318U, 0x77a7U, 0x662eU, 0x54b5U, 0x453cU,
+    0xbdcbU, 0xac42U, 0x9ed9U, 0x8f50U, 0xfbefU, 0xea66U, 0xd8fdU, 0xc974U,
+    0x4204U, 0x538dU, 0x6116U, 0x709fU, 0x0420U, 0x15a9U, 0x2732U, 0x36bbU,
+    0xce4cU, 0xdfc5U, 0xed5eU, 0xfcd7U, 0x8868U, 0x99e1U, 0xab7aU, 0xbaf3U,
+    0x5285U, 0x430cU, 0x7197U, 0x601eU, 0x14a1U, 0x0528U, 0x37b3U, 0x263aU,
+    0xdecdU, 0xcf44U, 0xfddfU, 0xec56U, 0x98e9U, 0x8960U, 0xbbfbU, 0xaa72U,
+    0x6306U, 0x728fU, 0x4014U, 0x519dU, 0x2522U, 0x34abU, 0x0630U, 0x17b9U,
+    0xef4eU, 0xfec7U, 0xcc5cU, 0xddd5U, 0xa96aU, 0xb8e3U, 0x8a78U, 0x9bf1U,
+    0x7387U, 0x620eU, 0x5095U, 0x411cU, 0x35a3U, 0x242aU, 0x16b1U, 0x0738U,
+    0xffcfU, 0xee46U, 0xdcddU, 0xcd54U, 0xb9ebU, 0xa862U, 0x9af9U, 0x8b70U,
+    0x8408U, 0x9581U, 0xa71aU, 0xb693U, 0xc22cU, 0xd3a5U, 0xe13eU, 0xf0b7U,
+    0x0840U, 0x19c9U, 0x2b52U, 0x3adbU, 0x4e64U, 0x5fedU, 0x6d76U, 0x7cffU,
+    0x9489U, 0x8500U, 0xb79bU, 0xa612U, 0xd2adU, 0xc324U, 0xf1bfU, 0xe036U,
+    0x18c1U, 0x0948U, 0x3bd3U, 0x2a5aU, 0x5ee5U, 0x4f6cU, 0x7df7U, 0x6c7eU,
+    0xa50aU, 0xb483U, 0x8618U, 0x9791U, 0xe32eU, 0xf2a7U, 0xc03cU, 0xd1b5U,
+    0x2942U, 0x38cbU, 0x0a50U, 0x1bd9U, 0x6f66U, 0x7eefU, 0x4c74U, 0x5dfdU,
+    0xb58bU, 0xa402U, 0x9699U, 0x8710U, 0xf3afU, 0xe226U, 0xd0bdU, 0xc134U,
+    0x39c3U, 0x284aU, 0x1ad1U, 0x0b58U, 0x7fe7U, 0x6e6eU, 0x5cf5U, 0x4d7cU,
+    0xc60cU, 0xd785U, 0xe51eU, 0xf497U, 0x8028U, 0x91a1U, 0xa33aU, 0xb2b3U,
+    0x4a44U, 0x5bcdU, 0x6956U, 0x78dfU, 0x0c60U, 0x1de9U, 0x2f72U, 0x3efbU,
+    0xd68dU, 0xc704U, 0xf59fU, 0xe416U, 0x90a9U, 0x8120U, 0xb3bbU, 0xa232U,
+    0x5ac5U, 0x4b4cU, 0x79d7U, 0x685eU, 0x1ce1U, 0x0d68U, 0x3ff3U, 0x2e7aU,
+    0xe70eU, 0xf687U, 0xc41cU, 0xd595U, 0xa12aU, 0xb0a3U, 0x8238U, 0x93b1U,
+    0x6b46U, 0x7acfU, 0x4854U, 0x59ddU, 0x2d62U, 0x3cebU, 0x0e70U, 0x1ff9U,
+    0xf78fU, 0xe606U, 0xd49dU, 0xc514U, 0xb1abU, 0xa022U, 0x92b9U, 0x8330U,
+    0x7bc7U, 0x6a4eU, 0x58d5U, 0x495cU, 0x3de3U, 0x2c6aU, 0x1ef1U, 0x0f78U };
+
+const uint16_t CCITT16_TABLE2[] = {
+    0x0000, 0x1021, 0x2042, 0x3063, 0x4084, 0x50A5, 0x60C6, 0x70E7,
+    0x8108, 0x9129, 0xA14A, 0xB16B, 0xC18C, 0xD1AD, 0xE1CE, 0xF1EF,
+    0x1231, 0x0210, 0x3273, 0x2252, 0x52B5, 0x4294, 0x72F7, 0x62D6,
+    0x9339, 0x8318, 0xB37B, 0xA35A, 0xD3BD, 0xC39C, 0xF3FF, 0xE3DE,
+    0x2462, 0x3443, 0x0420, 0x1401, 0x64E6, 0x74C7, 0x44A4, 0x5485,
+    0xA56A, 0xB54B, 0x8528, 0x9509, 0xE5EE, 0xF5CF, 0xC5AC, 0xD58D,
+    0x3653, 0x2672, 0x1611, 0x0630, 0x76D7, 0x66F6, 0x5695, 0x46B4,
+    0xB75B, 0xA77A, 0x9719, 0x8738, 0xF7DF, 0xE7FE, 0xD79D, 0xC7BC,
+    0x48C4, 0x58E5, 0x6886, 0x78A7, 0x0840, 0x1861, 0x2802, 0x3823,
+    0xC9CC, 0xD9ED, 0xE98E, 0xF9AF, 0x8948, 0x9969, 0xA90A, 0xB92B,
+    0x5AF5, 0x4AD4, 0x7AB7, 0x6A96, 0x1A71, 0x0A50, 0x3A33, 0x2A12,
+    0xDBFD, 0xCBDC, 0xFBBF, 0xEB9E, 0x9B79, 0x8B58, 0xBB3B, 0xAB1A,
+    0x6CA6, 0x7C87, 0x4CE4, 0x5CC5, 0x2C22, 0x3C03, 0x0C60, 0x1C41,
+    0xEDAE, 0xFD8F, 0xCDEC, 0xDDCD, 0xAD2A, 0xBD0B, 0x8D68, 0x9D49,
+    0x7E97, 0x6EB6, 0x5ED5, 0x4EF4, 0x3E13, 0x2E32, 0x1E51, 0x0E70,
+    0xFF9F, 0xEFBE, 0xDFDD, 0xCFFC, 0xBF1B, 0xAF3A, 0x9F59, 0x8F78,
+    0x9188, 0x81A9, 0xB1CA, 0xA1EB, 0xD10C, 0xC12D, 0xF14E, 0xE16F,
+    0x1080, 0x00A1, 0x30C2, 0x20E3, 0x5004, 0x4025, 0x7046, 0x6067,
+    0x83B9, 0x9398, 0xA3FB, 0xB3DA, 0xC33D, 0xD31C, 0xE37F, 0xF35E,
+    0x02B1, 0x1290, 0x22F3, 0x32D2, 0x4235, 0x5214, 0x6277, 0x7256,
+    0xB5EA, 0xA5CB, 0x95A8, 0x8589, 0xF56E, 0xE54F, 0xD52C, 0xC50D,
+    0x34E2, 0x24C3, 0x14A0, 0x0481, 0x7466, 0x6447, 0x5424, 0x4405,
+    0xA7DB, 0xB7FA, 0x8799, 0x97B8, 0xE75F, 0xF77E, 0xC71D, 0xD73C,
+    0x26D3, 0x36F2, 0x0691, 0x16B0, 0x6657, 0x7676, 0x4615, 0x5634,
+    0xD94C, 0xC96D, 0xF90E, 0xE92F, 0x99C8, 0x89E9, 0xB98A, 0xA9AB,
+    0x5844, 0x4865, 0x7806, 0x6827, 0x18C0, 0x08E1, 0x3882, 0x28A3,
+    0xCB7D, 0xDB5C, 0xEB3F, 0xFB1E, 0x8BF9, 0x9BD8, 0xABBB, 0xBB9A,
+    0x4A75, 0x5A54, 0x6A37, 0x7A16, 0x0AF1, 0x1AD0, 0x2AB3, 0x3A92,
+    0xFD2E, 0xED0F, 0xDD6C, 0xCD4D, 0xBDAA, 0xAD8B, 0x9DE8, 0x8DC9,
+    0x7C26, 0x6C07, 0x5C64, 0x4C45, 0x3CA2, 0x2C83, 0x1CE0, 0x0CC1,
+    0xEF1F, 0xFF3E, 0xCF5D, 0xDF7C, 0xAF9B, 0xBFBA, 0x8FD9, 0x9FF8,
+    0x6E17, 0x7E36, 0x4E55, 0x5E74, 0x2E93, 0x3EB2, 0x0ED1, 0x1EF0 };
+
+
+bool CCRC::checkFiveBit(bool* in, unsigned int tcrc)
+{
+    assert(in != NULL);
+    
+    unsigned int crc;
+    encodeFiveBit(in, crc);
+    
+    return crc == tcrc;
+}
+
+void CCRC::encodeFiveBit(const bool* in, unsigned int& tcrc)
+{
+    assert(in != NULL);
+    
+    unsigned short total = 0U;
+    for (unsigned int i = 0U; i < 72U; i += 8U) {
+        unsigned char c;
+        CUtils::bitsToByteBE(in + i, c);
+        total += c;
+    }
+    
+    total %= 31U;
+    
+    tcrc = total;
+}
+
+void CCRC::addCCITT162(unsigned char *in, unsigned int length)
+{
+    assert(in != NULL);
+    assert(length > 2U);
+    
+    union {
+        uint16_t crc16;
+        uint8_t  crc8[2U];
+    };
+    
+    crc16 = 0U;
+    
+    for (unsigned i = 0U; i < (length - 2U); i++)
+        crc16 = (uint16_t(crc8[0U]) << 8) ^ CCITT16_TABLE2[crc8[1U] ^ in[i]];
+    
+    crc16 = ~crc16;
+    
+    in[length - 1U] = crc8[0U];
+    in[length - 2U] = crc8[1U];
+}
+
+bool CCRC::checkCCITT162(const unsigned char *in, unsigned int length)
+{
+    assert(in != NULL);
+    assert(length > 2U);
+    
+    union {
+        uint16_t crc16;
+        uint8_t  crc8[2U];
+    };
+    
+    crc16 = 0U;
+    
+    for (unsigned i = 0U; i < (length - 2U); i++)
+        crc16 = (uint16_t(crc8[0U]) << 8) ^ CCITT16_TABLE2[crc8[1U] ^ in[i]];
+    
+    crc16 = ~crc16;
+    
+    return crc8[0U] == in[length - 1U] && crc8[1U] == in[length - 2U];
+}
+
+void CCRC::addCCITT161(unsigned char *in, unsigned int length)
+{
+    assert(in != NULL);
+    assert(length > 2U);
+    
+    union {
+        uint16_t crc16;
+        uint8_t  crc8[2U];
+    };
+    
+    crc16 = 0xFFFFU;
+    
+    for (unsigned int i = 0U; i < (length - 2U); i++)
+        crc16 = uint16_t(crc8[1U]) ^ CCITT16_TABLE1[crc8[0U] ^ in[i]];
+    
+    crc16 = ~crc16;
+    
+    in[length - 2U] = crc8[0U];
+    in[length - 1U] = crc8[1U];
+}
+
+bool CCRC::checkCCITT161(const unsigned char *in, unsigned int length)
+{
+    assert(in != NULL);
+    assert(length > 2U);
+    
+    union {
+        uint16_t crc16;
+        uint8_t  crc8[2U];
+    };
+    
+    crc16 = 0xFFFFU;
+    
+    for (unsigned int i = 0U; i < (length - 2U); i++)
+        crc16 = uint16_t(crc8[1U]) ^ CCITT16_TABLE1[crc8[0U] ^ in[i]];
+    
+    crc16 = ~crc16;
+    
+    return crc8[0U] == in[length - 2U] && crc8[1U] == in[length - 1U];
+}
+
+unsigned char CCRC::crc8(const unsigned char *in, unsigned int length)
+{
+    assert(in != NULL);
+    
+    uint8_t crc = 0U;
+    
+    for (unsigned int i = 0U; i < length; i++)
+        crc = CRC8_TABLE[crc ^ in[i]];
+    
+    return crc;
+}
diff --git a/src/ccrc.h b/src/ccrc.h
new file mode 100644
index 0000000..90491be
--- /dev/null
+++ b/src/ccrc.h
@@ -0,0 +1,37 @@
+/*
+ *   Copyright (C) 2015,2016 by Jonathan Naylor G4KLX
+ *
+ *   This program is free software; you can redistribute it and/or modify
+ *   it under the terms of the GNU General Public License as published by
+ *   the Free Software Foundation; either version 2 of the License, or
+ *   (at your option) any later version.
+ *
+ *   This program is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *   GNU General Public License for more details.
+ *
+ *   You should have received a copy of the GNU General Public License
+ *   along with this program; if not, write to the Free Software
+ *   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#if !defined(CRC_H)
+#define	CRC_H
+
+class CCRC
+{
+public:
+    static bool checkFiveBit(bool* in, unsigned int tcrc);
+    static void encodeFiveBit(const bool* in, unsigned int& tcrc);
+    
+    static void addCCITT161(unsigned char* in, unsigned int length);
+    static void addCCITT162(unsigned char* in, unsigned int length);
+    
+    static bool checkCCITT161(const unsigned char* in, unsigned int length);
+    static bool checkCCITT162(const unsigned char* in, unsigned int length);
+    
+    static unsigned char crc8(const unsigned char* in, unsigned int length);
+};
+
+#endif
diff --git a/src/cdcsclient.h b/src/cdcsclient.h
index 2fd48f9..2e579f4 100644
--- a/src/cdcsclient.h
+++ b/src/cdcsclient.h
@@ -48,6 +48,7 @@ public:
     // identity
     int GetProtocol(void) const                 { return PROTOCOL_DCS; }
     const char *GetProtocolName(void) const     { return "DCS"; }
+    int GetCodec(void) const                    { return CODEC_AMBEPLUS; }
     bool IsNode(void) const                     { return true; }
     
     // status
diff --git a/src/cdcsprotocol.cpp b/src/cdcsprotocol.cpp
index 2a0b9f8..27a0d79 100644
--- a/src/cdcsprotocol.cpp
+++ b/src/cdcsprotocol.cpp
@@ -29,18 +29,6 @@
 #include "creflector.h"
 #include "cgatekeeper.h"
 
-
-////////////////////////////////////////////////////////////////////////////////////////
-// constructor
-
-CDcsProtocol::CDcsProtocol()
-{
-    for ( int i = 0; i < m_iSeqCounters.size(); i++ )
-    {
-        m_iSeqCounters[i] = 0;
-    }
-}
-
 ////////////////////////////////////////////////////////////////////////////////////////
 // operation
 
@@ -95,7 +83,7 @@ void CDcsProtocol::Task(void)
             {
                 // handle it
                 OnDvHeaderPacketIn(Header, Ip);
-
+                
                 if ( !Frame->IsLastPacket() )
                 {
                     //std::cout << "DCS DV frame" << std::endl;
@@ -103,8 +91,8 @@ void CDcsProtocol::Task(void)
                 }
                 else
                 {
-                   //std::cout << "DCS DV last frame" << std::endl;
-                   OnDvLastFramePacketIn((CDvLastFramePacket *)Frame, &Ip);
+                    //std::cout << "DCS DV last frame" << std::endl;
+                    OnDvLastFramePacketIn((CDvLastFramePacket *)Frame, &Ip);
                 }
             }
             else
@@ -284,8 +272,8 @@ void CDcsProtocol::HandleQueue(void)
         if ( packet->IsDvHeader() )
         {
             // this relies on queue feeder setting valid module id
-            m_DvHeadersCache[iModId] = CDvHeaderPacket((const CDvHeaderPacket &)*packet);
-            m_iSeqCounters[iModId] = 0;
+            m_StreamsCache[iModId].m_dvHeader = CDvHeaderPacket((const CDvHeaderPacket &)*packet);
+            m_StreamsCache[iModId].m_iSeqCounter = 0;
         }
         else
         {
@@ -293,11 +281,19 @@ void CDcsProtocol::HandleQueue(void)
             CBuffer buffer;
             if ( packet->IsLastPacket() )
             {
-                EncodeDvLastPacket(m_DvHeadersCache[iModId], (const CDvFramePacket &)*packet, m_iSeqCounters[iModId]++, &buffer);
+                EncodeDvLastPacket(
+                                   m_StreamsCache[iModId].m_dvHeader,
+                                   (const CDvFramePacket &)*packet,
+                                   m_StreamsCache[iModId].m_iSeqCounter++,
+                                   &buffer);
             }
             else if ( packet->IsDvFrame() )
             {
-                EncodeDvPacket(m_DvHeadersCache[iModId], (const CDvFramePacket &)*packet, m_iSeqCounters[iModId]++, &buffer);
+                EncodeDvPacket(
+                               m_StreamsCache[iModId].m_dvHeader,
+                               (const CDvFramePacket &)*packet,
+                               m_StreamsCache[iModId].m_iSeqCounter++,
+                               &buffer);
             }
             
             // send it
@@ -558,9 +554,6 @@ void CDcsProtocol::EncodeDvPacket(const CDvHeaderPacket &Header, const CDvFrameP
 
 void CDcsProtocol::EncodeDvLastPacket(const CDvHeaderPacket &Header, const CDvFramePacket &DvFrame, uint32 iSeq, CBuffer *Buffer) const
 {
-    //uint8 tag[] = { 0x4e,0x8d,0x32,0x88,0x26,0x1a,0x3f,0x61,0xe8,0x55,0x55,0x55 };
-    
     EncodeDvPacket(Header, DvFrame, iSeq, Buffer);
     (Buffer->data())[45] |= 0x40;
-    //Buffer->Ser
 }
diff --git a/src/cdcsprotocol.h b/src/cdcsprotocol.h
index b356db3..b455dc2 100644
--- a/src/cdcsprotocol.h
+++ b/src/cdcsprotocol.h
@@ -37,11 +37,21 @@
 ////////////////////////////////////////////////////////////////////////////////////////
 // class
 
+class CDcsStreamCacheItem
+{
+public:
+    CDcsStreamCacheItem()     { m_iSeqCounter = 0; }
+    ~CDcsStreamCacheItem()    {}
+    
+    CDvHeaderPacket m_dvHeader;
+    uint32          m_iSeqCounter;
+};
+
 class CDcsProtocol : public CProtocol
 {
 public:
     // constructor
-    CDcsProtocol();
+    CDcsProtocol() {};
     
     // destructor
     virtual ~CDcsProtocol() {};
@@ -83,8 +93,7 @@ protected:
     CTimePoint          m_LastKeepaliveTime;
     
     // for queue header caches
-    std::array<CDvHeaderPacket, NB_OF_MODULES>  m_DvHeadersCache;
-    std::array<uint32, NB_OF_MODULES>           m_iSeqCounters;
+    std::array<CDcsStreamCacheItem, NB_OF_MODULES>    m_StreamsCache;
 };
 
 ////////////////////////////////////////////////////////////////////////////////////////
diff --git a/src/cdextraclient.h b/src/cdextraclient.h
index 2b42b65..132ff37 100644
--- a/src/cdextraclient.h
+++ b/src/cdextraclient.h
@@ -48,7 +48,8 @@ public:
     // identity
     int GetProtocol(void) const                 { return PROTOCOL_DEXTRA; }
     int GetProtocolRevision(void) const         { return m_ProtRev; }
-    const char *GetProtocolName(void) const     { return "DExtra"; }
+    const char *GetProtocolName(void) const     { return "Dextra"; }
+    int GetCodec(void) const                    { return CODEC_AMBEPLUS; }
     bool IsNode(void) const                     { return true; }
     
     // status
diff --git a/src/cdmriddir.cpp b/src/cdmriddir.cpp
new file mode 100644
index 0000000..5b2f5ce
--- /dev/null
+++ b/src/cdmriddir.cpp
@@ -0,0 +1,286 @@
+//
+//  cdmriddir.cpp
+//  xlxd
+//
+//  Created by Jean-Luc Deltombe (LX3JL) on 08/10/2016.
+//  Copyright © 2015 Jean-Luc Deltombe (LX3JL). All rights reserved.
+//
+// ----------------------------------------------------------------------------
+//    This file is part of xlxd.
+//
+//    xlxd is free software: you can redistribute it and/or modify
+//    it under the terms of the GNU General Public License as published by
+//    the Free Software Foundation, either version 3 of the License, or
+//    (at your option) any later version.
+//
+//    xlxd is distributed in the hope that it will be useful,
+//    but WITHOUT ANY WARRANTY; without even the implied warranty of
+//    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+//    GNU General Public License for more details.
+//
+//    You should have received a copy of the GNU General Public License
+//    along with Foobar.  If not, see <http://www.gnu.org/licenses/>.
+// ----------------------------------------------------------------------------
+
+#include <string.h>
+#include "main.h"
+#include "creflector.h"
+#include "cdmriddir.h"
+
+
+CDmridDir g_DmridDir;
+
+////////////////////////////////////////////////////////////////////////////////////////
+// find
+
+const CCallsign *CDmridDir::FindCallsign(uint32 dmrid)
+{
+    auto found = m_CallsignMap.find(dmrid);
+    if ( found != m_CallsignMap.end() )
+    {
+        return &(found->second);
+    }
+    return NULL;
+}
+
+uint32 CDmridDir::FindDmrid(const CCallsign &callsign)
+{
+    auto found = m_DmridMap.find(callsign);
+    if ( found != m_DmridMap.end() )
+    {
+        return (found->second);
+    }
+    return 0;
+}
+
+////////////////////////////////////////////////////////////////////////////////////////
+// refresh
+
+#if (DMRIDDB_USE_RLX_SERVER == 1)
+bool CDmridDir::RefreshContent(void)
+{
+    bool ok = true;
+    CBuffer buffer;
+    
+    // get file from xlxapi server
+    if ( (ok = HttpGet("xlxapi.rlx.lu", "/api/exportdmr.php", 80, &buffer)) )
+    {
+        // clear directory
+        m_CallsignMap.clear();
+        m_DmridMap.clear();
+
+        // scan file
+        if ( buffer.size() > 0 )
+        {
+            char *ptr1 = (char *)buffer.data();
+            char *ptr2;
+            // get next line
+            while ( (ptr2 = ::strchr(ptr1, '\n')) != NULL )
+            {
+                *ptr2 = 0;
+                // get items
+                char *dmrid;
+                char *callsign;
+                if ( ((dmrid = ::strtok(ptr1, ";")) != NULL) && IsValidDmrid(dmrid) )
+                {
+                    if ( ((callsign = ::strtok(NULL, ";")) != NULL) )
+                    {
+                        // new entry
+                        uint32 ui = atoi(dmrid);
+                        CCallsign cs(callsign, ui);
+                        if ( cs.IsValid() )
+                        {
+                            m_CallsignMap.insert(std::pair<uint32,CCallsign>(ui, cs));
+                            m_DmridMap.insert(std::pair<CCallsign,uint32>(cs,ui));
+                        }
+                    }
+                }
+                // next line
+                ptr1 = ptr2+1;
+            }
+            // done
+            ok = true;
+        }
+    }
+    
+    // report
+    std::cout << "Read " << m_DmridMap.size() << " DMR id from online database " << std::endl;
+    
+    // done
+    return ok;
+}
+#else
+bool CDmridDir::RefreshContent(void)
+{
+    bool ok = true;
+    CBuffer buffer;
+    std::ifstream file;
+    std::streampos size;
+    
+    // open file
+    file.open(DMRIDDB_PATH, std::ios::in | std::ios::binary | std::ios::ate);
+    if ( file.is_open() )
+    {
+        // clear directory
+        m_CallsignMap.clear();
+        m_DmridMap.clear();
+        
+        // scan file
+        size = file.tellg();
+        if ( size > 0 )
+        {
+            // read file into buffer
+            buffer.resize((int)size+1);
+            file.seekg (0, std::ios::beg);
+            file.read((char *)buffer.data(), (int)size);
+            
+            // close file
+            file.close();
+            
+            // crack it
+            char *ptr1 = (char *)buffer.data();
+            char *ptr2;
+            // get next line
+            while ( (ptr2 = ::strchr(ptr1, '\n')) != NULL )
+            {
+                *ptr2 = 0;
+                // get items
+                char *dmrid;
+                char *callsign;
+                if ( ((dmrid = ::strtok(ptr1, ";")) != NULL) && IsValidDmrid(dmrid) )
+                {
+                    if ( ((callsign = ::strtok(NULL, ";")) != NULL) )
+                    {
+                        // new entry
+                        uint32 ui = atoi(dmrid);
+                        CCallsign cs(callsign, ui);
+                        if ( cs.IsValid() )
+                        {
+                            m_CallsignMap.insert(std::pair<uint32,CCallsign>(ui, cs));
+                            m_DmridMap.insert(std::pair<CCallsign,uint32>(cs,ui));
+                        }
+                    }
+                }
+                // next line
+                ptr1 = ptr2+1;
+            }
+            // done
+            ok = true;
+        }
+    }
+    
+    // report
+    std::cout << "Read " << m_DmridMap.size() << " DMR id from online database " << std::endl;
+    
+    // done
+    return ok;
+}
+#endif
+
+////////////////////////////////////////////////////////////////////////////////////////
+// httpd helpers
+
+#define DMRID_HTTPGET_SIZEMAX       (256)
+#define DMRID_TEXTFILE_SIZEMAX      (10*1024*1024)
+
+bool CDmridDir::HttpGet(const char *hostname, const char *filename, int port, CBuffer *buffer)
+{
+    bool ok = false;
+    int sock_id;
+    
+    // open socket
+    if ( (sock_id = ::socket(AF_INET, SOCK_STREAM, 0)) >= 0 )
+    {
+        // get hostname address
+        struct sockaddr_in servaddr;
+        struct hostent *hp;
+        ::memset(&servaddr,0,sizeof(servaddr));
+        if( (hp = gethostbyname(hostname)) != NULL )
+        {
+            // dns resolved
+            ::memcpy((char *)&servaddr.sin_addr.s_addr, (char *)hp->h_addr, hp->h_length);
+            servaddr.sin_port = htons(port);
+            servaddr.sin_family = AF_INET;
+            
+            // connect
+            if ( ::connect(sock_id, (struct sockaddr *)&servaddr, sizeof(servaddr)) == 0)
+            {
+                // send the GET request
+                char request[DMRID_HTTPGET_SIZEMAX];
+                ::sprintf(request, "GET /%s HTTP/1.0\nFrom: %s\nUser-Agent: xlxd\n\n",
+                          filename, (const char *)g_Reflector.GetCallsign());
+                ::write(sock_id, request, strlen(request));
+
+                // config receive timeouts
+                fd_set read_set;
+                struct timeval timeout;
+                timeout.tv_sec = 5;
+                timeout.tv_usec = 0;
+                FD_ZERO(&read_set);
+                FD_SET(sock_id, &read_set);
+                
+                // get the reply back
+                buffer->clear();
+                bool done = false;
+                do
+                {
+                    char buf[1440];
+                    ssize_t len = 0;
+                    select(sock_id+1, &read_set, NULL, NULL, &timeout);
+                    //if ( (ret > 0) || ((ret < 0) && (errno == EINPROGRESS)) )
+                    //if ( ret >= 0 )
+                    //{
+                        usleep(5000);
+                        len = read(sock_id, buf, 1440);
+                        if ( len > 0 )
+                        {
+                            buffer->Append((uint8 *)buf, (int)len);
+                            ok = true;
+                        }
+                    //}
+                    done = (len <= 0);
+
+                } while (!done);
+                buffer->Append((uint8)0);
+                                
+                // and disconnect
+                close(sock_id);
+            }
+            else
+            {
+                std::cout << "Cannot establish connection with host " << hostname << std::endl;
+            }
+        }
+        else
+        {
+            std::cout << "Host " << hostname << " not found" << std::endl;
+        }
+        
+    }
+    else
+    {
+        std::cout << "Failed to open wget socket" << std::endl;
+    }
+    
+    // done
+    return ok;
+}
+
+////////////////////////////////////////////////////////////////////////////////////////
+// syntax helpers
+
+bool CDmridDir::IsValidDmrid(const char *sz)
+{
+    bool ok = false;
+    size_t n = ::strlen(sz);
+    if ( (n > 0) && (n <= 8) )
+    {
+        ok = true;
+        for ( size_t i = 0; (i < n) && ok; i++ )
+        {
+            ok &= ::isdigit(sz[i]);
+        }
+    }
+    return ok;
+}
+
diff --git a/src/cdmriddir.h b/src/cdmriddir.h
new file mode 100644
index 0000000..83f25b1
--- /dev/null
+++ b/src/cdmriddir.h
@@ -0,0 +1,78 @@
+//
+//  cdmriddir.h
+//  xlxd
+//
+//  Created by Jean-Luc Deltombe (LX3JL) on 08/10/2016.
+//  Copyright © 2015 Jean-Luc Deltombe (LX3JL). All rights reserved.
+//
+// ----------------------------------------------------------------------------
+//    This file is part of xlxd.
+//
+//    xlxd is free software: you can redistribute it and/or modify
+//    it under the terms of the GNU General Public License as published by
+//    the Free Software Foundation, either version 3 of the License, or
+//    (at your option) any later version.
+//
+//    xlxd is distributed in the hope that it will be useful,
+//    but WITHOUT ANY WARRANTY; without even the implied warranty of
+//    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+//    GNU General Public License for more details.
+//
+//    You should have received a copy of the GNU General Public License
+//    along with Foobar.  If not, see <http://www.gnu.org/licenses/>.
+// ----------------------------------------------------------------------------
+
+#ifndef cdmriddir_h
+#define cdmriddir_h
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <netdb.h>
+#include "cbuffer.h"
+#include "ccallsign.h"
+
+
+// compare function for std::map::find
+
+struct CallsignCompare
+{
+    bool operator() (const CCallsign &cs1, const CCallsign &cs2) const
+    { return cs1.HasLowerCallsign(cs2);}
+};
+
+
+////////////////////////////////////////////////////////////////////////////////////////
+
+class CDmridDir
+{
+public:
+    // constructor
+    CDmridDir() {}
+    
+    // destructor
+    ~CDmridDir() {}
+    
+    // refresh
+    bool RefreshContent(void);
+    
+    // find
+    const CCallsign *FindCallsign(uint32);
+    uint32 FindDmrid(const CCallsign &);
+    
+protected:
+    // httpd helpers
+    bool HttpGet(const char *, const char *, int, CBuffer *);
+    
+    // syntax helpers
+    bool IsValidDmrid(const char *);
+    
+protected:
+    // directory
+    std::map <uint32, CCallsign> m_CallsignMap;
+    std::map <CCallsign, uint32, CallsignCompare> m_DmridMap;
+};
+
+////////////////////////////////////////////////////////////////////////////////////////
+
+#endif /* cdmriddir_h */
diff --git a/src/cdmrmmdvmclient.cpp b/src/cdmrmmdvmclient.cpp
new file mode 100644
index 0000000..09c9e1c
--- /dev/null
+++ b/src/cdmrmmdvmclient.cpp
@@ -0,0 +1,52 @@
+//
+//  cdmrmmdvmclient.cpp
+//  xlxd
+//
+//  Created by Jean-Luc Deltombe (LX3JL) on 04/03/2017.
+//  Copyright © 2015 Jean-Luc Deltombe (LX3JL). All rights reserved.
+//
+// ----------------------------------------------------------------------------
+//    This file is part of xlxd.
+//
+//    xlxd is free software: you can redistribute it and/or modify
+//    it under the terms of the GNU General Public License as published by
+//    the Free Software Foundation, either version 3 of the License, or
+//    (at your option) any later version.
+//
+//    xlxd is distributed in the hope that it will be useful,
+//    but WITHOUT ANY WARRANTY; without even the implied warranty of
+//    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+//    GNU General Public License for more details.
+//
+//    You should have received a copy of the GNU General Public License
+//    along with Foobar.  If not, see <http://www.gnu.org/licenses/>.
+// ----------------------------------------------------------------------------
+
+#include "main.h"
+#include "cdmrmmdvmclient.h"
+
+
+////////////////////////////////////////////////////////////////////////////////////////
+// constructors
+
+CDmrmmdvmClient::CDmrmmdvmClient()
+{
+}
+
+CDmrmmdvmClient::CDmrmmdvmClient(const CCallsign &callsign, const CIp &ip, char reflectorModule)
+: CClient(callsign, ip, reflectorModule)
+{
+}
+
+CDmrmmdvmClient::CDmrmmdvmClient(const CDmrmmdvmClient &client)
+: CClient(client)
+{
+}
+
+////////////////////////////////////////////////////////////////////////////////////////
+// status
+
+bool CDmrmmdvmClient::IsAlive(void) const
+{
+    return (m_LastKeepaliveTime.DurationSinceNow() < DMRMMDVM_KEEPALIVE_TIMEOUT);
+}
diff --git a/src/cdmrmmdvmclient.h b/src/cdmrmmdvmclient.h
new file mode 100644
index 0000000..b5880ab
--- /dev/null
+++ b/src/cdmrmmdvmclient.h
@@ -0,0 +1,60 @@
+//
+//  cdmrmmdvmclient.h
+//  xlxd
+//
+//  Created by Jean-Luc Deltombe (LX3JL) on 04/03/2017.
+//  Copyright © 2015 Jean-Luc Deltombe (LX3JL). All rights reserved.
+//
+// ----------------------------------------------------------------------------
+//    This file is part of xlxd.
+//
+//    xlxd is free software: you can redistribute it and/or modify
+//    it under the terms of the GNU General Public License as published by
+//    the Free Software Foundation, either version 3 of the License, or
+//    (at your option) any later version.
+//
+//    xlxd is distributed in the hope that it will be useful,
+//    but WITHOUT ANY WARRANTY; without even the implied warranty of
+//    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+//    GNU General Public License for more details.
+//
+//    You should have received a copy of the GNU General Public License
+//    along with Foobar.  If not, see <http://www.gnu.org/licenses/>.
+// ----------------------------------------------------------------------------
+
+#ifndef cdmrmmdvmclient_h
+#define cdmrmmdvmclient_h
+
+#include "cclient.h"
+
+////////////////////////////////////////////////////////////////////////////////////////
+// define
+
+
+////////////////////////////////////////////////////////////////////////////////////////
+// class
+
+class CDmrmmdvmClient : public CClient
+{
+public:
+    // constructors
+    CDmrmmdvmClient();
+    CDmrmmdvmClient(const CCallsign &, const CIp &, char = ' ');
+    CDmrmmdvmClient(const CDmrmmdvmClient &);
+    
+    // destructor
+    virtual ~CDmrmmdvmClient() {};
+    
+    // identity
+    int GetProtocol(void) const                 { return PROTOCOL_DMRMMDVM; }
+    const char *GetProtocolName(void) const     { return "DMRMmdvm"; }
+    int GetCodec(void) const                    { return CODEC_AMBE2PLUS; }
+    bool IsNode(void) const                     { return true; }
+    
+    // status
+    bool IsAlive(void) const;
+};
+
+////////////////////////////////////////////////////////////////////////////////////////
+
+#endif /* cdmrmmdvmclient_h */
diff --git a/src/cdmrmmdvmprotocol.cpp b/src/cdmrmmdvmprotocol.cpp
new file mode 100644
index 0000000..3e8868e
--- /dev/null
+++ b/src/cdmrmmdvmprotocol.cpp
@@ -0,0 +1,1193 @@
+//
+//  cdmrmmdvmprotocol.cpp
+//  xlxd
+//
+//  Created by Jean-Luc Deltombe (LX3JL) on 104/03/2017.
+//  Copyright © 2015 Jean-Luc Deltombe (LX3JL). All rights reserved.
+//
+// ----------------------------------------------------------------------------
+//    This file is part of xlxd.
+//
+//    xlxd is free software: you can redistribute it and/or modify
+//    it under the terms of the GNU General Public License as published by
+//    the Free Software Foundation, either version 3 of the License, or
+//    (at your option) any later version.
+//
+//    xlxd is distributed in the hope that it will be useful,
+//    but WITHOUT ANY WARRANTY; without even the implied warranty of
+//    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+//    GNU General Public License for more details.
+//
+//    You should have received a copy of the GNU General Public License
+//    along with Foobar.  If not, see <http://www.gnu.org/licenses/>.
+// ----------------------------------------------------------------------------
+
+#include "main.h"
+#include <string.h>
+#include "cdmrmmdvmclient.h"
+#include "cdmrmmdvmprotocol.h"
+#include "creflector.h"
+#include "cgatekeeper.h"
+#include "cbptc19696.h"
+#include "crs129.h"
+#include "cgolay2087.h"
+#include "cqr1676.h"
+
+
+////////////////////////////////////////////////////////////////////////////////////////
+// define
+
+#define CMD_NONE        0
+#define CMD_LINK        1
+#define CMD_UNLINK      2
+
+////////////////////////////////////////////////////////////////////////////////////////
+// constants
+
+static uint8 g_DmrSyncBSVoice[]    = { 0x07,0x55,0xFD,0x7D,0xF7,0x5F,0x70 };
+static uint8 g_DmrSyncBSData[]     = { 0x0D,0xFF,0x57,0xD7,0x5D,0xF5,0xD0 };
+static uint8 g_DmrSyncMSVoice[]    = { 0x07,0xF7,0xD5,0xDD,0x57,0xDF,0xD0 };
+static uint8 g_DmrSyncMSData[]     = { 0x0D,0x5D,0x7F,0x77,0xFD,0x75,0x70 };
+
+
+////////////////////////////////////////////////////////////////////////////////////////
+// operation
+
+bool CDmrmmdvmProtocol::Init(void)
+{
+    bool ok;
+    
+    // base class
+    ok = CProtocol::Init();
+    
+    // update the reflector callsign
+    //m_ReflectorCallsign.PatchCallsign(0, (const uint8 *)"DMR", 3);
+    
+    // create our socket
+    ok &= m_Socket.Open(DMRMMDVM_PORT);
+    
+    // update time
+    m_LastKeepaliveTime.Now();
+    
+    // random number generator
+    time_t t;
+    ::srand((unsigned) time(&t));
+    m_uiAuthSeed = (uint32)rand();
+    
+    // done
+    return ok;
+}
+
+
+
+////////////////////////////////////////////////////////////////////////////////////////
+// task
+
+void CDmrmmdvmProtocol::Task(void)
+{
+    CBuffer             Buffer;
+    CIp                 Ip;
+    CCallsign           Callsign;
+    int                 iRssi;
+    uint8               Cmd;
+    uint8               CallType;
+    CDvHeaderPacket     *Header;
+    CDvFramePacket      *Frames[3];
+    CDvLastFramePacket  *LastFrame;
+    
+    // handle incoming packets
+    if ( m_Socket.Receive(&Buffer, &Ip, 20) != -1 )
+    {
+        //Buffer.DebugDump(g_Reflector.m_DebugFile);
+        // crack the packet
+        if ( IsValidDvFramePacket(Buffer, Frames) )
+        {
+            //std::cout << "DMRmmdvm DV frame" << std::endl;
+            
+            for ( int i = 0; i < 3; i++ )
+            {
+                OnDvFramePacketIn(Frames[i], &Ip);
+            }
+        }
+        else if ( IsValidDvHeaderPacket(Buffer, &Header, &Cmd, &CallType) )
+        {
+            //std::cout << "DMRmmdvm DV header:"  << std::endl <<  *Header << std::endl;
+            //std::cout << "DMRmmdvm DV header"  << std::endl;
+            
+            // callsign muted?
+            if ( g_GateKeeper.MayTransmit(Header->GetMyCallsign(), Ip, PROTOCOL_DMRMMDVM) )
+            {
+                // handle it
+                OnDvHeaderPacketIn(Header, Ip, Cmd, CallType);
+            }
+            else
+            {
+                delete Header;
+            }
+        }
+        else if ( IsValidDvLastFramePacket(Buffer, &LastFrame) )
+        {
+            //std::cout << "DMRmmdvm DV last frame"  << std::endl;
+            
+            OnDvLastFramePacketIn(LastFrame, &Ip);
+       }
+        else if ( IsValidConnectPacket(Buffer, &Callsign) )
+        {
+            std::cout << "DMRmmdvm connect packet from " << Callsign << " at " << Ip << std::endl;
+            
+            // callsign authorized?
+            if ( g_GateKeeper.MayLink(Callsign, Ip, PROTOCOL_DMRMMDVM) )
+            {
+                // acknowledge the request
+                EncodeConnectAckPacket(&Buffer, Callsign, m_uiAuthSeed);
+                m_Socket.Send(Buffer, Ip);
+            }
+            else
+            {
+                // deny the request
+                EncodeNackPacket(&Buffer, Callsign);
+                m_Socket.Send(Buffer, Ip);
+            }
+            
+        }
+        else if ( IsValidAuthenticationPacket(Buffer, &Callsign) )
+        {
+            std::cout << "DMRmmdvm authentication packet from " << Callsign << " at " << Ip << std::endl;
+            
+            // callsign authorized?
+            if ( g_GateKeeper.MayLink(Callsign, Ip, PROTOCOL_DMRMMDVM) )
+            {
+                // acknowledge the request
+                EncodeAckPacket(&Buffer, Callsign);
+                m_Socket.Send(Buffer, Ip);
+                
+                // add client if needed
+                CClients *clients = g_Reflector.GetClients();
+                CClient *client = clients->FindClient(Callsign, Ip, PROTOCOL_DMRMMDVM);
+                // client already connected ?
+                if ( client == NULL )
+                {
+                    std::cout << "DMRmmdvm login from " << Callsign << " at " << Ip << std::endl;
+                    
+                    // create the client
+                    CDmrmmdvmClient *newclient = new CDmrmmdvmClient(Callsign, Ip);
+                    
+                    // and append
+                    clients->AddClient(newclient);
+                }
+                else
+                {
+                    client->Alive();
+                }
+                // and done
+                g_Reflector.ReleaseClients();
+            }
+            else
+            {
+                // deny the request
+                EncodeNackPacket(&Buffer, Callsign);
+                m_Socket.Send(Buffer, Ip);
+            }
+            
+        }
+        else if ( IsValidDisconnectPacket(Buffer, &Callsign) )
+        {
+            std::cout << "DMRmmdvm disconnect packet from " << Callsign << " at " << Ip << std::endl;
+            
+            // find client & remove it
+            CClients *clients = g_Reflector.GetClients();
+            CClient *client = clients->FindClient(Ip, PROTOCOL_DMRMMDVM);
+            if ( client != NULL )
+            {
+                clients->RemoveClient(client);
+            }
+            g_Reflector.ReleaseClients();
+        }
+        else if ( IsValidConfigPacket(Buffer, &Callsign) )
+        {
+            std::cout << "DMRmmdvm configuration packet from " << Callsign << " at " << Ip << std::endl;
+            
+            // acknowledge the request
+            EncodeAckPacket(&Buffer, Callsign);
+            m_Socket.Send(Buffer, Ip);
+        }
+        else if ( IsValidKeepAlivePacket(Buffer, &Callsign) )
+        {
+            //std::cout << "DMRmmdvm keepalive packet from " << Callsign << " at " << Ip << std::endl;
+            
+            // find all clients with that callsign & ip and keep them alive
+            CClients *clients = g_Reflector.GetClients();
+            int index = -1;
+            CClient *client = NULL;
+            while ( (client = clients->FindNextClient(Callsign, Ip, PROTOCOL_DMRMMDVM, &index)) != NULL )
+            {
+                // acknowledge
+                EncodeKeepAlivePacket(&Buffer, client);
+                m_Socket.Send(Buffer, Ip);
+                
+                // and mark as alive
+                client->Alive();
+            }
+            g_Reflector.ReleaseClients();
+        }
+        else if ( IsValidRssiPacket(Buffer, &Callsign, &iRssi) )
+        {
+            // std::cout << "DMRmmdvm RSSI packet from " << Callsign << " at " << Ip << std::endl
+            
+            // ignore...
+        }
+        else if ( IsValidOptionPacket(Buffer, &Callsign) )
+        {
+            std::cout << "DMRmmdvm options packet from " << Callsign << " at " << Ip << std::endl;
+            
+            // acknowledge the request
+            EncodeAckPacket(&Buffer, Callsign);
+            m_Socket.Send(Buffer, Ip);
+        }
+        else if ( Buffer.size() != 55 )
+        {
+            std::cout << "DMRmmdvm packet (" << Buffer.size() << ")" << std::endl;
+            //std::cout << Buffer << std::endl;
+        }
+    }
+    
+    // handle end of streaming timeout
+    CheckStreamsTimeout();
+    
+    // handle queue from reflector
+    HandleQueue();
+    
+    
+    // keep client alive
+    if ( m_LastKeepaliveTime.DurationSinceNow() > DMRMMDVM_KEEPALIVE_PERIOD )
+    {
+        //
+        HandleKeepalives();
+        
+        // update time
+        m_LastKeepaliveTime.Now();
+    }
+    
+}
+
+////////////////////////////////////////////////////////////////////////////////////////
+// streams helpers
+
+bool CDmrmmdvmProtocol::OnDvHeaderPacketIn(CDvHeaderPacket *Header, const CIp &Ip, uint8 cmd, uint8 CallType)
+{
+    bool newstream = false;
+    bool lastheard = false;
+    
+    //
+    CCallsign via(Header->GetRpt1Callsign());
+
+    // find the stream
+    CPacketStream *stream = GetStream(Header->GetStreamId());
+    if ( stream == NULL )
+    {
+        // no stream open yet, open a new one
+        // firstfind this client
+        CClient *client = g_Reflector.GetClients()->FindClient(Ip, PROTOCOL_DMRMMDVM);
+        if ( client != NULL )
+        {
+            // process cmd if any
+            if ( !client->HasReflectorModule() )
+            {
+                // not linked yet
+                if ( cmd == CMD_LINK )
+                {
+                    if ( g_Reflector.IsValidModule(Header->GetRpt2Module()) )
+                    {
+                        std::cout << "DMRmmdvm client " << client->GetCallsign() << " linking on module " << Header->GetRpt2Module() << std::endl;
+                        // link
+                        client->SetReflectorModule(Header->GetRpt2Module());
+                   }
+                    else
+                    {
+                        std::cout << "DMRMMDVM node " << via << " link attempt on non-existing module" << std::endl;
+                    }
+                }
+            }
+            else
+            {
+                // already linked
+                if ( cmd == CMD_UNLINK )
+                {
+                    std::cout << "DMRmmdvm client " << client->GetCallsign() << " unlinking" << std::endl;
+                    // unlink
+                    client->SetReflectorModule(' ');
+                }
+                else
+                {
+                    // replace rpt2 module with currently linked module
+                    Header->SetRpt2Module(client->GetReflectorModule());
+                }
+            }
+            
+            // and now, re-check module is valid && that it's not a private call
+            if ( g_Reflector.IsValidModule(Header->GetRpt2Module()) && (CallType == DMR_GROUP_CALL) )
+            {
+                // yes, try to open the stream
+                if ( (stream = g_Reflector.OpenStream(Header, client)) != NULL )
+                {
+                    // keep the handle
+                    m_Streams.push_back(stream);
+                    newstream = true;
+                    lastheard = true;
+                }
+            }
+        }
+        else
+        {
+            lastheard = true;
+        }
+        
+        // release
+        g_Reflector.ReleaseClients();
+        
+        // update last heard
+        if ( lastheard )
+        {
+            g_Reflector.GetUsers()->Hearing(Header->GetMyCallsign(), via, Header->GetRpt2Callsign());
+            g_Reflector.ReleaseUsers();
+        }
+        
+        // delete header if needed
+        if ( !newstream )
+        {
+            delete Header;
+        }
+    }
+    else
+    {
+        // stream already open
+        // skip packet, but tickle the stream
+        stream->Tickle();
+        // and delete packet
+        delete Header;
+    }
+    
+    // done
+    return newstream;
+}
+
+////////////////////////////////////////////////////////////////////////////////////////
+// queue helper
+
+void CDmrmmdvmProtocol::HandleQueue(void)
+{
+    
+    m_Queue.Lock();
+    while ( !m_Queue.empty() )
+    {
+        // get the packet
+        CPacket *packet = m_Queue.front();
+        m_Queue.pop();
+        
+        // get our sender's id
+        int iModId = g_Reflector.GetModuleIndex(packet->GetModuleId());
+        
+        // encode
+        CBuffer buffer;
+        
+        // check if it's header
+        if ( packet->IsDvHeader() )
+        {
+            // update local stream cache
+            // this relies on queue feeder setting valid module id
+            m_StreamsCache[iModId].m_dvHeader = CDvHeaderPacket((const CDvHeaderPacket &)*packet);
+            m_StreamsCache[iModId].m_uiSeqId = 0;
+            
+            // encode it
+            EncodeDvHeaderPacket((const CDvHeaderPacket &)*packet, m_StreamsCache[iModId].m_uiSeqId, &buffer);
+            m_StreamsCache[iModId].m_uiSeqId = 1;
+        }
+        // check if it's a last frame
+        else if ( packet->IsLastPacket() )
+        {
+            // encode it
+            EncodeDvLastPacket(m_StreamsCache[iModId].m_dvHeader, m_StreamsCache[iModId].m_uiSeqId, &buffer);
+            m_StreamsCache[iModId].m_uiSeqId = (m_StreamsCache[iModId].m_uiSeqId + 1) & 0xFF;
+        }
+        // otherwise, just a regular DV frame
+        else
+        {
+            // update local stream cache or send triplet when needed
+            switch ( packet->GetDmrPacketSubid() )
+            {
+                case 1:
+                    m_StreamsCache[iModId].m_dvFrame0 = CDvFramePacket((const CDvFramePacket &)*packet);
+                    break;
+                case 2:
+                    m_StreamsCache[iModId].m_dvFrame1 = CDvFramePacket((const CDvFramePacket &)*packet);
+                    break;
+                case 3:
+                    EncodeDvPacket(
+                                   m_StreamsCache[iModId].m_dvHeader,
+                                   m_StreamsCache[iModId].m_dvFrame0,
+                                   m_StreamsCache[iModId].m_dvFrame1,
+                                   (const CDvFramePacket &)*packet,
+                                   m_StreamsCache[iModId].m_uiSeqId,
+                                   &buffer);
+                    m_StreamsCache[iModId].m_uiSeqId = (m_StreamsCache[iModId].m_uiSeqId + 1) & 0xFF;
+                    break;
+                default:
+                    break;
+            }
+        }
+        
+        // send it
+        if ( buffer.size() > 0 )
+        {
+            // and push it to all our clients linked to the module and who are not streaming in
+            CClients *clients = g_Reflector.GetClients();
+            int index = -1;
+            CClient *client = NULL;
+            while ( (client = clients->FindNextClient(PROTOCOL_DMRMMDVM, &index)) != NULL )
+            {
+                // is this client busy ?
+                if ( !client->IsAMaster() && (client->GetReflectorModule() == packet->GetModuleId()) )
+                {
+                    // no, send the packet
+                    m_Socket.Send(buffer, client->GetIp());
+                    
+                }
+            }
+            g_Reflector.ReleaseClients();
+        }
+        
+        // done
+        delete packet;
+    }
+    m_Queue.Unlock();
+}
+
+////////////////////////////////////////////////////////////////////////////////////////
+// keepalive helpers
+
+void CDmrmmdvmProtocol::HandleKeepalives(void)
+{
+    // DMRhomebrew protocol keepalive request is client tasks
+    // here, just check that all clients are still alive
+    // and disconnect them if not
+    
+    // iterate on clients
+    CClients *clients = g_Reflector.GetClients();
+    int index = -1;
+    CClient *client = NULL;
+    while ( (client = clients->FindNextClient(PROTOCOL_DMRMMDVM, &index)) != NULL )
+    {
+        // is this client busy ?
+        if ( client->IsAMaster() )
+        {
+            // yes, just tickle it
+            client->Alive();
+        }
+        // check it's still with us
+        else if ( !client->IsAlive() )
+        {
+            // no, disconnect
+            CBuffer disconnect;
+            m_Socket.Send(disconnect, client->GetIp());
+            
+            // remove it
+            std::cout << "DMRmmdvm client " << client->GetCallsign() << " keepalive timeout" << std::endl;
+            clients->RemoveClient(client);
+        }
+        
+    }
+    g_Reflector.ReleaseClients();
+}
+
+////////////////////////////////////////////////////////////////////////////////////////
+// packet decoding helpers
+
+bool CDmrmmdvmProtocol::IsValidKeepAlivePacket(const CBuffer &Buffer, CCallsign *callsign)
+{
+    uint8 tag[] = { 'R','P','T','P','I','N','G' };
+    
+    bool valid = false;
+    if ( (Buffer.size() == 11) && (Buffer.Compare(tag, sizeof(tag)) == 0) )
+    {
+        uint32 uiRptrId = MAKEDWORD(MAKEWORD(Buffer.data()[10],Buffer.data()[9]),MAKEWORD(Buffer.data()[8],Buffer.data()[7]));
+        callsign->SetDmrid(uiRptrId, true);
+        callsign->SetModule('B');
+        valid = callsign->IsValid();
+    }
+    return valid;
+}
+
+bool CDmrmmdvmProtocol::IsValidConnectPacket(const CBuffer &Buffer, CCallsign *callsign)
+{
+    uint8 tag[] = { 'R','P','T','L' };
+    
+    bool valid = false;
+    if ( (Buffer.size() == 8) && (Buffer.Compare(tag, sizeof(tag)) == 0) )
+    {
+        uint32 uiRptrId = MAKEDWORD(MAKEWORD(Buffer.data()[7],Buffer.data()[6]),MAKEWORD(Buffer.data()[5],Buffer.data()[4]));
+        callsign->SetDmrid(uiRptrId, true);
+        callsign->SetModule('B');
+        valid = callsign->IsValid();
+        if ( !valid)
+        {
+            std::cout << "DMRmmdvm connect packet from unrecognized id " << (int)callsign->GetDmrid()  << std::endl;
+        }
+    }
+    return valid;
+}
+
+bool CDmrmmdvmProtocol::IsValidAuthenticationPacket(const CBuffer &Buffer, CCallsign *callsign)
+{
+    uint8 tag[] = { 'R','P','T','K' };
+    
+    bool valid = false;
+    if ( (Buffer.size() == 40) && (Buffer.Compare(tag, sizeof(tag)) == 0) )
+    {
+        uint32 uiRptrId = MAKEDWORD(MAKEWORD(Buffer.data()[7],Buffer.data()[6]),MAKEWORD(Buffer.data()[5],Buffer.data()[4]));
+        callsign->SetDmrid(uiRptrId, true);
+        callsign->SetModule('B');
+        valid = callsign->IsValid();
+    }
+    return valid;
+}
+
+bool CDmrmmdvmProtocol::IsValidDisconnectPacket(const CBuffer &Buffer, CCallsign *callsign)
+{
+    uint8 tag[] = { 'R','P','T','C','L' };
+    
+    bool valid = false;
+    if ( (Buffer.size() == 13) && (Buffer.Compare(tag, sizeof(tag)) == 0) )
+    {
+        uint32 uiRptrId = MAKEDWORD(MAKEWORD(Buffer.data()[7],Buffer.data()[6]),MAKEWORD(Buffer.data()[5],Buffer.data()[4]));
+        callsign->SetDmrid(uiRptrId, true);
+        callsign->SetModule('B');
+        valid = callsign->IsValid();
+    }
+    return valid;
+}
+
+bool CDmrmmdvmProtocol::IsValidConfigPacket(const CBuffer &Buffer, CCallsign *callsign)
+{
+    uint8 tag[] = { 'R','P','T','C' };
+    
+    bool valid = false;
+    if ( (Buffer.size() == 302) && (Buffer.Compare(tag, sizeof(tag)) == 0) )
+    {
+        uint32 uiRptrId = MAKEDWORD(MAKEWORD(Buffer.data()[7],Buffer.data()[6]),MAKEWORD(Buffer.data()[5],Buffer.data()[4]));
+        callsign->SetDmrid(uiRptrId, true);
+        callsign->SetModule('B');
+        valid = callsign->IsValid();
+    }
+    return valid;
+}
+
+bool CDmrmmdvmProtocol::IsValidOptionPacket(const CBuffer &Buffer, CCallsign *callsign)
+{
+    uint8 tag[] = { 'R','P','T','O' };
+    
+    bool valid = false;
+    if ( (Buffer.size() >= 8) && (Buffer.Compare(tag, sizeof(tag)) == 0) )
+    {
+        uint32 uiRptrId = MAKEDWORD(MAKEWORD(Buffer.data()[7],Buffer.data()[6]),MAKEWORD(Buffer.data()[5],Buffer.data()[4]));
+        callsign->SetDmrid(uiRptrId, true);
+        callsign->SetModule('B');
+        valid = callsign->IsValid();
+    }
+    return valid;
+}
+
+bool CDmrmmdvmProtocol::IsValidRssiPacket(const CBuffer &Buffer, CCallsign *callsign, int *rssi)
+{
+    uint8 tag[] = { 'R','P','T','I','N','T','R' };
+    
+    bool valid = false;
+    if ( (Buffer.size() == 17) && (Buffer.Compare(tag, sizeof(tag)) == 0) )
+    {
+        // ignore rest of it, as not used
+        // dmrid is asci hex on 8 bytes
+        // rssi is ascii :x-xx
+        valid = true;
+    }
+    return valid;
+}
+
+bool CDmrmmdvmProtocol::IsValidDvHeaderPacket(const CBuffer &Buffer, CDvHeaderPacket **header, uint8 *cmd, uint8 *CallType)
+{
+    uint8 tag[] = { 'D','M','R','D' };
+    
+    bool valid = false;
+    *header = NULL;
+    *cmd = CMD_NONE;
+    
+    if ( (Buffer.size() == 55) && (Buffer.Compare(tag, sizeof(tag)) == 0) )
+    {
+        // frame details
+        uint8 uiFrameType = (Buffer.data()[15] & 0x30) >> 4;
+        uint8 uiSlot = (Buffer.data()[15] & 0x80) ? DMR_SLOT2 : DMR_SLOT1;
+        uint8 uiCallType = (Buffer.data()[15] & 0x40) ? DMR_PRIVATE_CALL : DMR_GROUP_CALL;
+        uint8 uiSlotType = Buffer.data()[15] & 0x0F;
+        //std::cout << (int)uiSlot << std::endl;
+        if ( (uiFrameType == DMRMMDVM_FRAMETYPE_DATASYNC) &&
+             (uiSlot == DMRMMDVM_REFLECTOR_SLOT) &&
+             (uiSlotType == MMDVM_SLOTTYPE_HEADER) )
+        {
+            // extract sync
+            uint8 dmrsync[7];
+            dmrsync[0] = Buffer.data()[33] & 0x0F;
+            ::memcpy(&dmrsync[1], &Buffer.data()[34], 5);
+            dmrsync[6] = Buffer.data()[39] & 0xF0;
+            // and check
+            if ( (::memcmp(dmrsync, g_DmrSyncMSData, sizeof(dmrsync)) == 0) ||
+                 (::memcmp(dmrsync, g_DmrSyncBSData, sizeof(dmrsync)) == 0))
+            {
+                // get payload
+                //CBPTC19696 bptc;
+                //uint8 lcdata[12];
+                //bptc.decode(&(Buffer.data()[20]), lcdata);
+                
+                // crack DMR header
+                //uint8 uiSeqId = Buffer.data()[4];
+                uint32 uiSrcId = MAKEDWORD(MAKEWORD(Buffer.data()[7],Buffer.data()[6]),MAKEWORD(Buffer.data()[5],0));
+                uint32 uiDstId = MAKEDWORD(MAKEWORD(Buffer.data()[10],Buffer.data()[9]),MAKEWORD(Buffer.data()[8],0));
+                uint32 uiRptrId = MAKEDWORD(MAKEWORD(Buffer.data()[14],Buffer.data()[13]),MAKEWORD(Buffer.data()[12],Buffer.data()[11]));
+                //uint8 uiVoiceSeq = (Buffer.data()[15] & 0x0F);
+                uint32 uiStreamId = *(uint32 *)(&Buffer.data()[16]);
+                
+                // call type
+                *CallType = uiCallType;
+                
+                // link/unlink command ?
+                if ( uiDstId == 4000 )
+                {
+                    *cmd = CMD_UNLINK;
+                }
+                else if ( DmrDstIdToModule(uiDstId) != ' ' )
+                {
+                    *cmd = CMD_LINK;
+                }
+                else
+                {
+                    *cmd = CMD_NONE;
+                }
+                
+                // build DVHeader
+                CCallsign csMY =  CCallsign("", uiSrcId);
+                CCallsign rpt1 = CCallsign("", uiRptrId);
+                rpt1.SetModule('B');
+                CCallsign rpt2 = m_ReflectorCallsign;
+                rpt2.SetModule(DmrDstIdToModule(uiDstId));
+                
+                // and packet
+                *header = new CDvHeaderPacket(uiSrcId, CCallsign("CQCQCQ"), rpt1, rpt2, uiStreamId, 0, 0);
+                valid = (*header)->IsValid();
+                if ( !valid )
+                {
+                    delete *header;
+                    *header = NULL;
+                }
+            }
+        }
+    }
+    // done
+    return valid;
+}
+
+bool CDmrmmdvmProtocol::IsValidDvFramePacket(const CBuffer &Buffer, CDvFramePacket **frames)
+{
+    uint8 tag[] = { 'D','M','R','D' };
+    
+    bool valid = false;
+    frames[0] = NULL;
+    frames[1] = NULL;
+    frames[2] = NULL;
+    
+    if ( (Buffer.size() == 55) && (Buffer.Compare(tag, sizeof(tag)) == 0) )
+    {
+        // frame details
+        uint8 uiFrameType = (Buffer.data()[15] & 0x30) >> 4;
+        uint8 uiSlot = (Buffer.data()[15] & 0x80) ? DMR_SLOT2 : DMR_SLOT1;
+        uint8 uiCallType = (Buffer.data()[15] & 0x40) ? DMR_PRIVATE_CALL : DMR_GROUP_CALL;
+        if ( ((uiFrameType == DMRMMDVM_FRAMETYPE_VOICE) || (uiFrameType == DMRMMDVM_FRAMETYPE_VOICESYNC)) &&
+              (uiSlot == DMRMMDVM_REFLECTOR_SLOT) && (uiCallType == DMR_GROUP_CALL) )
+        {
+            // crack DMR header
+            //uint8 uiSeqId = Buffer.data()[4];
+            //uint32 uiSrcId = MAKEDWORD(MAKEWORD(Buffer.data()[7],Buffer.data()[6]),MAKEWORD(Buffer.data()[5],0));
+            //uint32 uiDstId = MAKEDWORD(MAKEWORD(Buffer.data()[10],Buffer.data()[9]),MAKEWORD(Buffer.data()[8],0));
+            //uint32 uiRptrId = MAKEDWORD(MAKEWORD(Buffer.data()[14],Buffer.data()[13]),MAKEWORD(Buffer.data()[12],Buffer.data()[11]));
+            uint8 uiVoiceSeq = (Buffer.data()[15] & 0x0F);
+            uint32 uiStreamId = *(uint32 *)(&Buffer.data()[16]);
+           
+            // crack payload
+            uint8 dmrframe[33];
+            uint8 dmr3ambe[27];
+            uint8 dmrambe[9];
+            uint8 dmrsync[7];
+            // get the 33 bytes ambe
+            memcpy(dmrframe, &(Buffer.data()[20]), 33);
+            // extract the 3 ambe frames
+            memcpy(dmr3ambe, dmrframe, 14);
+            dmr3ambe[13] &= 0xF0;
+            dmr3ambe[13] |= (dmrframe[19] & 0x0F);
+            memcpy(&dmr3ambe[14], &dmrframe[20], 14);
+            // extract sync
+            dmrsync[0] = dmrframe[13] & 0x0F;
+            ::memcpy(&dmrsync[1], &dmrframe[14], 5);
+            dmrsync[6] = dmrframe[19] & 0xF0;
+            
+            // debug
+            //CBuffer dump;
+            //dump.Set(dmrsync, 6);
+            //dump.DebugDump(g_Reflector.m_DebugFile);
+            
+            // and create 3 dv frames
+            // frame1
+            memcpy(dmrambe, &dmr3ambe[0], 9);
+            frames[0] = new CDvFramePacket(dmrambe, dmrsync, uiStreamId, uiVoiceSeq, 1);
+            
+            // frame2
+            memcpy(dmrambe, &dmr3ambe[9], 9);
+            frames[1] = new CDvFramePacket(dmrambe, dmrsync, uiStreamId, uiVoiceSeq, 2);
+            
+            // frame3
+            memcpy(dmrambe, &dmr3ambe[18], 9);
+            frames[2] = new CDvFramePacket(dmrambe, dmrsync, uiStreamId, uiVoiceSeq, 3);
+            
+            // check
+            valid = true;
+        }
+    }
+    // done
+    return valid;
+}
+
+bool CDmrmmdvmProtocol::IsValidDvLastFramePacket(const CBuffer &Buffer, CDvLastFramePacket **frame)
+{
+    uint8 tag[] = { 'D','M','R','D' };
+    
+    bool valid = false;
+    *frame = NULL;
+    
+    if ( (Buffer.size() == 55) && (Buffer.Compare(tag, sizeof(tag)) == 0) )
+    {
+        // frame details
+        uint8 uiFrameType = (Buffer.data()[15] & 0x30) >> 4;
+        uint8 uiSlot = (Buffer.data()[15] & 0x80) ? DMR_SLOT2 : DMR_SLOT1;
+        //uint8 uiCallType = (Buffer.data()[15] & 0x40) ? DMR_PRIVATE_CALL : DMR_GROUP_CALL;
+        uint8 uiSlotType = Buffer.data()[15] & 0x0F;
+        //std::cout << (int)uiSlot << std::endl;
+        if ( (uiFrameType == DMRMMDVM_FRAMETYPE_DATASYNC) &&
+            (uiSlot == DMRMMDVM_REFLECTOR_SLOT) &&
+            (uiSlotType == MMDVM_SLOTTYPE_TERMINATOR) )
+        {
+            // extract sync
+            uint8 dmrsync[7];
+            dmrsync[0] = Buffer.data()[33] & 0x0F;
+            ::memcpy(&dmrsync[1], &Buffer.data()[34], 5);
+            dmrsync[6] = Buffer.data()[39] & 0xF0;
+            // and check
+            if ( (::memcmp(dmrsync, g_DmrSyncMSData, sizeof(dmrsync)) == 0) ||
+                (::memcmp(dmrsync, g_DmrSyncBSData, sizeof(dmrsync)) == 0))
+            {
+                // get payload
+                //CBPTC19696 bptc;
+                //uint8 lcdata[12];
+                //bptc.decode(&(Buffer.data()[20]), lcdata);
+                
+                // crack DMR header
+                //uint8 uiSeqId = Buffer.data()[4];
+                //uint32 uiSrcId = MAKEDWORD(MAKEWORD(Buffer.data()[7],Buffer.data()[6]),MAKEWORD(Buffer.data()[5],0));
+                //uint32 uiDstId = MAKEDWORD(MAKEWORD(Buffer.data()[10],Buffer.data()[9]),MAKEWORD(Buffer.data()[8],0));
+                //uint32 uiRptrId = MAKEDWORD(MAKEWORD(Buffer.data()[14],Buffer.data()[13]),MAKEWORD(Buffer.data()[12],Buffer.data()[11]));
+                //uint8 uiVoiceSeq = (Buffer.data()[15] & 0x0F);
+                uint32 uiStreamId = *(uint32 *)(&Buffer.data()[16]);
+                
+                // dummy ambe
+                uint8 ambe[9];
+                ::memset(ambe, 0, sizeof(ambe));
+                
+                
+                // and packet
+                *frame = new CDvLastFramePacket(ambe, dmrsync, uiStreamId, 0, 0);
+                
+                // check
+                valid = true;
+            }
+        }
+    }
+    // done
+    return valid;
+}
+
+////////////////////////////////////////////////////////////////////////////////////////
+// packet encoding helpers
+
+void CDmrmmdvmProtocol::EncodeKeepAlivePacket(CBuffer *Buffer, CClient *Client)
+{
+    uint8 tag[] = { 'M','S','T','P','O','N','G' };
+    
+    Buffer->Set(tag, sizeof(tag));
+    uint32 uiDmrId = Client->GetCallsign().GetDmrid();
+    Buffer->Append((uint8 *)&uiDmrId, 4);
+}
+
+void CDmrmmdvmProtocol::EncodeAckPacket(CBuffer *Buffer, const CCallsign &Callsign)
+{
+    uint8 tag[] = { 'R','P','T','A','C','K' };
+    
+    Buffer->Set(tag, sizeof(tag));
+}
+
+void CDmrmmdvmProtocol::EncodeConnectAckPacket(CBuffer *Buffer, const CCallsign &Callsign, uint32 AuthSeed)
+{
+    uint8 tag[] = { 'R','P','T','A','C','K' };
+    
+    Buffer->Set(tag, sizeof(tag));
+    Buffer->Append(AuthSeed);
+}
+
+void CDmrmmdvmProtocol::EncodeNackPacket(CBuffer *Buffer, const CCallsign &Callsign)
+{
+    uint8 tag[] = { 'M','S','T','N','A','K' };
+    
+    Buffer->Set(tag, sizeof(tag));
+}
+
+void CDmrmmdvmProtocol::EncodeClosePacket(CBuffer *Buffer, CClient *Client)
+{
+    uint8 tag[] = { 'M','S','T','C','L' };
+    
+    Buffer->Set(tag, sizeof(tag));
+}
+
+
+bool CDmrmmdvmProtocol::EncodeDvHeaderPacket(const CDvHeaderPacket &Packet, uint8 seqid, CBuffer *Buffer) const
+{
+    uint8 tag[] = { 'D','M','R','D' };
+    
+    Buffer->Set(tag, sizeof(tag));
+    
+    // DMR header
+    // uiSeqId
+    Buffer->Append((uint8)seqid);
+    // uiSrcId
+    uint32 uiSrcId = Packet.GetMyCallsign().GetDmrid();
+    AppendDmrIdToBuffer(Buffer, uiSrcId);
+    // uiDstId = TG9
+    uint32 uiDstId = 9; // ModuleToDmrDestId(Packet.GetRpt2Module());
+    AppendDmrIdToBuffer(Buffer, uiDstId);
+    // uiRptrId
+    uint32 uiRptrId = Packet.GetRpt1Callsign().GetDmrid();
+    AppendDmrRptrIdToBuffer(Buffer, uiRptrId);
+    // uiBitField
+    uint8 uiBitField =
+        (DMRMMDVM_FRAMETYPE_DATASYNC << 4) |
+        ((DMRMMDVM_REFLECTOR_SLOT == DMR_SLOT2) ? 0x80 : 0x00) |
+        MMDVM_SLOTTYPE_HEADER;
+    Buffer->Append((uint8)uiBitField);
+    // uiStreamId
+    uint32 uiStreamId = Packet.GetStreamId();
+    Buffer->Append((uint32)uiStreamId);
+    
+    // Payload
+    AppendVoiceLCToBuffer(Buffer, uiSrcId);
+    
+    // BER
+    Buffer->Append((uint8)0);
+    
+    // RSSI
+    Buffer->Append((uint8)0);
+    
+    // done
+    return true;
+}
+
+void CDmrmmdvmProtocol::EncodeDvPacket(
+      const CDvHeaderPacket &Header,
+      const CDvFramePacket &DvFrame0, const CDvFramePacket &DvFrame1, const CDvFramePacket &DvFrame2,
+      uint8 seqid, CBuffer *Buffer) const
+{
+    uint8 tag[] = { 'D','M','R','D' };
+    Buffer->Set(tag, sizeof(tag));
+    
+    // DMR header
+    // uiSeqId
+    Buffer->Append((uint8)seqid);
+    // uiSrcId
+    uint32 uiSrcId = Header.GetMyCallsign().GetDmrid();
+    AppendDmrIdToBuffer(Buffer, uiSrcId);
+    // uiDstId = TG9
+    uint32 uiDstId = 9; // ModuleToDmrDestId(Header.GetRpt2Module());
+    AppendDmrIdToBuffer(Buffer, uiDstId);
+    // uiRptrId
+    uint32 uiRptrId = Header.GetRpt1Callsign().GetDmrid();
+    AppendDmrRptrIdToBuffer(Buffer, uiRptrId);
+    // uiBitField
+    uint8 uiBitField =
+        ((DMRMMDVM_REFLECTOR_SLOT == DMR_SLOT2) ? 0x80 : 0x00);
+    if ( DvFrame0.GetDmrPacketId() == 0 )
+    {
+        uiBitField |= (DMRMMDVM_FRAMETYPE_VOICESYNC << 4);
+    }
+    else
+    {
+        uiBitField |= (DMRMMDVM_FRAMETYPE_VOICE << 4);
+    }
+    uiBitField |= (DvFrame0.GetDmrPacketId() & 0x0F);
+    Buffer->Append((uint8)uiBitField);
+    
+    // uiStreamId
+    uint32 uiStreamId = Header.GetStreamId();
+    Buffer->Append((uint32)uiStreamId);
+    
+    // Payload
+    // frame0
+    Buffer->ReplaceAt(20, DvFrame0.GetAmbePlus(), 9);
+    // 1/2 frame1
+    Buffer->ReplaceAt(29, DvFrame1.GetAmbePlus(), 5);
+    Buffer->ReplaceAt(33, (uint8)(Buffer->at(33) & 0xF0));
+    // 1/2 frame1
+    Buffer->ReplaceAt(39, DvFrame1.GetAmbePlus()+4, 5);
+    Buffer->ReplaceAt(39, (uint8)(Buffer->at(39) & 0x0F));
+    // frame2
+    Buffer->ReplaceAt(44, DvFrame2.GetAmbePlus(), 9);
+    
+    // sync or embedded signaling
+    ReplaceEMBInBuffer(Buffer, DvFrame0.GetDmrPacketId());
+
+    // debug
+    //CBuffer dump;
+    //dump.Set(&(Buffer->data()[33]), 7);
+    //dump.DebugDump(g_Reflector.m_DebugFile);
+
+    // BER
+    Buffer->Append((uint8)0);
+    
+    // RSSI
+    Buffer->Append((uint8)0);    
+}
+
+
+void CDmrmmdvmProtocol::EncodeDvLastPacket(const CDvHeaderPacket &Packet, uint8 seqid, CBuffer *Buffer) const
+{
+    uint8 tag[] = { 'D','M','R','D' };
+    
+    Buffer->Set(tag, sizeof(tag));
+    
+    // DMR header
+    // uiSeqId
+    Buffer->Append((uint8)seqid);
+    // uiSrcId
+    uint32 uiSrcId = Packet.GetMyCallsign().GetDmrid();
+    AppendDmrIdToBuffer(Buffer, uiSrcId);
+    // uiDstId
+    uint32 uiDstId = 9; //ModuleToDmrDestId(Packet.GetRpt2Module());
+    AppendDmrIdToBuffer(Buffer, uiDstId);
+    // uiRptrId
+    uint32 uiRptrId = Packet.GetRpt1Callsign().GetDmrid();
+    AppendDmrRptrIdToBuffer(Buffer, uiRptrId);
+    // uiBitField
+    uint8 uiBitField =
+        (DMRMMDVM_FRAMETYPE_DATASYNC << 4) |
+        ((DMRMMDVM_REFLECTOR_SLOT == DMR_SLOT2) ? 0x80 : 0x00) |
+        MMDVM_SLOTTYPE_TERMINATOR;
+    Buffer->Append((uint8)uiBitField);
+    // uiStreamId
+    uint32 uiStreamId = Packet.GetStreamId();
+    Buffer->Append((uint32)uiStreamId);
+    
+    // Payload
+    AppendTerminatorLCToBuffer(Buffer, uiSrcId);
+    
+    // BER
+    Buffer->Append((uint8)0);
+    
+    // RSSI
+    Buffer->Append((uint8)0);
+}
+
+
+////////////////////////////////////////////////////////////////////////////////////////
+// DestId to Module helper
+
+char CDmrmmdvmProtocol::DmrDstIdToModule(uint32 tg) const
+{
+    // is it a 4xxx ?
+    if ( (tg >= 4001) && (tg <= (4000 + NB_OF_MODULES)) )
+    {
+        return ((char)(tg - 4001) + 'A');
+    }
+    return ' ';
+}
+
+uint32 CDmrmmdvmProtocol::ModuleToDmrDestId(char m) const
+{
+    return (uint32)(m - 'A')+4001;
+}
+
+////////////////////////////////////////////////////////////////////////////////////////
+// Buffer & LC helpers
+
+void CDmrmmdvmProtocol::AppendVoiceLCToBuffer(CBuffer *buffer, uint32 uiSrcId) const
+{
+    uint8 payload[33];
+    
+    // fill payload
+    CBPTC19696 bptc;
+    ::memset(payload, 0, sizeof(payload));
+    // LC data
+    uint8 lc[12];
+    {
+        ::memset(lc, 0, sizeof(lc));
+        // uiDstId = TG9
+        lc[5] = 9;
+        // uiSrcId
+        lc[6] = (uint8)LOBYTE(HIWORD(uiSrcId));
+        lc[7] = (uint8)HIBYTE(LOWORD(uiSrcId));
+        lc[8] = (uint8)LOBYTE(LOWORD(uiSrcId));
+        // parity
+        uint8 parity[4];
+        CRS129::encode(lc, 9, parity);
+        lc[9]  = parity[2] ^ DMR_VOICE_LC_HEADER_CRC_MASK;
+        lc[10] = parity[1] ^ DMR_VOICE_LC_HEADER_CRC_MASK;
+        lc[11] = parity[0] ^ DMR_VOICE_LC_HEADER_CRC_MASK;
+    }
+    // sync
+    ::memcpy(payload+13, g_DmrSyncBSData, sizeof(g_DmrSyncBSData));
+    // slot type
+    {
+        // slot type
+        uint8 slottype[3];
+        ::memset(slottype, 0, sizeof(slottype));
+        slottype[0]  = (DMRMMDVM_REFLECTOR_COLOUR << 4) & 0xF0;
+        slottype[0] |= (DMR_DT_VOICE_LC_HEADER  << 0) & 0x0FU;
+        CGolay2087::encode(slottype);
+        payload[12U] = (payload[12U] & 0xC0U) | ((slottype[0U] >> 2) & 0x3FU);
+        payload[13U] = (payload[13U] & 0x0FU) | ((slottype[0U] << 6) & 0xC0U) | ((slottype[1U] >> 2) & 0x30U);
+        payload[19U] = (payload[19U] & 0xF0U) | ((slottype[1U] >> 2) & 0x0FU);
+        payload[20U] = (payload[20U] & 0x03U) | ((slottype[1U] << 6) & 0xC0U) | ((slottype[2U] >> 2) & 0x3CU);
+        
+    }
+    // and encode
+    bptc.encode(lc, payload);
+    
+    // and append
+    buffer->Append(payload, sizeof(payload));
+}
+
+void CDmrmmdvmProtocol::AppendTerminatorLCToBuffer(CBuffer *buffer, uint32 uiSrcId) const
+{
+    uint8 payload[33];
+    
+    // fill payload
+    CBPTC19696 bptc;
+    ::memset(payload, 0, sizeof(payload));
+    // LC data
+    uint8 lc[12];
+    {
+        ::memset(lc, 0, sizeof(lc));
+        // uiDstId = TG9
+        lc[5] = 9;
+        // uiSrcId
+        lc[6] = (uint8)LOBYTE(HIWORD(uiSrcId));
+        lc[7] = (uint8)HIBYTE(LOWORD(uiSrcId));
+        lc[8] = (uint8)LOBYTE(LOWORD(uiSrcId));
+        // parity
+        uint8 parity[4];
+        CRS129::encode(lc, 9, parity);
+        lc[9]  = parity[2] ^ DMR_TERMINATOR_WITH_LC_CRC_MASK;
+        lc[10] = parity[1] ^ DMR_TERMINATOR_WITH_LC_CRC_MASK;
+        lc[11] = parity[0] ^ DMR_TERMINATOR_WITH_LC_CRC_MASK;
+    }
+    // sync
+    ::memcpy(payload+13, g_DmrSyncBSData, sizeof(g_DmrSyncBSData));
+    // slot type
+    {
+        // slot type
+        uint8 slottype[3];
+        ::memset(slottype, 0, sizeof(slottype));
+        slottype[0]  = (DMRMMDVM_REFLECTOR_COLOUR << 4) & 0xF0;
+        slottype[0] |= (DMR_DT_TERMINATOR_WITH_LC  << 0) & 0x0FU;
+        CGolay2087::encode(slottype);
+        payload[12U] = (payload[12U] & 0xC0U) | ((slottype[0U] >> 2) & 0x3FU);
+        payload[13U] = (payload[13U] & 0x0FU) | ((slottype[0U] << 6) & 0xC0U) | ((slottype[1U] >> 2) & 0x30U);
+        payload[19U] = (payload[19U] & 0xF0U) | ((slottype[1U] >> 2) & 0x0FU);
+        payload[20U] = (payload[20U] & 0x03U) | ((slottype[1U] << 6) & 0xC0U) | ((slottype[2U] >> 2) & 0x3CU);
+    }
+    // and encode
+    bptc.encode(lc, payload);
+    
+    // and append
+    buffer->Append(payload, sizeof(payload));
+}
+
+void CDmrmmdvmProtocol::ReplaceEMBInBuffer(CBuffer *buffer, uint8 uiDmrPacketId) const
+{
+    // voice packet A ?
+    if ( uiDmrPacketId == 0 )
+    {
+        // sync
+        buffer->ReplaceAt(33, (uint8)(buffer->at(33) | (g_DmrSyncBSVoice[0] & 0x0F)));
+        buffer->ReplaceAt(34, g_DmrSyncBSVoice+1, 5);
+        buffer->ReplaceAt(39, (uint8)(buffer->at(39) | (g_DmrSyncBSVoice[6] & 0xF0)));
+    }
+    // voice packet B,C,D,E ?
+    else if ( (uiDmrPacketId >= 1) && (uiDmrPacketId <= 4 ) )
+    {
+        // EMB LC
+        uint8 emb[2];
+        emb[0]  = (DMRMMDVM_REFLECTOR_COLOUR << 4) & 0xF0;
+        //emb[0] |= PI ? 0x08U : 0x00;
+        //emb[0] |= (LCSS << 1) & 0x06;
+        emb[1]  = 0x00;
+        // encode
+        CQR1676::encode(emb);
+        // and append
+        buffer->ReplaceAt(33, (uint8)((buffer->at(33) & 0xF0) | ((emb[0U] >> 4) & 0x0F)));
+        buffer->ReplaceAt(34, (uint8)((buffer->at(34) & 0x0F) | ((emb[0U] << 4) & 0xF0)));
+        buffer->ReplaceAt(34, (uint8)(buffer->at(34) & 0xF0));
+        buffer->ReplaceAt(35, (uint8)0);
+        buffer->ReplaceAt(36, (uint8)0);
+        buffer->ReplaceAt(37, (uint8)0);
+        buffer->ReplaceAt(38, (uint8)(buffer->at(38) & 0x0F));
+        buffer->ReplaceAt(38, (uint8)((buffer->at(38) & 0xF0) | ((emb[1U] >> 4) & 0x0F)));
+        buffer->ReplaceAt(39, (uint8)((buffer->at(39) & 0x0F) | ((emb[1U] << 4) & 0xF0)));
+    }
+    // voice packet F
+    else
+    {
+        // NULL
+        uint8 emb[2];
+        emb[0]  = (DMRMMDVM_REFLECTOR_COLOUR << 4) & 0xF0;
+        //emb[0] |= PI ? 0x08U : 0x00;
+        //emb[0] |= (LCSS << 1) & 0x06;
+        emb[1]  = 0x00;
+        // encode
+        CQR1676::encode(emb);
+        // and append
+        buffer->ReplaceAt(33, (uint8)((buffer->at(33) & 0xF0) | ((emb[0U] >> 4) & 0x0F)));
+        buffer->ReplaceAt(34, (uint8)((buffer->at(34) & 0x0F) | ((emb[0U] << 4) & 0xF0)));
+        buffer->ReplaceAt(34, (uint8)(buffer->at(34) & 0xF0));
+        buffer->ReplaceAt(35, (uint8)0);
+        buffer->ReplaceAt(36, (uint8)0);
+        buffer->ReplaceAt(37, (uint8)0);
+        buffer->ReplaceAt(38, (uint8)(buffer->at(38) & 0x0F));
+        buffer->ReplaceAt(38, (uint8)((buffer->at(38) & 0xF0) | ((emb[1U] >> 4) & 0x0F)));
+        buffer->ReplaceAt(39, (uint8)((buffer->at(39) & 0x0F) | ((emb[1U] << 4) & 0xF0)));
+    }
+}
+
+void CDmrmmdvmProtocol::AppendDmrIdToBuffer(CBuffer *buffer, uint32 id) const
+{
+    buffer->Append((uint8)LOBYTE(HIWORD(id)));
+    buffer->Append((uint8)HIBYTE(LOWORD(id)));
+    buffer->Append((uint8)LOBYTE(LOWORD(id)));
+}
+
+void CDmrmmdvmProtocol::AppendDmrRptrIdToBuffer(CBuffer *buffer, uint32 id) const
+{
+    buffer->Append((uint8)HIBYTE(HIWORD(id)));
+    buffer->Append((uint8)LOBYTE(HIWORD(id)));
+    buffer->Append((uint8)HIBYTE(LOWORD(id)));
+    buffer->Append((uint8)LOBYTE(LOWORD(id)));
+}
diff --git a/src/cdmrmmdvmprotocol.h b/src/cdmrmmdvmprotocol.h
new file mode 100644
index 0000000..2f221c5
--- /dev/null
+++ b/src/cdmrmmdvmprotocol.h
@@ -0,0 +1,138 @@
+//
+//  cdmrmmdvmprotocol.h
+//  xlxd
+//
+//  Created by Jean-Luc Deltombe (LX3JL) on 04/03/2017.
+//  Copyright © 2015 Jean-Luc Deltombe (LX3JL). All rights reserved.
+//
+// ----------------------------------------------------------------------------
+//    This file is part of xlxd.
+//
+//    xlxd is free software: you can redistribute it and/or modify
+//    it under the terms of the GNU General Public License as published by
+//    the Free Software Foundation, either version 3 of the License, or
+//    (at your option) any later version.
+//
+//    xlxd is distributed in the hope that it will be useful,
+//    but WITHOUT ANY WARRANTY; without even the implied warranty of
+//    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+//    GNU General Public License for more details.
+//
+//    You should have received a copy of the GNU General Public License
+//    along with Foobar.  If not, see <http://www.gnu.org/licenses/>.
+// ----------------------------------------------------------------------------
+
+#ifndef cdmrmmdvmprotocol_h
+#define cdmrmmdvmprotocol_h
+
+#include "ctimepoint.h"
+#include "cprotocol.h"
+#include "cdvheaderpacket.h"
+#include "cdvframepacket.h"
+#include "cdvlastframepacket.h"
+
+////////////////////////////////////////////////////////////////////////////////////////
+// define
+
+// frame type
+#define DMRMMDVM_FRAMETYPE_VOICE     0
+#define DMRMMDVM_FRAMETYPE_VOICESYNC 1
+#define DMRMMDVM_FRAMETYPE_DATASYNC  2
+
+// slot type
+#define MMDVM_SLOTTYPE_HEADER        1
+#define MMDVM_SLOTTYPE_TERMINATOR    2
+
+////////////////////////////////////////////////////////////////////////////////////////
+// class
+
+class CDmrmmdvmStreamCacheItem
+{
+public:
+    CDmrmmdvmStreamCacheItem()     {}
+    ~CDmrmmdvmStreamCacheItem()    {}
+    
+    CDvHeaderPacket m_dvHeader;
+    CDvFramePacket  m_dvFrame0;
+    CDvFramePacket  m_dvFrame1;
+    
+    uint8  m_uiSeqId;
+};
+
+
+class CDmrmmdvmProtocol : public CProtocol
+{
+public:
+    // constructor
+    CDmrmmdvmProtocol() {};
+    
+    // destructor
+    virtual ~CDmrmmdvmProtocol() {};
+    
+    // initialization
+    bool Init(void);
+    
+    // task
+    void Task(void);
+    
+protected:
+    // queue helper
+    void HandleQueue(void);
+    
+    // keepalive helpers
+    void HandleKeepalives(void);
+    
+    // stream helpers
+    bool OnDvHeaderPacketIn(CDvHeaderPacket *, const CIp &, uint8, uint8);
+    
+    // packet decoding helpers
+    bool IsValidConnectPacket(const CBuffer &, CCallsign *);
+    bool IsValidAuthenticationPacket(const CBuffer &, CCallsign *);
+    bool IsValidDisconnectPacket(const CBuffer &, CCallsign *);
+    bool IsValidConfigPacket(const CBuffer &, CCallsign *);
+    bool IsValidOptionPacket(const CBuffer &, CCallsign *);
+    bool IsValidKeepAlivePacket(const CBuffer &, CCallsign *);
+    bool IsValidRssiPacket(const CBuffer &, CCallsign *, int *);
+    bool IsValidDvHeaderPacket(const CBuffer &, CDvHeaderPacket **, uint8 *, uint8 *);
+    bool IsValidDvFramePacket(const CBuffer &, CDvFramePacket **);
+    bool IsValidDvLastFramePacket(const CBuffer &, CDvLastFramePacket **);
+    
+    // packet encoding helpers
+    void EncodeKeepAlivePacket(CBuffer *, CClient *);
+    void EncodeAckPacket(CBuffer *, const CCallsign &);
+    void EncodeConnectAckPacket(CBuffer *, const CCallsign &, uint32);
+    void EncodeNackPacket(CBuffer *, const CCallsign &);
+    void EncodeClosePacket(CBuffer *, CClient *);
+    bool EncodeDvHeaderPacket(const CDvHeaderPacket &, uint8, CBuffer *) const;
+    void EncodeDvPacket(const CDvHeaderPacket &, const CDvFramePacket &, const CDvFramePacket &, const CDvFramePacket &, uint8, CBuffer *) const;
+    void EncodeDvLastPacket(const CDvHeaderPacket &, uint8, CBuffer *) const;
+  
+    // dmr DstId to Module helper
+    char DmrDstIdToModule(uint32) const;
+    uint32 ModuleToDmrDestId(char) const;
+    
+    // Buffer & LC helpers
+    void AppendVoiceLCToBuffer(CBuffer *, uint32) const;
+    void AppendTerminatorLCToBuffer(CBuffer *, uint32) const;
+    void ReplaceEMBInBuffer(CBuffer *, uint8) const;
+    void AppendDmrIdToBuffer(CBuffer *, uint32) const;
+    void AppendDmrRptrIdToBuffer(CBuffer *, uint32) const;
+
+
+protected:
+    // for keep alive
+    CTimePoint          m_LastKeepaliveTime;
+    
+    // for stream id
+    uint16              m_uiStreamId;
+    
+    // for queue header caches
+    std::array<CDmrmmdvmStreamCacheItem, NB_OF_MODULES>    m_StreamsCache;
+    
+    // for authentication
+    uint32              m_uiAuthSeed;
+};
+
+////////////////////////////////////////////////////////////////////////////////////////
+
+#endif /* cdmrmmdvmprotocol_h */
diff --git a/src/cdmrplusclient.cpp b/src/cdmrplusclient.cpp
new file mode 100644
index 0000000..7f457d1
--- /dev/null
+++ b/src/cdmrplusclient.cpp
@@ -0,0 +1,52 @@
+//
+//  cdmrplusclient.cpp
+//  xlxd
+//
+//  Created by Jean-Luc Deltombe (LX3JL on 10/01/2016.
+//  Copyright © 2015 Jean-Luc Deltombe (LX3JL). All rights reserved.
+//
+// ----------------------------------------------------------------------------
+//    This file is part of xlxd.
+//
+//    xlxd is free software: you can redistribute it and/or modify
+//    it under the terms of the GNU General Public License as published by
+//    the Free Software Foundation, either version 3 of the License, or
+//    (at your option) any later version.
+//
+//    xlxd is distributed in the hope that it will be useful,
+//    but WITHOUT ANY WARRANTY; without even the implied warranty of
+//    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+//    GNU General Public License for more details.
+//
+//    You should have received a copy of the GNU General Public License
+//    along with Foobar.  If not, see <http://www.gnu.org/licenses/>.
+// ----------------------------------------------------------------------------
+
+#include "main.h"
+#include "cdmrplusclient.h"
+
+
+////////////////////////////////////////////////////////////////////////////////////////
+// constructors
+
+CDmrplusClient::CDmrplusClient()
+{
+}
+
+CDmrplusClient::CDmrplusClient(const CCallsign &callsign, const CIp &ip, char reflectorModule)
+: CClient(callsign, ip, reflectorModule)
+{
+}
+
+CDmrplusClient::CDmrplusClient(const CDmrplusClient &client)
+: CClient(client)
+{
+}
+
+////////////////////////////////////////////////////////////////////////////////////////
+// status
+
+bool CDmrplusClient::IsAlive(void) const
+{
+    return (m_LastKeepaliveTime.DurationSinceNow() < DMRPLUS_KEEPALIVE_TIMEOUT);
+}
diff --git a/src/cdmrplusclient.h b/src/cdmrplusclient.h
new file mode 100644
index 0000000..628b2a4
--- /dev/null
+++ b/src/cdmrplusclient.h
@@ -0,0 +1,59 @@
+//
+//  cdmrplusclient.h
+//  xlxd
+//
+//  Created by Jean-Luc Deltombe (LX3JL) on 10/01/2016.
+//  Copyright © 2015 Jean-Luc Deltombe (LX3JL). All rights reserved.
+//
+// ----------------------------------------------------------------------------
+//    This file is part of xlxd.
+//
+//    xlxd is free software: you can redistribute it and/or modify
+//    it under the terms of the GNU General Public License as published by
+//    the Free Software Foundation, either version 3 of the License, or
+//    (at your option) any later version.
+//
+//    xlxd is distributed in the hope that it will be useful,
+//    but WITHOUT ANY WARRANTY; without even the implied warranty of
+//    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+//    GNU General Public License for more details.
+//
+//    You should have received a copy of the GNU General Public License
+//    along with Foobar.  If not, see <http://www.gnu.org/licenses/>.
+// ----------------------------------------------------------------------------
+
+#ifndef cdmrplusclient_h
+#define cdmrplusclient_h
+
+#include "cclient.h"
+
+////////////////////////////////////////////////////////////////////////////////////////
+// define
+
+
+////////////////////////////////////////////////////////////////////////////////////////
+// class
+
+class CDmrplusClient : public CClient
+{
+public:
+    // constructors
+    CDmrplusClient();
+    CDmrplusClient(const CCallsign &, const CIp &, char = ' ');
+    CDmrplusClient(const CDmrplusClient &);
+    
+    // destructor
+    virtual ~CDmrplusClient() {};
+    
+    // identity
+    int GetProtocol(void) const                 { return PROTOCOL_DMRPLUS; }
+    const char *GetProtocolName(void) const     { return "DMRplus"; }
+    int GetCodec(void) const                    { return CODEC_AMBE2PLUS; }
+    bool IsNode(void) const                     { return true; }
+    
+    // status
+    bool IsAlive(void) const;
+};
+
+////////////////////////////////////////////////////////////////////////////////////////
+#endif /* cdmrplusclient_h */
diff --git a/src/cdmrplusprotocol.cpp b/src/cdmrplusprotocol.cpp
new file mode 100644
index 0000000..fb4f270
--- /dev/null
+++ b/src/cdmrplusprotocol.cpp
@@ -0,0 +1,846 @@
+//
+//  cdmrplusprotocol.cpp
+//  xlxd
+//
+//  Created by Jean-Luc Deltombe (LX3JL) on 10/01/2016.
+//  Copyright © 2015 Jean-Luc Deltombe (LX3JL). All rights reserved.
+//
+// ----------------------------------------------------------------------------
+//    This file is part of xlxd.
+//
+//    xlxd is free software: you can redistribute it and/or modify
+//    it under the terms of the GNU General Public License as published by
+//    the Free Software Foundation, either version 3 of the License, or
+//    (at your option) any later version.
+//
+//    xlxd is distributed in the hope that it will be useful,
+//    but WITHOUT ANY WARRANTY; without even the implied warranty of
+//    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+//    GNU General Public License for more details.
+//
+//    You should have received a copy of the GNU General Public License
+//    along with Foobar.  If not, see <http://www.gnu.org/licenses/>.
+// ----------------------------------------------------------------------------
+
+#include "main.h"
+#include <string.h>
+#include "cdmrplusclient.h"
+#include "cdmrplusprotocol.h"
+#include "creflector.h"
+#include "cgatekeeper.h"
+#include "cdmriddir.h"
+#include "cbptc19696.h"
+#include "crs129.h"
+#include "cgolay2087.h"
+#include "cqr1676.h"
+
+////////////////////////////////////////////////////////////////////////////////////////
+// constants
+
+static uint8 g_DmrSyncBSVoice[]    = { 0x07,0x55,0xFD,0x7D,0xF7,0x5F,0x70 };
+static uint8 g_DmrSyncBSData[]     = { 0x0D,0xFF,0x57,0xD7,0x5D,0xF5,0xD0 };
+static uint8 g_DmrSyncMSVoice[]    = { 0x07,0xF7,0xD5,0xDD,0x57,0xDF,0xD0 };
+static uint8 g_DmrSyncMSData[]     = { 0x0D,0x5D,0x7F,0x77,0xFD,0x75,0x70 };
+
+
+////////////////////////////////////////////////////////////////////////////////////////
+// operation
+
+bool CDmrplusProtocol::Init(void)
+{
+    bool ok;
+    
+    // base class
+    ok = CProtocol::Init();
+    
+    // update the reflector callsign
+    //m_ReflectorCallsign.PatchCallsign(0, (const uint8 *)"DMR", 3);
+    
+    // create our socket
+    ok &= m_Socket.Open(DMRPLUS_PORT);
+    
+    // update time
+    m_LastKeepaliveTime.Now();
+    
+    // random number generator
+    time_t t;
+    ::srand((unsigned) time(&t));
+    
+    // done
+    return ok;
+}
+
+
+
+////////////////////////////////////////////////////////////////////////////////////////
+// task
+
+void CDmrplusProtocol::Task(void)
+{
+    CBuffer             Buffer;
+    CIp                 Ip;
+    CCallsign           Callsign;
+    char                ToLinkModule;
+    CDvHeaderPacket     *Header;
+    CDvFramePacket      *Frames[3];
+    
+    // handle incoming packets
+    if ( m_Socket.Receive(&Buffer, &Ip, 20) != -1 )
+    {
+       // crack the packet
+        if ( IsValidDvFramePacket(Ip, Buffer, Frames) )
+        {
+            //std::cout << "DMRplus DV frame" << std::endl;
+            //Buffer.DebugDump(g_Reflector.m_DebugFile);
+
+            for ( int i = 0; i < 3; i++ )
+            {
+                OnDvFramePacketIn(Frames[i], &Ip);
+                /*if ( !Frames[i]->IsLastPacket() )
+                {
+                    //std::cout << "DMRplus DV frame" << std::endl;
+                    OnDvFramePacketIn(Frames[i], &Ip);
+                }
+                else
+                {
+                    //std::cout << "DMRplus DV last frame" << std::endl;
+                    OnDvLastFramePacketIn((CDvLastFramePacket *)Frames[i], &Ip);
+                }*/
+            }
+        }
+        else if ( IsValidDvHeaderPacket(Ip, Buffer, &Header) )
+        {
+            //std::cout << "DMRplus DV header:"  << std::endl;
+            //std::cout << "DMRplus DV header:"  << std::endl <<  *Header << std::endl;
+            //Buffer.DebugDump(g_Reflector.m_DebugFile);
+            
+            // callsign muted?
+            if ( g_GateKeeper.MayTransmit(Header->GetMyCallsign(), Ip, PROTOCOL_DMRPLUS) )
+            {
+                // handle it
+                OnDvHeaderPacketIn(Header, Ip);
+            }
+            else
+            {
+                delete Header;
+            }
+        }
+        else if ( IsValidConnectPacket(Buffer, &Callsign, &ToLinkModule) )
+        {
+            //std::cout << "DMRplus keepalive/connect packet for module " << ToLinkModule << " from " << Callsign << " at " << Ip << std::endl;
+            
+            // callsign authorized?
+            if ( g_GateKeeper.MayLink(Callsign, Ip, PROTOCOL_DMRPLUS) )
+            {
+                // acknowledge the request
+                EncodeConnectAckPacket(&Buffer);
+                m_Socket.Send(Buffer, Ip);
+                
+                // add client if needed
+                CClients *clients = g_Reflector.GetClients();
+                CClient *client = clients->FindClient(Callsign, Ip, PROTOCOL_DMRPLUS);
+                // client already connected ?
+                if ( client == NULL )
+                {
+                    std::cout << "DMRplus connect packet for module " << ToLinkModule << " from " << Callsign << " at " << Ip << std::endl;
+                    
+                    // create the client
+                    CDmrplusClient *newclient = new CDmrplusClient(Callsign, Ip, ToLinkModule);
+                    
+                    // and append
+                    clients->AddClient(newclient);
+                }
+                else
+                {
+                    client->Alive();
+                }
+                // and done
+                g_Reflector.ReleaseClients();
+            }
+            else
+            {
+                // deny the request
+                EncodeConnectNackPacket(&Buffer);
+                m_Socket.Send(Buffer, Ip);
+            }
+            
+        }
+        else if ( IsValidDisconnectPacket(Buffer, &Callsign, &ToLinkModule) )
+        {
+            std::cout << "DMRplus disconnect packet for module " << ToLinkModule << " from " << Callsign << " at " << Ip << std::endl;
+            
+            // find client & remove it
+            CClients *clients = g_Reflector.GetClients();
+            CClient *client = clients->FindClient(Ip, PROTOCOL_DMRPLUS);
+            if ( client != NULL )
+            {
+                clients->RemoveClient(client);
+            }
+            g_Reflector.ReleaseClients();
+        }
+        else
+        {
+            //std::cout << "DMRPlus packet (" << Buffer.size() << ")"  <<  " at " << Ip << std::endl;
+        }
+    }
+    
+    // handle end of streaming timeout
+    CheckStreamsTimeout();
+    
+    // handle queue from reflector
+    HandleQueue();
+    
+    
+    // keep client alive
+    if ( m_LastKeepaliveTime.DurationSinceNow() > DMRPLUS_KEEPALIVE_PERIOD )
+    {
+        //
+        HandleKeepalives();
+        
+        // update time
+        m_LastKeepaliveTime.Now();
+    }
+}
+
+////////////////////////////////////////////////////////////////////////////////////////
+// streams helpers
+
+bool CDmrplusProtocol::OnDvHeaderPacketIn(CDvHeaderPacket *Header, const CIp &Ip)
+{
+    bool newstream = false;
+    
+    // find the stream
+    CPacketStream *stream = GetStream(Header->GetStreamId());
+    if ( stream == NULL )
+    {
+        // no stream open yet, open a new one
+        // find this client
+        CClient *client = g_Reflector.GetClients()->FindClient(Ip, PROTOCOL_DMRPLUS);
+        if ( client != NULL )
+        {
+            // and try to open the stream
+            if ( (stream = g_Reflector.OpenStream(Header, client)) != NULL )
+            {
+                // keep the handle
+                m_Streams.push_back(stream);
+                newstream = true;
+            }
+        }
+        // release
+        g_Reflector.ReleaseClients();
+    }
+    else
+    {
+        // stream already open
+        // skip packet, but tickle the stream
+        stream->Tickle();
+        // and delete packet
+        delete Header;
+    }
+    
+    // update last heard
+    g_Reflector.GetUsers()->Hearing(Header->GetMyCallsign(), Header->GetRpt1Callsign(), Header->GetRpt2Callsign());
+    g_Reflector.ReleaseUsers();
+    
+    // delete header if needed
+    if ( !newstream )
+    {
+        delete Header;
+    }
+    
+    
+    // done
+    return newstream;
+}
+
+////////////////////////////////////////////////////////////////////////////////////////
+// queue helper
+
+void CDmrplusProtocol::HandleQueue(void)
+{
+    
+    m_Queue.Lock();
+    while ( !m_Queue.empty() )
+    {
+        // get the packet
+        CPacket *packet = m_Queue.front();
+        m_Queue.pop();
+        
+        // get our sender's id
+        int iModId = g_Reflector.GetModuleIndex(packet->GetModuleId());
+        
+        // encode
+        CBuffer buffer;
+
+        // check if it's header
+        if ( packet->IsDvHeader() )
+        {
+            // update local stream cache
+            // this relies on queue feeder setting valid module id
+            m_StreamsCache[iModId].m_dvHeader = CDvHeaderPacket((const CDvHeaderPacket &)*packet);
+            m_StreamsCache[iModId].m_uiSeqId = 4;
+            
+            // encode it
+            EncodeDvHeaderPacket((const CDvHeaderPacket &)*packet, &buffer);
+        }
+        else
+        {
+            // update local stream cache or send triplet when needed
+            switch ( packet->GetDmrPacketSubid() )
+            {
+                case 1:
+                    m_StreamsCache[iModId].m_dvFrame0 = CDvFramePacket((const CDvFramePacket &)*packet);
+                    break;
+                case 2:
+                    m_StreamsCache[iModId].m_dvFrame1 = CDvFramePacket((const CDvFramePacket &)*packet);
+                    break;
+                case 3:
+                    EncodeDvPacket(
+                                   m_StreamsCache[iModId].m_dvHeader,
+                                   m_StreamsCache[iModId].m_dvFrame0,
+                                   m_StreamsCache[iModId].m_dvFrame1,
+                                   (const CDvFramePacket &)*packet,
+                                   m_StreamsCache[iModId].m_uiSeqId,
+                                   &buffer);
+                    m_StreamsCache[iModId].m_uiSeqId = GetNextSeqId(m_StreamsCache[iModId].m_uiSeqId);
+                    break;
+                default:
+                    break;
+            }
+            
+        }
+        
+        // send it
+        if ( buffer.size() > 0 )
+        {
+            // and push it to all our clients linked to the module and who are not streaming in
+            CClients *clients = g_Reflector.GetClients();
+            int index = -1;
+            CClient *client = NULL;
+            while ( (client = clients->FindNextClient(PROTOCOL_DMRPLUS, &index)) != NULL )
+            {
+                // is this client busy ?
+                if ( !client->IsAMaster() && (client->GetReflectorModule() == packet->GetModuleId()) )
+                {
+                    // no, send the packet
+                    m_Socket.Send(buffer, client->GetIp());
+                }
+            }
+            g_Reflector.ReleaseClients();
+            
+            // debug
+            //buffer.DebugDump(g_Reflector.m_DebugFile);
+        }
+
+        // done
+        delete packet;
+    }
+    m_Queue.Unlock();
+}
+
+void CDmrplusProtocol::SendBufferToClients(const CBuffer &buffer, uint8 module)
+{
+    if ( buffer.size() > 0 )
+    {
+        // and push it to all our clients linked to the module and who are not streaming in
+        CClients *clients = g_Reflector.GetClients();
+        int index = -1;
+        CClient *client = NULL;
+        while ( (client = clients->FindNextClient(PROTOCOL_DMRPLUS, &index)) != NULL )
+        {
+            // is this client busy ?
+            if ( !client->IsAMaster() && (client->GetReflectorModule() == module) )
+            {
+                // no, send the packet
+                m_Socket.Send(buffer, client->GetIp());
+            }
+        }
+        g_Reflector.ReleaseClients();
+        
+        // debug
+        //buffer.DebugDump(g_Reflector.m_DebugFile);
+    }
+}
+
+
+////////////////////////////////////////////////////////////////////////////////////////
+// keepalive helpers
+
+void CDmrplusProtocol::HandleKeepalives(void)
+{
+    // DMRplus protocol keepalive request is client tasks
+    // here, just check that all clients are still alive
+    // and disconnect them if not
+    
+    // iterate on clients
+    CClients *clients = g_Reflector.GetClients();
+    int index = -1;
+    CClient *client = NULL;
+    while ( (client = clients->FindNextClient(PROTOCOL_DMRPLUS, &index)) != NULL )
+    {
+        // is this client busy ?
+        if ( client->IsAMaster() )
+        {
+            // yes, just tickle it
+            client->Alive();
+        }
+        // check it's still with us
+        else if ( !client->IsAlive() )
+        {
+            // no, disconnect
+            //CBuffer disconnect;
+            //EncodeDisconnectPacket(&disconnect, client);
+            //m_Socket.Send(disconnect, client->GetIp());
+            
+            // remove it
+            std::cout << "DMRplus client " << client->GetCallsign() << " keepalive timeout" << std::endl;
+            clients->RemoveClient(client);
+        }
+        
+    }
+    g_Reflector.ReleaseClients();
+}
+
+////////////////////////////////////////////////////////////////////////////////////////
+// packet decoding helpers
+
+bool CDmrplusProtocol::IsValidConnectPacket(const CBuffer &Buffer, CCallsign *callsign, char *reflectormodule)
+{
+    bool valid = false;
+    if ( Buffer.size() == 31 )
+    {
+        char sz[9];
+        ::memcpy(sz, Buffer.data(), 8);
+        sz[8] = 0;
+        uint32 dmrid = atoi(sz);
+        callsign->SetDmrid(dmrid, true);
+        callsign->SetModule('B');
+        ::memcpy(sz, &Buffer.data()[8], 4);
+        sz[4] = 0;
+        *reflectormodule = DmrDstIdToModule(atoi(sz));
+        valid = (callsign->IsValid() && (std::isupper(*reflectormodule) || (*reflectormodule == ' ')) );
+        if ( !valid)
+        {
+            std::cout << "DMRplus connect packet from unrecognized id " << (int)callsign->GetDmrid()  << std::endl;
+        }
+    }
+    return valid;
+}
+
+bool CDmrplusProtocol::IsValidDisconnectPacket(const CBuffer &Buffer, CCallsign *callsign, char *reflectormodule)
+{
+    bool valid = false;
+    if ( Buffer.size() == 32 )
+    {
+        char sz[9];
+        ::memcpy(sz, Buffer.data(), 8);
+        sz[8] = 0;
+        uint32 dmrid = atoi(sz);
+        callsign->SetDmrid(dmrid, true);
+        callsign->SetModule('B');
+        *reflectormodule = Buffer.data()[11] - '0' + 'A';
+        valid = (callsign->IsValid() && std::isupper(*reflectormodule));
+    }
+    return valid;
+}
+
+bool CDmrplusProtocol::IsValidDvHeaderPacket(const CIp &Ip, const CBuffer &Buffer, CDvHeaderPacket **Header)
+{
+    bool valid = false;
+    *Header = NULL;
+
+    uint8 uiPacketType = Buffer.data()[8];
+    if ( (Buffer.size() == 72)  && ( uiPacketType == 2 ) )
+    {
+        // frame details
+        uint8 uiSlot = (Buffer.data()[16] == 0x22) ? DMR_SLOT2 : DMR_SLOT1;
+        uint8 uiCallType = (Buffer.data()[62] == 1) ? DMR_GROUP_CALL : DMR_PRIVATE_CALL;
+        uint8 uiColourCode = Buffer.data()[20] & 0x0F;
+        if ( (uiSlot == DMRPLUS_REFLECTOR_SLOT) && (uiCallType == DMR_GROUP_CALL) && (uiColourCode == DMRPLUS_REFLECTOR_COLOUR) )
+        {
+            // more frames details
+            //uint8 uiSeqId = Buffer.data()[4];
+            //uint8 uiVoiceSeq = (Buffer.data()[18] & 0x0F) - 7; // aka slot type
+            uint32 uiDstId = *(uint32 *)(&Buffer.data()[64]) & 0x00FFFFFF;
+            uint32 uiSrcId = *(uint32 *)(&Buffer.data()[68]) & 0x00FFFFFF;
+
+            // build DVHeader
+            CCallsign csMY =  CCallsign("", uiSrcId);
+            CCallsign rpt1 = CCallsign("", uiSrcId);
+            rpt1.SetModule('B');
+            CCallsign rpt2 = m_ReflectorCallsign;
+            rpt2.SetModule(DmrDstIdToModule(uiDstId));
+            uint32 uiStreamId = IpToStreamId(Ip);
+            
+            // and packet
+            *Header = new CDvHeaderPacket(uiSrcId, CCallsign("CQCQCQ"), rpt1, rpt2, uiStreamId, 0, 0);
+            valid = (*Header)->IsValid();
+            if ( !valid )
+            {
+                delete *Header;
+                *Header = NULL;
+            }
+        }
+    }
+    // done
+    return valid;
+}
+
+bool CDmrplusProtocol::IsValidDvFramePacket(const CIp &Ip, const CBuffer &Buffer, CDvFramePacket **frames)
+{
+    bool valid = false;
+    frames[0] = NULL;
+    frames[1] = NULL;
+    frames[2] = NULL;
+    
+    uint8 uiPacketType = Buffer.data()[8];
+    if ( (Buffer.size() == 72)  && ((uiPacketType == 1) || (uiPacketType == 3)) )
+    {
+        // frame details
+        uint8 uiSlot = (Buffer.data()[16] == 0x22) ? DMR_SLOT2 : DMR_SLOT1;
+        uint8 uiCallType = (Buffer.data()[62] == 1) ? DMR_GROUP_CALL : DMR_PRIVATE_CALL;
+        uint8 uiColourCode = Buffer.data()[20] & 0x0F;
+        if ( (uiSlot == DMRPLUS_REFLECTOR_SLOT) && (uiCallType == DMR_GROUP_CALL) && (uiColourCode == DMRPLUS_REFLECTOR_COLOUR) )
+        {
+            // more frames details
+            //uint8 uiSeqId = Buffer.data()[4];
+            uint8 uiVoiceSeq = (Buffer.data()[18] & 0x0F) - 7; // aka slot type
+            //uint32 uiDstId = *(uint32 *)(&Buffer.data()[64]) & 0x00FFFFFF;
+            //uint32 uiSrcId = *(uint32 *)(&Buffer.data()[68]) & 0x00FFFFFF;
+        
+            // crack payload
+            uint8 dmrframe[33];
+            uint8 dmr3ambe[27];
+            uint8 dmrambe[9];
+            uint8 dmrsync[7];
+            // get the 33 bytes ambe
+            memcpy(dmrframe, &(Buffer.data()[26]), 33);
+            // handle endianess
+            SwapEndianess(dmrframe, sizeof(dmrframe));
+            // extract the 3 ambe frames
+            memcpy(dmr3ambe, dmrframe, 14);
+            dmr3ambe[13] &= 0xF0;
+            dmr3ambe[13] |= (dmrframe[19] & 0x0F);
+            memcpy(&dmr3ambe[14], &dmrframe[20], 14);
+            // extract sync
+            dmrsync[0] = dmrframe[13] & 0x0F;
+            ::memcpy(&dmrsync[1], &dmrframe[14], 5);
+            dmrsync[6] = dmrframe[19] & 0xF0;
+            
+            // and create 3 dv frames
+            uint32 uiStreamId = IpToStreamId(Ip);
+            // frame1
+            memcpy(dmrambe, &dmr3ambe[0], 9);
+            frames[0] = new CDvFramePacket(dmrambe, dmrsync, uiStreamId, uiVoiceSeq, 1);
+            
+            // frame2
+            memcpy(dmrambe, &dmr3ambe[9], 9);
+            frames[1] = new CDvFramePacket(dmrambe, dmrsync, uiStreamId, uiVoiceSeq, 2);
+            
+            // frame3
+            memcpy(dmrambe, &dmr3ambe[18], 9);
+            if ( uiPacketType == 3 )
+            {
+                frames[2] = new CDvLastFramePacket(dmrambe, dmrsync, uiStreamId, uiVoiceSeq, 3);
+            }
+            else
+            {
+                frames[2] = new CDvFramePacket(dmrambe, dmrsync, uiStreamId, uiVoiceSeq, 3);
+            }
+            
+            // check
+            valid = true;
+        }
+    }
+    
+    // done
+    return valid;
+}
+
+
+////////////////////////////////////////////////////////////////////////////////////////
+// packet encoding helpers
+
+void CDmrplusProtocol::EncodeConnectAckPacket(CBuffer *Buffer)
+{
+    uint8 tag[] = { 'A','C','K',' ','O','K',0x0A,0x00 };
+    Buffer->Set(tag, sizeof(tag));
+}
+
+void CDmrplusProtocol::EncodeConnectNackPacket(CBuffer *Buffer)
+{
+    uint8 tag[] = { 'N','A','K',' ','O','K',0x0A,0x00 };
+    Buffer->Set(tag, sizeof(tag));
+}
+
+bool CDmrplusProtocol::EncodeDvHeaderPacket(const CDvHeaderPacket &Packet, CBuffer *Buffer) const
+{
+    uint8 tag[]	= { 0x00,0x00,0x00,0x00,0x02,0x00,0x00,0x00,0x02,
+        0x00,0x05,0x01,0x02,0x00,0x00,0x00  } ;
+    Buffer->Set(tag, sizeof(tag));
+    
+    // uiSeqId
+    //Buffer->ReplaceAt(4, 2);
+    // uiPktType
+    //Buffer->ReplaceAt(8, 2);
+    // uiSlot
+    Buffer->Append((uint16)((DMRPLUS_REFLECTOR_SLOT == DMR_SLOT1) ? 0x1111 : 0x2222));
+    // uiSlotType
+    Buffer->Append((uint16)0xEEEE);
+    // uiColourCode
+    uint8 uiColourCode = DMRPLUS_REFLECTOR_COLOUR | (DMRPLUS_REFLECTOR_COLOUR << 4);
+    Buffer->Append((uint8)uiColourCode);
+    Buffer->Append((uint8)uiColourCode);
+    // uiFrameType
+    Buffer->Append((uint16)0x1111);
+    // reserved
+    Buffer->Append((uint16)0x0000);
+    // payload
+    uint32 uiSrcId = Packet.GetMyCallsign().GetDmrid()  & 0x00FFFFFF;
+    uint32 uiDstId = ModuleToDmrDestId(Packet.GetRpt2Module()) & 0x00FFFFFF;
+    Buffer->Append((uint8)0x00, 34);
+    Buffer->ReplaceAt(36, HIBYTE(HIWORD(uiSrcId)));
+    Buffer->ReplaceAt(38, LOBYTE(HIWORD(uiSrcId)));
+    Buffer->ReplaceAt(40, HIBYTE(LOWORD(uiSrcId)));
+    Buffer->ReplaceAt(42, LOBYTE(LOWORD(uiSrcId)));
+    
+    // reserved
+    Buffer->Append((uint16)0x0000);
+    // uiCallType
+    Buffer->Append((uint8)0x01);
+    // reserved
+    Buffer->Append((uint8)0x00);
+    // uiDstId
+    Buffer->Append(uiDstId);
+    // uiSrcId
+    Buffer->Append(uiSrcId);
+    
+    // done
+    return true;
+}
+
+void CDmrplusProtocol::EncodeDvPacket
+    (const CDvHeaderPacket &Header,
+     const CDvFramePacket &DvFrame0, const CDvFramePacket &DvFrame1, const CDvFramePacket &DvFrame2,
+     uint8 seqid, CBuffer *Buffer) const
+ {
+     
+     uint8 tag[]	= { 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,
+                        0x00,0x05,0x01,0x02,0x00,0x00,0x00  } ;
+     Buffer->Set(tag, sizeof(tag));
+     
+     // uiSeqId
+     Buffer->ReplaceAt(4, seqid);
+     // uiPktType
+     //Buffer->ReplaceAt(8, 1);
+     // uiSlot
+     Buffer->Append((uint16)((DMRPLUS_REFLECTOR_SLOT == DMR_SLOT1) ? 0x1111 : 0x2222));
+     // uiVoiceSeq
+     uint8 uiVoiceSeq = (DvFrame0.GetDmrPacketId() + 7) | ((DvFrame0.GetDmrPacketId() + 7) << 4);
+     Buffer->Append((uint8)uiVoiceSeq);
+     Buffer->Append((uint8)uiVoiceSeq);
+     // uiColourCode
+     uint8 uiColourCode = DMRPLUS_REFLECTOR_COLOUR | (DMRPLUS_REFLECTOR_COLOUR << 4);
+     Buffer->Append((uint8)uiColourCode);
+     Buffer->Append((uint8)uiColourCode);
+     // uiFrameType
+     Buffer->Append((uint16)0x1111);
+     // reserved
+     Buffer->Append((uint16)0x0000);
+     
+     // payload
+     uint32 uiSrcId = Header.GetMyCallsign().GetDmrid()  & 0x00FFFFFF;
+     uint32 uiDstId = ModuleToDmrDestId(Header.GetRpt2Module()) & 0x00FFFFFF;
+     // frame0
+     Buffer->ReplaceAt(26, DvFrame0.GetAmbePlus(), 9);
+     // 1/2 frame1
+     Buffer->ReplaceAt(35, DvFrame1.GetAmbePlus(), 5);
+     Buffer->ReplaceAt(39, (uint8)(Buffer->at(39) & 0xF0));
+     // 1/2 frame1
+     Buffer->ReplaceAt(45, DvFrame1.GetAmbePlus()+4, 5);
+     Buffer->ReplaceAt(45, (uint8)(Buffer->at(45) & 0x0F));
+     // frame2
+     Buffer->ReplaceAt(50, DvFrame2.GetAmbePlus(), 9);
+
+     // sync or embedded signaling
+     ReplaceEMBInBuffer(Buffer, DvFrame0.GetDmrPacketId());
+
+     // reserved
+     Buffer->Append((uint16)0x0000);
+     Buffer->Append((uint8)0x00);
+     // uiCallType
+     Buffer->Append((uint8)0x01);
+     // reserved
+     Buffer->Append((uint8)0x00);
+     // uiDstId
+     Buffer->Append(uiDstId);
+     // uiSrcId
+     Buffer->Append(uiSrcId);
+
+     // handle indianess
+     SwapEndianess(&(Buffer->data()[26]), 34);
+}
+
+
+void CDmrplusProtocol::EncodeDvLastPacket
+    (const CDvHeaderPacket &Header,
+     const CDvFramePacket &DvFrame0, const CDvFramePacket &DvFrame1, const CDvFramePacket &DvFrame2,
+     uint8 seqid, CBuffer *Buffer) const
+ {
+     EncodeDvPacket(Header, DvFrame0, DvFrame1, DvFrame2, seqid, Buffer);
+     Buffer->ReplaceAt(8, (uint8)3);
+     Buffer->ReplaceAt(18, (uint16)0x2222);
+ }
+
+void CDmrplusProtocol::SwapEndianess(uint8 *buffer, int len) const
+{
+    for ( int i = 0; i < len; i += 2 )
+    {
+        uint8 t = buffer[i];
+        buffer[i] = buffer[i+1];
+        buffer[i+1] = t;
+    }
+}
+
+
+////////////////////////////////////////////////////////////////////////////////////////
+// SeqId helper
+
+uint8 CDmrplusProtocol::GetNextSeqId(uint8 uiSeqId) const
+{
+    return (uiSeqId + 1) & 0xFF;
+}
+
+////////////////////////////////////////////////////////////////////////////////////////
+// DestId to Module helper
+
+char CDmrplusProtocol::DmrDstIdToModule(uint32 tg) const
+{
+    // is it a 4xxx ?
+    if ( (tg >= 4001) && (tg <= (4000 + NB_OF_MODULES)) )
+    {
+        return ((char)(tg - 4001) + 'A');
+    }
+    return ' ';
+}
+
+uint32 CDmrplusProtocol::ModuleToDmrDestId(char m) const
+{
+    return (uint32)(m - 'A')+4001;
+}
+
+////////////////////////////////////////////////////////////////////////////////////////
+// Buffer & LC helpers
+
+void CDmrplusProtocol::AppendVoiceLCToBuffer(CBuffer *buffer, uint32 uiSrcId) const
+{
+    uint8 payload[34];
+    
+    // fill payload
+    CBPTC19696 bptc;
+    ::memset(payload, 0, sizeof(payload));
+    // LC data
+    uint8 lc[12];
+    {
+        ::memset(lc, 0, sizeof(lc));
+        // uiDstId = TG9
+        lc[5] = 9;
+        // uiSrcId
+        lc[6] = (uint8)LOBYTE(HIWORD(uiSrcId));
+        lc[7] = (uint8)HIBYTE(LOWORD(uiSrcId));
+        lc[8] = (uint8)LOBYTE(LOWORD(uiSrcId));
+        // parity
+        uint8 parity[4];
+        CRS129::encode(lc, 9, parity);
+        lc[9]  = parity[2] ^ DMR_VOICE_LC_HEADER_CRC_MASK;
+        lc[10] = parity[1] ^ DMR_VOICE_LC_HEADER_CRC_MASK;
+        lc[11] = parity[0] ^ DMR_VOICE_LC_HEADER_CRC_MASK;
+    }
+    // sync
+    ::memcpy(payload+13, g_DmrSyncBSData, sizeof(g_DmrSyncBSData));
+    // slot type
+    {
+        // slot type
+        uint8 slottype[3];
+        ::memset(slottype, 0, sizeof(slottype));
+        slottype[0]  = (DMRPLUS_REFLECTOR_COLOUR << 4) & 0xF0;
+        slottype[0] |= (DMR_DT_VOICE_LC_HEADER  << 0) & 0x0FU;
+        CGolay2087::encode(slottype);
+        payload[12U] = (payload[12U] & 0xC0U) | ((slottype[0U] >> 2) & 0x3FU);
+        payload[13U] = (payload[13U] & 0x0FU) | ((slottype[0U] << 6) & 0xC0U) | ((slottype[1U] >> 2) & 0x30U);
+        payload[19U] = (payload[19U] & 0xF0U) | ((slottype[1U] >> 2) & 0x0FU);
+        payload[20U] = (payload[20U] & 0x03U) | ((slottype[1U] << 6) & 0xC0U) | ((slottype[2U] >> 2) & 0x3CU);
+        
+    }
+    // and encode
+    bptc.encode(lc, payload);
+    
+    // and append
+    buffer->Append(payload, sizeof(payload));
+}
+
+void CDmrplusProtocol::ReplaceEMBInBuffer(CBuffer *buffer, uint8 uiDmrPacketId) const
+{
+    // voice packet A ?
+    if ( uiDmrPacketId == 0 )
+    {
+        // sync
+        buffer->ReplaceAt(39, (uint8)(buffer->at(39) | (g_DmrSyncBSVoice[0] & 0x0F)));
+        buffer->ReplaceAt(40, g_DmrSyncBSVoice+1, 5);
+        buffer->ReplaceAt(45, (uint8)(buffer->at(45) | (g_DmrSyncBSVoice[6] & 0xF0)));
+    }
+    // voice packet B,C,D,E ?
+    else if ( (uiDmrPacketId >= 1) && (uiDmrPacketId <= 4 ) )
+    {
+        // EMB LC
+        uint8 emb[2];
+        emb[0]  = (DMRMMDVM_REFLECTOR_COLOUR << 4) & 0xF0;
+        //emb[0] |= PI ? 0x08U : 0x00;
+        //emb[0] |= (LCSS << 1) & 0x06;
+        emb[1]  = 0x00;
+        // encode
+        CQR1676::encode(emb);
+        // and append
+        buffer->ReplaceAt(39, (uint8)((buffer->at(39) & 0xF0) | ((emb[0U] >> 4) & 0x0F)));
+        buffer->ReplaceAt(40, (uint8)((buffer->at(40) & 0x0F) | ((emb[0U] << 4) & 0xF0)));
+        buffer->ReplaceAt(40, (uint8)(buffer->at(40) & 0xF0));
+        buffer->ReplaceAt(41, (uint8)0);
+        buffer->ReplaceAt(42, (uint8)0);
+        buffer->ReplaceAt(43, (uint8)0);
+        buffer->ReplaceAt(44, (uint8)(buffer->at(44) & 0x0F));
+        buffer->ReplaceAt(44, (uint8)((buffer->at(44) & 0xF0) | ((emb[1U] >> 4) & 0x0F)));
+        buffer->ReplaceAt(45, (uint8)((buffer->at(45) & 0x0F) | ((emb[1U] << 4) & 0xF0)));
+    }
+    // voice packet F
+    else
+    {
+        // NULL
+        uint8 emb[2];
+        emb[0]  = (DMRMMDVM_REFLECTOR_COLOUR << 4) & 0xF0;
+        //emb[0] |= PI ? 0x08U : 0x00;
+        //emb[0] |= (LCSS << 1) & 0x06;
+        emb[1]  = 0x00;
+        // encode
+        CQR1676::encode(emb);
+        // and append
+        buffer->ReplaceAt(39, (uint8)((buffer->at(39) & 0xF0) | ((emb[0U] >> 4) & 0x0F)));
+        buffer->ReplaceAt(40, (uint8)((buffer->at(40) & 0x0F) | ((emb[0U] << 4) & 0xF0)));
+        buffer->ReplaceAt(40, (uint8)(buffer->at(40) & 0xF0));
+        buffer->ReplaceAt(41, (uint8)0);
+        buffer->ReplaceAt(42, (uint8)0);
+        buffer->ReplaceAt(43, (uint8)0);
+        buffer->ReplaceAt(44, (uint8)(buffer->at(44) & 0x0F));
+        buffer->ReplaceAt(44, (uint8)((buffer->at(44) & 0xF0) | ((emb[1U] >> 4) & 0x0F)));
+        buffer->ReplaceAt(45, (uint8)((buffer->at(45) & 0x0F) | ((emb[1U] << 4) & 0xF0)));
+    }
+}
+
+////////////////////////////////////////////////////////////////////////////////////////
+// uiStreamId helpers
+
+
+// uiStreamId helpers
+uint32 CDmrplusProtocol::IpToStreamId(const CIp &ip) const
+{
+    return ip.GetAddr() ^ (uint32)(MAKEDWORD(ip.GetPort(), ip.GetPort()));
+}
diff --git a/src/cdmrplusprotocol.h b/src/cdmrplusprotocol.h
new file mode 100644
index 0000000..430f741
--- /dev/null
+++ b/src/cdmrplusprotocol.h
@@ -0,0 +1,121 @@
+//
+//  cdmrplusprotocol.h
+//  xlxd
+//
+//  Created by Jean-Luc Deltombe (LX3JL) on 10/01/2016.
+//  Copyright © 2015 Jean-Luc Deltombe (LX3JL). All rights reserved.
+//
+// ----------------------------------------------------------------------------
+//    This file is part of xlxd.
+//
+//    xlxd is free software: you can redistribute it and/or modify
+//    it under the terms of the GNU General Public License as published by
+//    the Free Software Foundation, either version 3 of the License, or
+//    (at your option) any later version.
+//
+//    xlxd is distributed in the hope that it will be useful,
+//    but WITHOUT ANY WARRANTY; without even the implied warranty of
+//    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+//    GNU General Public License for more details.
+//
+//    You should have received a copy of the GNU General Public License
+//    along with Foobar.  If not, see <http://www.gnu.org/licenses/>.
+// ----------------------------------------------------------------------------
+
+#ifndef cdmrplusprotocol_h
+#define cdmrplusprotocol_h
+
+#include "ctimepoint.h"
+#include "cprotocol.h"
+#include "cdvheaderpacket.h"
+#include "cdvframepacket.h"
+#include "cdvlastframepacket.h"
+
+////////////////////////////////////////////////////////////////////////////////////////
+// define
+
+////////////////////////////////////////////////////////////////////////////////////////
+// class
+
+class CDmrplusStreamCacheItem
+{
+public:
+    CDmrplusStreamCacheItem()     { m_uiSeqId = 0x77; }
+    ~CDmrplusStreamCacheItem()    {}
+    
+    CDvHeaderPacket m_dvHeader;
+    CDvFramePacket  m_dvFrame0;
+    CDvFramePacket  m_dvFrame1;
+    
+    uint8   m_uiSeqId;
+};
+
+
+class CDmrplusProtocol : public CProtocol
+{
+public:
+    // constructor
+    CDmrplusProtocol() {};
+    
+    // destructor
+    virtual ~CDmrplusProtocol() {};
+    
+    // initialization
+    bool Init(void);
+    
+    // task
+    void Task(void);
+    
+protected:
+    // queue helper
+    void HandleQueue(void);
+    void SendBufferToClients(const CBuffer &, uint8);
+    
+    // keepalive helpers
+    void HandleKeepalives(void);
+    
+    // stream helpers
+    bool OnDvHeaderPacketIn(CDvHeaderPacket *, const CIp &);
+    
+    // packet decoding helpers
+    bool IsValidConnectPacket(const CBuffer &, CCallsign *, char *);
+    bool IsValidDisconnectPacket(const CBuffer &, CCallsign *, char *);
+    bool IsValidDvHeaderPacket(const CIp &, const CBuffer &, CDvHeaderPacket **);
+    bool IsValidDvFramePacket(const CIp &, const CBuffer &, CDvFramePacket **);
+    
+    // packet encoding helpers
+    void EncodeConnectAckPacket(CBuffer *);
+    void EncodeConnectNackPacket(CBuffer *);
+    bool EncodeDvHeaderPacket(const CDvHeaderPacket &, CBuffer *) const;
+    void EncodeDvPacket(const CDvHeaderPacket &, const CDvFramePacket &, const CDvFramePacket &, const CDvFramePacket &, uint8, CBuffer *) const;
+    void EncodeDvLastPacket(const CDvHeaderPacket &, const CDvFramePacket &, const CDvFramePacket &, const CDvFramePacket &, uint8, CBuffer *) const;
+    void SwapEndianess(uint8 *, int) const;
+    
+    // dmr SeqId helper
+    uint8 GetNextSeqId(uint8) const;
+    
+    // dmr DstId to Module helper
+    char DmrDstIdToModule(uint32) const;
+    uint32 ModuleToDmrDestId(char) const;
+    
+    // uiStreamId helpers
+    uint32 IpToStreamId(const CIp &) const;
+    
+    // Buffer & LC helpers
+    void AppendVoiceLCToBuffer(CBuffer *, uint32) const;
+    void AppendTerminatorLCToBuffer(CBuffer *, uint32) const;
+    void ReplaceEMBInBuffer(CBuffer *, uint8) const;
+
+    
+protected:
+    // for keep alive
+    CTimePoint          m_LastKeepaliveTime;
+    
+    // for queue header caches
+    std::array<CDmrplusStreamCacheItem, NB_OF_MODULES>    m_StreamsCache;
+};
+
+////////////////////////////////////////////////////////////////////////////////////////
+
+
+#endif /* cdmrplusprotocol_h */
diff --git a/src/cdplusclient.h b/src/cdplusclient.h
index c8ed9e1..f4b165f 100644
--- a/src/cdplusclient.h
+++ b/src/cdplusclient.h
@@ -47,7 +47,8 @@ public:
     
     // identity
     int GetProtocol(void) const                 { return PROTOCOL_DPLUS; }
-    const char *GetProtocolName(void) const     { return "DPlus"; }
+    const char *GetProtocolName(void) const     { return "Dplus"; }
+    int GetCodec(void) const                    { return CODEC_AMBEPLUS; }
     bool IsNode(void) const                     { return true; }
     bool IsDextraDongle(void) const             { return m_bDextraDongle; }
     void SetDextraDongle(void)                  { m_bDextraDongle = true; }
diff --git a/src/cdplusprotocol.cpp b/src/cdplusprotocol.cpp
index 37cfbca..86aa4fb 100644
--- a/src/cdplusprotocol.cpp
+++ b/src/cdplusprotocol.cpp
@@ -30,17 +30,6 @@
 #include "cgatekeeper.h"
 
 
-////////////////////////////////////////////////////////////////////////////////////////
-// constructor
-
-CDplusProtocol::CDplusProtocol()
-{
-    for ( int i = 0; i < m_iSeqCounters.size(); i++ )
-    {
-        m_iSeqCounters[i] = 0;
-    }
-}
-
 ////////////////////////////////////////////////////////////////////////////////////////
 // operation
 
@@ -53,7 +42,6 @@ bool CDplusProtocol::Init(void)
     
     // update the reflector callsign
     m_ReflectorCallsign.PatchCallsign(0, (const uint8 *)"REF", 3);
-    //m_ReflectorCallsign.PatchCallsign(0, (const uint8 *)"XRF", 3);
     
     // create our socket
     ok &= m_Socket.Open(DPLUS_PORT);
@@ -250,7 +238,7 @@ bool CDplusProtocol::OnDvHeaderPacketIn(CDvHeaderPacket *Header, const CIp &Ip)
             // update last heard
             g_Reflector.GetUsers()->Hearing(Header->GetMyCallsign(), via, Header->GetRpt2Callsign());
             g_Reflector.ReleaseUsers();
-
+            
             // delete header if needed
             if ( !newstream )
             {
@@ -294,8 +282,8 @@ void CDplusProtocol::HandleQueue(void)
         if ( packet->IsDvHeader() )
         {
             // this relies on queue feeder setting valid module id
-            m_DvHeadersCache[iModId] = CDvHeaderPacket((const CDvHeaderPacket &)*packet);
-            m_iSeqCounters[iModId] = 0;
+            m_StreamsCache[iModId].m_dvHeader = CDvHeaderPacket((const CDvHeaderPacket &)*packet);
+            m_StreamsCache[iModId].m_iSeqCounter = 0;
         }
 
         // encode it
@@ -327,11 +315,11 @@ void CDplusProtocol::HandleQueue(void)
                          m_Socket.Send(buffer, client->GetIp());
 
                         // is it time to insert a DVheader copy ?
-                        if ( (m_iSeqCounters[iModId]++ % 21) == 20 )
+                        if ( (m_StreamsCache[iModId].m_iSeqCounter++ % 21) == 20 )
                         {
                             // yes, clone it
-                            CDvHeaderPacket packet2(m_DvHeadersCache[iModId]);
-                            // and send it 
+                            CDvHeaderPacket packet2(m_StreamsCache[iModId].m_dvHeader);
+                            // and send it
                             SendDvHeader(&packet2, (CDplusClient *)client);
                         }
                     }
diff --git a/src/cdplusprotocol.h b/src/cdplusprotocol.h
index 4a0b2f2..80bf1be 100644
--- a/src/cdplusprotocol.h
+++ b/src/cdplusprotocol.h
@@ -39,11 +39,21 @@
 
 class CDplusClient;
 
+class CDPlusStreamCacheItem
+{
+public:
+    CDPlusStreamCacheItem()     { m_iSeqCounter = 0; }
+    ~CDPlusStreamCacheItem()    {}
+    
+    CDvHeaderPacket m_dvHeader;
+    uint8           m_iSeqCounter;
+};
+
 class CDplusProtocol : public CProtocol
 {
 public:
     // constructor
-    CDplusProtocol();
+    CDplusProtocol() {};
     
     // destructor
     virtual ~CDplusProtocol() {};
@@ -89,8 +99,7 @@ protected:
     CTimePoint          m_LastKeepaliveTime;
     
     // for queue header caches
-    std::array<CDvHeaderPacket, NB_OF_MODULES>  m_DvHeadersCache;
-    std::array<uint32, NB_OF_MODULES>           m_iSeqCounters;
+    std::array<CDPlusStreamCacheItem, NB_OF_MODULES>    m_StreamsCache;
 };
 
 ////////////////////////////////////////////////////////////////////////////////////////
diff --git a/src/cdvframepacket.cpp b/src/cdvframepacket.cpp
index b037ab7..aa0e7bf 100644
--- a/src/cdvframepacket.cpp
+++ b/src/cdvframepacket.cpp
@@ -35,21 +35,41 @@ CDvFramePacket::CDvFramePacket()
 {
     ::memset(m_uiAmbe, 0, sizeof(m_uiAmbe));
     ::memset(m_uiDvData, 0, sizeof(m_uiDvData));
-}
+    ::memset(m_uiAmbePlus, 0, sizeof(m_uiAmbePlus));
+    ::memset(m_uiDvSync, 0, sizeof(m_uiDvSync));
+};
+
+// dstar constructor
 
 CDvFramePacket::CDvFramePacket(const struct dstar_dvframe *dvframe, uint16 sid, uint8 pid)
     : CPacket(sid, pid)
 {
     ::memcpy(m_uiAmbe, dvframe->AMBE, sizeof(m_uiAmbe));
     ::memcpy(m_uiDvData, dvframe->DVDATA, sizeof(m_uiDvData));
+    ::memset(m_uiAmbePlus, 0, sizeof(m_uiAmbePlus));
+    ::memset(m_uiDvSync, 0, sizeof(m_uiDvSync));
 }
 
+// dmr constructor
+
+CDvFramePacket::CDvFramePacket(const uint8 *ambe, const uint8 *sync, uint16 sid, uint8 pid, uint8 spid)
+    : CPacket(sid, pid, spid)
+{
+    ::memcpy(m_uiAmbePlus, ambe, sizeof(m_uiAmbePlus));
+    ::memcpy(m_uiDvSync, sync, sizeof(m_uiDvSync));
+    ::memset(m_uiAmbe, 0, sizeof(m_uiAmbe));
+    ::memset(m_uiDvData, 0, sizeof(m_uiDvData));
+}
+
+// copy constructor
 
 CDvFramePacket::CDvFramePacket(const CDvFramePacket &DvFrame)
     : CPacket(DvFrame)
 {
     ::memcpy(m_uiAmbe, DvFrame.m_uiAmbe, sizeof(m_uiAmbe));
     ::memcpy(m_uiDvData, DvFrame.m_uiDvData, sizeof(m_uiDvData));
+    ::memcpy(m_uiAmbePlus, DvFrame.m_uiAmbePlus, sizeof(m_uiAmbePlus));
+    ::memcpy(m_uiDvSync, DvFrame.m_uiDvSync, sizeof(m_uiDvSync));
 }
 
 ////////////////////////////////////////////////////////////////////////////////////////
@@ -60,6 +80,40 @@ CPacket *CDvFramePacket::Duplicate(void) const
     return new CDvFramePacket(*this);
 }
 
+////////////////////////////////////////////////////////////////////////////////////////
+// get
+
+const uint8 *CDvFramePacket::GetAmbe(uint8 uiCodec) const
+{
+    switch (uiCodec)
+    {
+        case CODEC_AMBEPLUS:    return m_uiAmbe;
+        case CODEC_AMBE2PLUS:   return m_uiAmbePlus;
+        default:                return NULL;
+    }
+}
+
+////////////////////////////////////////////////////////////////////////////////////////
+// set
+
+void CDvFramePacket::SetDvData(uint8 *DvData)
+{
+    ::memcpy(m_uiDvData, DvData, sizeof(m_uiDvData));
+}
+
+void CDvFramePacket::SetAmbe(uint8 uiCodec, uint8 *Ambe)
+{
+    switch (uiCodec)
+    {
+        case CODEC_AMBEPLUS:
+            ::memcpy(m_uiAmbe, Ambe, sizeof(m_uiAmbe));
+            break;
+        case CODEC_AMBE2PLUS:
+            ::memcpy(m_uiAmbePlus, Ambe, sizeof(m_uiAmbe));
+            break;
+    }
+}
+
 
 ////////////////////////////////////////////////////////////////////////////////////////
 // operators
@@ -67,5 +121,7 @@ CPacket *CDvFramePacket::Duplicate(void) const
 bool CDvFramePacket::operator ==(const CDvFramePacket &DvFrame) const
 {
     return ( (::memcmp(m_uiAmbe, DvFrame.m_uiAmbe, sizeof(m_uiAmbe)) == 0) &&
-            (::memcmp(m_uiDvData, DvFrame.m_uiDvData, sizeof(m_uiDvData)) == 0) );
+             (::memcmp(m_uiDvData, DvFrame.m_uiDvData, sizeof(m_uiDvData)) == 0) &&
+             (::memcmp(m_uiAmbePlus, DvFrame.m_uiAmbePlus, sizeof(m_uiAmbePlus)) == 0) &&
+             (::memcmp(m_uiDvSync, DvFrame.m_uiDvSync, sizeof(m_uiDvSync)) == 0) );
 }
diff --git a/src/cdvframepacket.h b/src/cdvframepacket.h
index c02ce3a..555770d 100644
--- a/src/cdvframepacket.h
+++ b/src/cdvframepacket.h
@@ -33,6 +33,9 @@
 #define AMBE_SIZE       9
 #define DVDATA_SIZE     3
 
+#define AMBEPLUS_SIZE   9
+#define DVSYNC_SIZE     7
+
 // typedef & structures
 
 struct __attribute__ ((__packed__))dstar_dvframe
@@ -46,10 +49,12 @@ struct __attribute__ ((__packed__))dstar_dvframe
 
 class CDvFramePacket : public CPacket
 {
+friend class CCodecStream;
 public:
     // constructor
     CDvFramePacket();
-    CDvFramePacket(const struct dstar_dvframe *, uint16 = 0, uint8 = 0);
+    CDvFramePacket(const struct dstar_dvframe *, uint16, uint8);
+    CDvFramePacket(const uint8 *, const uint8 *, uint16, uint8, uint8);
     CDvFramePacket(const CDvFramePacket &);
     
     // destructor
@@ -59,19 +64,35 @@ public:
     CPacket *Duplicate(void) const;
     
     // identity
-    bool IsDvFrame(void) const          { return true; }
+    bool IsDvFrame(void) const              { return true; }
+    bool HaveTranscodableAmbe(void) const   { return true; }
     
     // get
-    const uint8 *GetAmbe(void) const    { return m_uiAmbe; }
-    const uint8 *GetDvData(void) const  { return m_uiDvData; }
+    const uint8 *GetAmbe(uint8) const;
+    const uint8 *GetAmbe(void) const        { return m_uiAmbe; }
+    const uint8 *GetAmbePlus(void) const    { return m_uiAmbePlus; }
+    const uint8 *GetDvData(void) const      { return m_uiDvData; }
+    const uint8 *GetDvSync(void) const      { return m_uiDvSync; }
+    
+    // set
+    void SetDvData(uint8 *);
+    void SetAmbe(uint8, uint8 *);
 
     // operators
     bool operator ==(const CDvFramePacket &) const;
+
+protected:
+    // get
+    uint8 *GetAmbeData(void)                { return m_uiAmbe; }
+    uint8 *GetAmbePlusData(void)            { return m_uiAmbePlus; }
     
 protected:
-    // data
+    // data (dstar)
     uint8       m_uiAmbe[AMBE_SIZE];
     uint8       m_uiDvData[DVDATA_SIZE];
+    // data (dmr)
+    uint8       m_uiAmbePlus[AMBEPLUS_SIZE];
+    uint8       m_uiDvSync[DVSYNC_SIZE];
 };
 
 
diff --git a/src/cdvheaderpacket.cpp b/src/cdvheaderpacket.cpp
index f7cf04b..0ee30d0 100644
--- a/src/cdvheaderpacket.cpp
+++ b/src/cdvheaderpacket.cpp
@@ -25,6 +25,7 @@
 #include "main.h"
 #include <string.h>
 #include <cstdio>
+#include "cdmriddir.h"
 #include "cdvheaderpacket.h"
 
 ////////////////////////////////////////////////////////////////////////////////////////
@@ -38,6 +39,8 @@ CDvHeaderPacket::CDvHeaderPacket()
     m_uiCrc = 0;
 }
 
+// dstar constructor
+
 CDvHeaderPacket::CDvHeaderPacket(const struct dstar_header *buffer, uint16 sid, uint8 pid)
     : CPacket(sid, pid)
 {
@@ -52,8 +55,25 @@ CDvHeaderPacket::CDvHeaderPacket(const struct dstar_header *buffer, uint16 sid,
     m_uiCrc = buffer->Crc;
 }
 
+// dmr constructor
+
+CDvHeaderPacket::CDvHeaderPacket(uint32 my, const CCallsign &ur, const CCallsign &rpt1, const CCallsign &rpt2, uint16 sid, uint8 pid, uint8 spid)
+    : CPacket(sid, pid, spid)
+{
+    m_uiFlag1 = 0;
+    m_uiFlag2 = 0;
+    m_uiFlag3 = 0;
+    m_uiCrc = 0;
+    m_csUR = ur;
+    m_csRPT1 = rpt1;
+    m_csRPT2 = rpt2;
+    m_csMY = CCallsign("", my);
+}
+
+// copy constructor
+
 CDvHeaderPacket::CDvHeaderPacket(const CDvHeaderPacket &Header)
-    : CPacket(Header)
+: CPacket(Header)
 {
     m_uiFlag1 = Header.m_uiFlag1;
     m_uiFlag2 = Header.m_uiFlag2;
@@ -65,6 +85,7 @@ CDvHeaderPacket::CDvHeaderPacket(const CDvHeaderPacket &Header)
     m_uiCrc = Header.m_uiCrc;
 }
 
+
 ////////////////////////////////////////////////////////////////////////////////////////
 // virtual duplication
 
@@ -134,4 +155,4 @@ CDvHeaderPacket::operator const char *() const
         
     return m_sz;
 }
-#endif
\ No newline at end of file
+#endif
diff --git a/src/cdvheaderpacket.h b/src/cdvheaderpacket.h
index 26f0d17..28cb771 100644
--- a/src/cdvheaderpacket.h
+++ b/src/cdvheaderpacket.h
@@ -61,7 +61,8 @@ class CDvHeaderPacket : public CPacket
 public:
     // constructor
     CDvHeaderPacket();
-    CDvHeaderPacket(const struct dstar_header *, uint16 = 0, uint8 = 0);
+    CDvHeaderPacket(const struct dstar_header *, uint16, uint8);
+    CDvHeaderPacket(uint32, const CCallsign &, const CCallsign &, const CCallsign &, uint16, uint8, uint8);
     CDvHeaderPacket(const CDvHeaderPacket &);
     
     // destructor
diff --git a/src/cdvlastframepacket.cpp b/src/cdvlastframepacket.cpp
index e37c033..46673f3 100644
--- a/src/cdvlastframepacket.cpp
+++ b/src/cdvlastframepacket.cpp
@@ -33,11 +33,22 @@ CDvLastFramePacket::CDvLastFramePacket()
 {
 }
 
+// dstar constructor
+
 CDvLastFramePacket::CDvLastFramePacket(const struct dstar_dvframe *DvFrame, uint16 sid, uint8 pid)
     : CDvFramePacket(DvFrame, sid, pid)
 {
 }
 
+// dmr constructor
+
+CDvLastFramePacket::CDvLastFramePacket(const uint8 *ambe, const uint8 *sync, uint16 sid, uint8 pid, uint8 spid)
+    : CDvFramePacket(ambe, sync, sid, pid, spid)
+{
+}
+
+// copy constructor
+    
 CDvLastFramePacket::CDvLastFramePacket(const CDvLastFramePacket &DvFrame)
     : CDvFramePacket(DvFrame)
 {
diff --git a/src/cdvlastframepacket.h b/src/cdvlastframepacket.h
index 44622cc..d561fc6 100644
--- a/src/cdvlastframepacket.h
+++ b/src/cdvlastframepacket.h
@@ -40,7 +40,8 @@ class CDvLastFramePacket : public CDvFramePacket
 public:
     // constructor
     CDvLastFramePacket();
-    CDvLastFramePacket(const struct dstar_dvframe *, uint16 = 0, uint8 = 0);
+    CDvLastFramePacket(const struct dstar_dvframe *, uint16, uint8);
+    CDvLastFramePacket(const uint8 *, const uint8 *, uint16, uint8, uint8);
     CDvLastFramePacket(const CDvLastFramePacket &);
     
     // destructor
@@ -50,7 +51,8 @@ public:
     CPacket *Duplicate(void) const;
     
     // identity
-    bool IsLastPacket(void) const      { return true; }
+    bool IsLastPacket(void) const           { return true; }
+    bool HaveTranscodableAmbe(void) const   { return false; }
 };
 
 
diff --git a/src/cgatekeeper.cpp b/src/cgatekeeper.cpp
index 09264ef..fb2d0b0 100644
--- a/src/cgatekeeper.cpp
+++ b/src/cgatekeeper.cpp
@@ -99,6 +99,8 @@ bool CGateKeeper::MayLink(const CCallsign &callsign, const CIp &ip, int protocol
         case PROTOCOL_DEXTRA:
         case PROTOCOL_DPLUS:
         case PROTOCOL_DCS:
+        case PROTOCOL_DMRPLUS:
+        case PROTOCOL_DMRMMDVM:
             // first check is IP & callsigned listed OK
             ok &= IsNodeListedOk(callsign, ip);
             // todo: then apply any protocol specific authorisation for the operation
@@ -137,6 +139,8 @@ bool CGateKeeper::MayTransmit(const CCallsign &callsign, const CIp &ip, int prot
         case PROTOCOL_DEXTRA:
         case PROTOCOL_DPLUS:
         case PROTOCOL_DCS:
+        case PROTOCOL_DMRPLUS:
+        case PROTOCOL_DMRMMDVM:
             // first check is IP & callsigned listed OK
             ok &= IsNodeListedOk(callsign, ip, module);
             // todo: then apply any protocol specific authorisation for the operation
diff --git a/src/cgolay2087.cpp b/src/cgolay2087.cpp
new file mode 100644
index 0000000..7ff6f92
--- /dev/null
+++ b/src/cgolay2087.cpp
@@ -0,0 +1,262 @@
+/*
+ *   Copyright (C) 2015,2016 by Jonathan Naylor G4KLX
+ *
+ *   This program is free software; you can redistribute it and/or modify
+ *   it under the terms of the GNU General Public License as published by
+ *   the Free Software Foundation; either version 2 of the License, or
+ *   (at your option) any later version.
+ *
+ *   This program is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *   GNU General Public License for more details.
+ *
+ *   You should have received a copy of the GNU General Public License
+ *   along with this program; if not, write to the Free Software
+ *   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include "cgolay2087.h"
+
+#include <cstdio>
+#include <cassert>
+
+const unsigned int ENCODING_TABLE_2087[] =
+{0x0000U, 0xB08EU, 0xE093U, 0x501DU, 0x70A9U, 0xC027U, 0x903AU, 0x20B4U, 0x60DCU, 0xD052U, 0x804FU, 0x30C1U,
+    0x1075U, 0xA0FBU, 0xF0E6U, 0x4068U, 0x7036U, 0xC0B8U, 0x90A5U, 0x202BU, 0x009FU, 0xB011U, 0xE00CU, 0x5082U,
+    0x10EAU, 0xA064U, 0xF079U, 0x40F7U, 0x6043U, 0xD0CDU, 0x80D0U, 0x305EU, 0xD06CU, 0x60E2U, 0x30FFU, 0x8071U,
+    0xA0C5U, 0x104BU, 0x4056U, 0xF0D8U, 0xB0B0U, 0x003EU, 0x5023U, 0xE0ADU, 0xC019U, 0x7097U, 0x208AU, 0x9004U,
+    0xA05AU, 0x10D4U, 0x40C9U, 0xF047U, 0xD0F3U, 0x607DU, 0x3060U, 0x80EEU, 0xC086U, 0x7008U, 0x2015U, 0x909BU,
+    0xB02FU, 0x00A1U, 0x50BCU, 0xE032U, 0x90D9U, 0x2057U, 0x704AU, 0xC0C4U, 0xE070U, 0x50FEU, 0x00E3U, 0xB06DU,
+    0xF005U, 0x408BU, 0x1096U, 0xA018U, 0x80ACU, 0x3022U, 0x603FU, 0xD0B1U, 0xE0EFU, 0x5061U, 0x007CU, 0xB0F2U,
+    0x9046U, 0x20C8U, 0x70D5U, 0xC05BU, 0x8033U, 0x30BDU, 0x60A0U, 0xD02EU, 0xF09AU, 0x4014U, 0x1009U, 0xA087U,
+    0x40B5U, 0xF03BU, 0xA026U, 0x10A8U, 0x301CU, 0x8092U, 0xD08FU, 0x6001U, 0x2069U, 0x90E7U, 0xC0FAU, 0x7074U,
+    0x50C0U, 0xE04EU, 0xB053U, 0x00DDU, 0x3083U, 0x800DU, 0xD010U, 0x609EU, 0x402AU, 0xF0A4U, 0xA0B9U, 0x1037U,
+    0x505FU, 0xE0D1U, 0xB0CCU, 0x0042U, 0x20F6U, 0x9078U, 0xC065U, 0x70EBU, 0xA03DU, 0x10B3U, 0x40AEU, 0xF020U,
+    0xD094U, 0x601AU, 0x3007U, 0x8089U, 0xC0E1U, 0x706FU, 0x2072U, 0x90FCU, 0xB048U, 0x00C6U, 0x50DBU, 0xE055U,
+    0xD00BU, 0x6085U, 0x3098U, 0x8016U, 0xA0A2U, 0x102CU, 0x4031U, 0xF0BFU, 0xB0D7U, 0x0059U, 0x5044U, 0xE0CAU,
+    0xC07EU, 0x70F0U, 0x20EDU, 0x9063U, 0x7051U, 0xC0DFU, 0x90C2U, 0x204CU, 0x00F8U, 0xB076U, 0xE06BU, 0x50E5U,
+    0x108DU, 0xA003U, 0xF01EU, 0x4090U, 0x6024U, 0xD0AAU, 0x80B7U, 0x3039U, 0x0067U, 0xB0E9U, 0xE0F4U, 0x507AU,
+    0x70CEU, 0xC040U, 0x905DU, 0x20D3U, 0x60BBU, 0xD035U, 0x8028U, 0x30A6U, 0x1012U, 0xA09CU, 0xF081U, 0x400FU,
+    0x30E4U, 0x806AU, 0xD077U, 0x60F9U, 0x404DU, 0xF0C3U, 0xA0DEU, 0x1050U, 0x5038U, 0xE0B6U, 0xB0ABU, 0x0025U,
+    0x2091U, 0x901FU, 0xC002U, 0x708CU, 0x40D2U, 0xF05CU, 0xA041U, 0x10CFU, 0x307BU, 0x80F5U, 0xD0E8U, 0x6066U,
+    0x200EU, 0x9080U, 0xC09DU, 0x7013U, 0x50A7U, 0xE029U, 0xB034U, 0x00BAU, 0xE088U, 0x5006U, 0x001BU, 0xB095U,
+    0x9021U, 0x20AFU, 0x70B2U, 0xC03CU, 0x8054U, 0x30DAU, 0x60C7U, 0xD049U, 0xF0FDU, 0x4073U, 0x106EU, 0xA0E0U,
+    0x90BEU, 0x2030U, 0x702DU, 0xC0A3U, 0xE017U, 0x5099U, 0x0084U, 0xB00AU, 0xF062U, 0x40ECU, 0x10F1U, 0xA07FU,
+    0x80CBU, 0x3045U, 0x6058U, 0xD0D6U};
+
+const unsigned int DECODING_TABLE_1987[] =
+{0x00000U, 0x00001U, 0x00002U, 0x00003U, 0x00004U, 0x00005U, 0x00006U, 0x00007U, 0x00008U, 0x00009U, 0x0000AU, 0x0000BU, 0x0000CU,
+    0x0000DU, 0x0000EU, 0x24020U, 0x00010U, 0x00011U, 0x00012U, 0x00013U, 0x00014U, 0x00015U, 0x00016U, 0x00017U, 0x00018U, 0x00019U,
+    0x0001AU, 0x0001BU, 0x0001CU, 0x0001DU, 0x48040U, 0x01480U, 0x00020U, 0x00021U, 0x00022U, 0x00023U, 0x00024U, 0x00025U, 0x00026U,
+    0x24008U, 0x00028U, 0x00029U, 0x0002AU, 0x24004U, 0x0002CU, 0x24002U, 0x24001U, 0x24000U, 0x00030U, 0x00031U, 0x00032U, 0x08180U,
+    0x00034U, 0x00C40U, 0x00036U, 0x00C42U, 0x00038U, 0x43000U, 0x0003AU, 0x43002U, 0x02902U, 0x24012U, 0x02900U, 0x24010U, 0x00040U,
+    0x00041U, 0x00042U, 0x00043U, 0x00044U, 0x00045U, 0x00046U, 0x00047U, 0x00048U, 0x00049U, 0x0004AU, 0x02500U, 0x0004CU, 0x0004DU,
+    0x48010U, 0x48011U, 0x00050U, 0x00051U, 0x00052U, 0x21200U, 0x00054U, 0x00C20U, 0x48008U, 0x48009U, 0x00058U, 0x00059U, 0x48004U,
+    0x48005U, 0x48002U, 0x48003U, 0x48000U, 0x48001U, 0x00060U, 0x00061U, 0x00062U, 0x00063U, 0x00064U, 0x00C10U, 0x10300U, 0x0B000U,
+    0x00068U, 0x00069U, 0x01880U, 0x01881U, 0x40181U, 0x40180U, 0x24041U, 0x24040U, 0x00070U, 0x00C04U, 0x00072U, 0x00C06U, 0x00C01U,
+    0x00C00U, 0x00C03U, 0x00C02U, 0x05204U, 0x00C0CU, 0x48024U, 0x48025U, 0x05200U, 0x00C08U, 0x48020U, 0x48021U, 0x00080U, 0x00081U,
+    0x00082U, 0x00083U, 0x00084U, 0x00085U, 0x00086U, 0x00087U, 0x00088U, 0x00089U, 0x0008AU, 0x50200U, 0x0008CU, 0x0A800U, 0x01411U,
+    0x01410U, 0x00090U, 0x00091U, 0x00092U, 0x08120U, 0x00094U, 0x00095U, 0x04A00U, 0x01408U, 0x00098U, 0x00099U, 0x01405U, 0x01404U,
+    0x01403U, 0x01402U, 0x01401U, 0x01400U, 0x000A0U, 0x000A1U, 0x000A2U, 0x08110U, 0x000A4U, 0x000A5U, 0x42400U, 0x42401U, 0x000A8U,
+    0x000A9U, 0x01840U, 0x01841U, 0x40141U, 0x40140U, 0x24081U, 0x24080U, 0x000B0U, 0x08102U, 0x08101U, 0x08100U, 0x000B4U, 0x08106U,
+    0x08105U, 0x08104U, 0x20A01U, 0x20A00U, 0x08109U, 0x08108U, 0x01423U, 0x01422U, 0x01421U, 0x01420U, 0x000C0U, 0x000C1U, 0x000C2U,
+    0x000C3U, 0x000C4U, 0x000C5U, 0x000C6U, 0x000C7U, 0x000C8U, 0x000C9U, 0x01820U, 0x01821U, 0x20600U, 0x40120U, 0x16000U, 0x16001U,
+    0x000D0U, 0x000D1U, 0x42801U, 0x42800U, 0x03100U, 0x18200U, 0x03102U, 0x18202U, 0x000D8U, 0x000D9U, 0x48084U, 0x01444U, 0x48082U,
+    0x01442U, 0x48080U, 0x01440U, 0x000E0U, 0x32000U, 0x01808U, 0x04600U, 0x40109U, 0x40108U, 0x0180CU, 0x4010AU, 0x01802U, 0x40104U,
+    0x01800U, 0x01801U, 0x40101U, 0x40100U, 0x01804U, 0x40102U, 0x0A408U, 0x08142U, 0x08141U, 0x08140U, 0x00C81U, 0x00C80U, 0x00C83U,
+    0x00C82U, 0x0A400U, 0x0A401U, 0x01810U, 0x01811U, 0x40111U, 0x40110U, 0x01814U, 0x40112U, 0x00100U, 0x00101U, 0x00102U, 0x00103U,
+    0x00104U, 0x00105U, 0x00106U, 0x41800U, 0x00108U, 0x00109U, 0x0010AU, 0x02440U, 0x0010CU, 0x0010DU, 0x0010EU, 0x02444U, 0x00110U,
+    0x00111U, 0x00112U, 0x080A0U, 0x00114U, 0x00115U, 0x00116U, 0x080A4U, 0x00118U, 0x00119U, 0x15000U, 0x15001U, 0x02822U, 0x02823U,
+    0x02820U, 0x02821U, 0x00120U, 0x00121U, 0x00122U, 0x08090U, 0x00124U, 0x00125U, 0x10240U, 0x10241U, 0x00128U, 0x00129U, 0x0012AU,
+    0x24104U, 0x09400U, 0x400C0U, 0x02810U, 0x24100U, 0x00130U, 0x08082U, 0x08081U, 0x08080U, 0x31001U, 0x31000U, 0x02808U, 0x08084U,
+    0x02806U, 0x0808AU, 0x02804U, 0x08088U, 0x02802U, 0x02803U, 0x02800U, 0x02801U, 0x00140U, 0x00141U, 0x00142U, 0x02408U, 0x00144U,
+    0x00145U, 0x10220U, 0x10221U, 0x00148U, 0x02402U, 0x02401U, 0x02400U, 0x400A1U, 0x400A0U, 0x02405U, 0x02404U, 0x00150U, 0x00151U,
+    0x00152U, 0x02418U, 0x03080U, 0x03081U, 0x03082U, 0x03083U, 0x09801U, 0x09800U, 0x02411U, 0x02410U, 0x48102U, 0x09804U, 0x48100U,
+    0x48101U, 0x00160U, 0x00161U, 0x10204U, 0x10205U, 0x10202U, 0x40088U, 0x10200U, 0x10201U, 0x40085U, 0x40084U, 0x02421U, 0x02420U,
+    0x40081U, 0x40080U, 0x10208U, 0x40082U, 0x41402U, 0x080C2U, 0x41400U, 0x080C0U, 0x00D01U, 0x00D00U, 0x10210U, 0x10211U, 0x40095U,
+    0x40094U, 0x02844U, 0x080C8U, 0x40091U, 0x40090U, 0x02840U, 0x02841U, 0x00180U, 0x00181U, 0x00182U, 0x08030U, 0x00184U, 0x14400U,
+    0x22201U, 0x22200U, 0x00188U, 0x00189U, 0x0018AU, 0x08038U, 0x40061U, 0x40060U, 0x40063U, 0x40062U, 0x00190U, 0x08022U, 0x08021U,
+    0x08020U, 0x03040U, 0x03041U, 0x08025U, 0x08024U, 0x40C00U, 0x40C01U, 0x08029U, 0x08028U, 0x2C000U, 0x2C001U, 0x01501U, 0x01500U,
+    0x001A0U, 0x08012U, 0x08011U, 0x08010U, 0x40049U, 0x40048U, 0x08015U, 0x08014U, 0x06200U, 0x40044U, 0x30400U, 0x08018U, 0x40041U,
+    0x40040U, 0x40043U, 0x40042U, 0x08003U, 0x08002U, 0x08001U, 0x08000U, 0x08007U, 0x08006U, 0x08005U, 0x08004U, 0x0800BU, 0x0800AU,
+    0x08009U, 0x08008U, 0x40051U, 0x40050U, 0x02880U, 0x0800CU, 0x001C0U, 0x001C1U, 0x64000U, 0x64001U, 0x03010U, 0x40028U, 0x08C00U,
+    0x08C01U, 0x40025U, 0x40024U, 0x02481U, 0x02480U, 0x40021U, 0x40020U, 0x40023U, 0x40022U, 0x03004U, 0x03005U, 0x08061U, 0x08060U,
+    0x03000U, 0x03001U, 0x03002U, 0x03003U, 0x0300CU, 0x40034U, 0x30805U, 0x30804U, 0x03008U, 0x40030U, 0x30801U, 0x30800U, 0x4000DU,
+    0x4000CU, 0x08051U, 0x08050U, 0x40009U, 0x40008U, 0x10280U, 0x4000AU, 0x40005U, 0x40004U, 0x01900U, 0x40006U, 0x40001U, 0x40000U,
+    0x40003U, 0x40002U, 0x14800U, 0x08042U, 0x08041U, 0x08040U, 0x03020U, 0x40018U, 0x08045U, 0x08044U, 0x40015U, 0x40014U, 0x08049U,
+    0x08048U, 0x40011U, 0x40010U, 0x40013U, 0x40012U, 0x00200U, 0x00201U, 0x00202U, 0x00203U, 0x00204U, 0x00205U, 0x00206U, 0x00207U,
+    0x00208U, 0x00209U, 0x0020AU, 0x50080U, 0x0020CU, 0x0020DU, 0x0020EU, 0x50084U, 0x00210U, 0x00211U, 0x00212U, 0x21040U, 0x00214U,
+    0x00215U, 0x04880U, 0x04881U, 0x00218U, 0x00219U, 0x0E001U, 0x0E000U, 0x0021CU, 0x0021DU, 0x04888U, 0x0E004U, 0x00220U, 0x00221U,
+    0x00222U, 0x00223U, 0x00224U, 0x00225U, 0x10140U, 0x10141U, 0x00228U, 0x00229U, 0x0022AU, 0x24204U, 0x12401U, 0x12400U, 0x24201U,
+    0x24200U, 0x00230U, 0x00231U, 0x00232U, 0x21060U, 0x2A000U, 0x2A001U, 0x2A002U, 0x2A003U, 0x20881U, 0x20880U, 0x20883U, 0x20882U,
+    0x05040U, 0x05041U, 0x05042U, 0x24210U, 0x00240U, 0x00241U, 0x00242U, 0x21010U, 0x00244U, 0x46000U, 0x10120U, 0x10121U, 0x00248U,
+    0x00249U, 0x0024AU, 0x21018U, 0x20480U, 0x20481U, 0x20482U, 0x20483U, 0x00250U, 0x21002U, 0x21001U, 0x21000U, 0x18081U, 0x18080U,
+    0x21005U, 0x21004U, 0x12800U, 0x12801U, 0x21009U, 0x21008U, 0x05020U, 0x05021U, 0x48200U, 0x48201U, 0x00260U, 0x00261U, 0x10104U,
+    0x04480U, 0x10102U, 0x10103U, 0x10100U, 0x10101U, 0x62002U, 0x62003U, 0x62000U, 0x62001U, 0x05010U, 0x05011U, 0x10108U, 0x10109U,
+    0x0500CU, 0x21022U, 0x21021U, 0x21020U, 0x05008U, 0x00E00U, 0x10110U, 0x10111U, 0x05004U, 0x05005U, 0x05006U, 0x21028U, 0x05000U,
+    0x05001U, 0x05002U, 0x05003U, 0x00280U, 0x00281U, 0x00282U, 0x50008U, 0x00284U, 0x00285U, 0x04810U, 0x22100U, 0x00288U, 0x50002U,
+    0x50001U, 0x50000U, 0x20440U, 0x20441U, 0x50005U, 0x50004U, 0x00290U, 0x00291U, 0x04804U, 0x04805U, 0x04802U, 0x18040U, 0x04800U,
+    0x04801U, 0x20821U, 0x20820U, 0x50011U, 0x50010U, 0x0480AU, 0x01602U, 0x04808U, 0x01600U, 0x002A0U, 0x002A1U, 0x04441U, 0x04440U,
+    0x002A4U, 0x002A5U, 0x04830U, 0x04444U, 0x06100U, 0x20810U, 0x50021U, 0x50020U, 0x06104U, 0x20814U, 0x50025U, 0x50024U, 0x20809U,
+    0x20808U, 0x13000U, 0x08300U, 0x04822U, 0x2080CU, 0x04820U, 0x04821U, 0x20801U, 0x20800U, 0x20803U, 0x20802U, 0x20805U, 0x20804U,
+    0x04828U, 0x20806U, 0x002C0U, 0x002C1U, 0x04421U, 0x04420U, 0x20408U, 0x18010U, 0x2040AU, 0x18012U, 0x20404U, 0x20405U, 0x50041U,
+    0x50040U, 0x20400U, 0x20401U, 0x20402U, 0x20403U, 0x18005U, 0x18004U, 0x21081U, 0x21080U, 0x18001U, 0x18000U, 0x04840U, 0x18002U,
+    0x20414U, 0x1800CU, 0x21089U, 0x21088U, 0x20410U, 0x18008U, 0x20412U, 0x1800AU, 0x04403U, 0x04402U, 0x04401U, 0x04400U, 0x10182U,
+    0x04406U, 0x10180U, 0x04404U, 0x01A02U, 0x0440AU, 0x01A00U, 0x04408U, 0x20420U, 0x40300U, 0x20422U, 0x40302U, 0x04413U, 0x04412U,
+    0x04411U, 0x04410U, 0x18021U, 0x18020U, 0x10190U, 0x18022U, 0x20841U, 0x20840U, 0x01A10U, 0x20842U, 0x05080U, 0x05081U, 0x05082U,
+    0x05083U, 0x00300U, 0x00301U, 0x00302U, 0x00303U, 0x00304U, 0x00305U, 0x10060U, 0x22080U, 0x00308U, 0x00309U, 0x28800U, 0x28801U,
+    0x44402U, 0x44403U, 0x44400U, 0x44401U, 0x00310U, 0x00311U, 0x10C01U, 0x10C00U, 0x00314U, 0x00315U, 0x10070U, 0x10C04U, 0x00318U,
+    0x00319U, 0x28810U, 0x10C08U, 0x44412U, 0x00000U, 0x44410U, 0x44411U, 0x00320U, 0x60400U, 0x10044U, 0x10045U, 0x10042U, 0x0C800U,
+    0x10040U, 0x10041U, 0x06080U, 0x06081U, 0x06082U, 0x06083U, 0x1004AU, 0x0C808U, 0x10048U, 0x10049U, 0x58008U, 0x08282U, 0x08281U,
+    0x08280U, 0x10052U, 0x0C810U, 0x10050U, 0x10051U, 0x58000U, 0x58001U, 0x58002U, 0x08288U, 0x02A02U, 0x02A03U, 0x02A00U, 0x02A01U,
+    0x00340U, 0x00341U, 0x10024U, 0x10025U, 0x10022U, 0x10023U, 0x10020U, 0x10021U, 0x34001U, 0x34000U, 0x02601U, 0x02600U, 0x1002AU,
+    0x34004U, 0x10028U, 0x10029U, 0x0C400U, 0x0C401U, 0x21101U, 0x21100U, 0x60800U, 0x60801U, 0x10030U, 0x10031U, 0x0C408U, 0x34010U,
+    0x21109U, 0x21108U, 0x60808U, 0x60809U, 0x10038U, 0x28420U, 0x10006U, 0x10007U, 0x10004U, 0x10005U, 0x10002U, 0x10003U, 0x10000U,
+    0x10001U, 0x1000EU, 0x40284U, 0x1000CU, 0x1000DU, 0x1000AU, 0x40280U, 0x10008U, 0x10009U, 0x10016U, 0x10017U, 0x10014U, 0x10015U,
+    0x10012U, 0x10013U, 0x10010U, 0x10011U, 0x05104U, 0x44802U, 0x44801U, 0x44800U, 0x05100U, 0x05101U, 0x10018U, 0x28400U, 0x00380U,
+    0x00381U, 0x22005U, 0x22004U, 0x22003U, 0x22002U, 0x22001U, 0x22000U, 0x06020U, 0x06021U, 0x50101U, 0x50100U, 0x11800U, 0x11801U,
+    0x22009U, 0x22008U, 0x45001U, 0x45000U, 0x08221U, 0x08220U, 0x04902U, 0x22012U, 0x04900U, 0x22010U, 0x06030U, 0x45008U, 0x08229U,
+    0x08228U, 0x11810U, 0x11811U, 0x04908U, 0x22018U, 0x06008U, 0x06009U, 0x08211U, 0x08210U, 0x100C2U, 0x22022U, 0x100C0U, 0x22020U,
+    0x06000U, 0x06001U, 0x06002U, 0x06003U, 0x06004U, 0x40240U, 0x06006U, 0x40242U, 0x08203U, 0x08202U, 0x08201U, 0x08200U, 0x08207U,
+    0x08206U, 0x08205U, 0x08204U, 0x06010U, 0x20900U, 0x08209U, 0x08208U, 0x61002U, 0x20904U, 0x61000U, 0x61001U, 0x29020U, 0x29021U,
+    0x100A4U, 0x22044U, 0x100A2U, 0x22042U, 0x100A0U, 0x22040U, 0x20504U, 0x40224U, 0x0D005U, 0x0D004U, 0x20500U, 0x40220U, 0x0D001U,
+    0x0D000U, 0x03204U, 0x18104U, 0x08261U, 0x08260U, 0x03200U, 0x18100U, 0x03202U, 0x18102U, 0x11421U, 0x11420U, 0x00000U, 0x11422U,
+    0x03208U, 0x18108U, 0x0D011U, 0x0D010U, 0x29000U, 0x29001U, 0x10084U, 0x04500U, 0x10082U, 0x40208U, 0x10080U, 0x10081U, 0x06040U,
+    0x40204U, 0x06042U, 0x40206U, 0x40201U, 0x40200U, 0x10088U, 0x40202U, 0x29010U, 0x08242U, 0x08241U, 0x08240U, 0x10092U, 0x40218U,
+    0x10090U, 0x10091U, 0x11401U, 0x11400U, 0x11403U, 0x11402U, 0x40211U, 0x40210U, 0x10098U, 0x40212U, 0x00400U, 0x00401U, 0x00402U,
+    0x00403U, 0x00404U, 0x00405U, 0x00406U, 0x00407U, 0x00408U, 0x00409U, 0x0040AU, 0x02140U, 0x0040CU, 0x0040DU, 0x01091U, 0x01090U,
+    0x00410U, 0x00411U, 0x00412U, 0x00413U, 0x00414U, 0x00860U, 0x01089U, 0x01088U, 0x00418U, 0x38000U, 0x01085U, 0x01084U, 0x01083U,
+    0x01082U, 0x01081U, 0x01080U, 0x00420U, 0x00421U, 0x00422U, 0x00423U, 0x00424U, 0x00850U, 0x42080U, 0x42081U, 0x00428U, 0x00429U,
+    0x48801U, 0x48800U, 0x09100U, 0x12200U, 0x24401U, 0x24400U, 0x00430U, 0x00844U, 0x00432U, 0x00846U, 0x00841U, 0x00840U, 0x1C000U,
+    0x00842U, 0x00438U, 0x0084CU, 0x010A5U, 0x010A4U, 0x00849U, 0x00848U, 0x010A1U, 0x010A0U, 0x00440U, 0x00441U, 0x00442U, 0x02108U,
+    0x00444U, 0x00830U, 0x70001U, 0x70000U, 0x00448U, 0x02102U, 0x02101U, 0x02100U, 0x20280U, 0x20281U, 0x02105U, 0x02104U, 0x00450U,
+    0x00824U, 0x00452U, 0x00826U, 0x00821U, 0x00820U, 0x00823U, 0x00822U, 0x24802U, 0x02112U, 0x24800U, 0x02110U, 0x00829U, 0x00828U,
+    0x48400U, 0x010C0U, 0x00460U, 0x00814U, 0x04281U, 0x04280U, 0x00811U, 0x00810U, 0x00813U, 0x00812U, 0x54000U, 0x54001U, 0x02121U,
+    0x02120U, 0x00819U, 0x00818U, 0x0081BU, 0x0081AU, 0x00805U, 0x00804U, 0x41100U, 0x00806U, 0x00801U, 0x00800U, 0x00803U, 0x00802U,
+    0x0A080U, 0x0080CU, 0x0A082U, 0x0080EU, 0x00809U, 0x00808U, 0x0080BU, 0x0080AU, 0x00480U, 0x00481U, 0x00482U, 0x00483U, 0x00484U,
+    0x14100U, 0x42020U, 0x01018U, 0x00488U, 0x00489U, 0x01015U, 0x01014U, 0x20240U, 0x01012U, 0x01011U, 0x01010U, 0x00490U, 0x00491U,
+    0x0100DU, 0x0100CU, 0x0100BU, 0x0100AU, 0x01009U, 0x01008U, 0x40900U, 0x01006U, 0x01005U, 0x01004U, 0x01003U, 0x01002U, 0x01001U,
+    0x01000U, 0x004A0U, 0x004A1U, 0x42004U, 0x04240U, 0x42002U, 0x42003U, 0x42000U, 0x42001U, 0x30102U, 0x30103U, 0x30100U, 0x30101U,
+    0x4200AU, 0x01032U, 0x42008U, 0x01030U, 0x25000U, 0x25001U, 0x08501U, 0x08500U, 0x008C1U, 0x008C0U, 0x42010U, 0x01028U, 0x0A040U,
+    0x0A041U, 0x01025U, 0x01024U, 0x01023U, 0x01022U, 0x01021U, 0x01020U, 0x004C0U, 0x49000U, 0x04221U, 0x04220U, 0x20208U, 0x20209U,
+    0x08900U, 0x08901U, 0x20204U, 0x20205U, 0x02181U, 0x02180U, 0x20200U, 0x20201U, 0x20202U, 0x01050U, 0x0A028U, 0x008A4U, 0x0104DU,
+    0x0104CU, 0x008A1U, 0x008A0U, 0x01049U, 0x01048U, 0x0A020U, 0x0A021U, 0x01045U, 0x01044U, 0x20210U, 0x01042U, 0x01041U, 0x01040U,
+    0x04203U, 0x04202U, 0x04201U, 0x04200U, 0x00891U, 0x00890U, 0x42040U, 0x04204U, 0x0A010U, 0x0A011U, 0x01C00U, 0x04208U, 0x20220U,
+    0x40500U, 0x20222U, 0x40502U, 0x0A008U, 0x00884U, 0x04211U, 0x04210U, 0x00881U, 0x00880U, 0x00883U, 0x00882U, 0x0A000U, 0x0A001U,
+    0x0A002U, 0x0A003U, 0x0A004U, 0x00888U, 0x01061U, 0x01060U, 0x00500U, 0x00501U, 0x00502U, 0x02048U, 0x00504U, 0x14080U, 0x00506U,
+    0x14082U, 0x00508U, 0x02042U, 0x02041U, 0x02040U, 0x09020U, 0x09021U, 0x44200U, 0x02044U, 0x00510U, 0x00511U, 0x10A01U, 0x10A00U,
+    0x4A001U, 0x4A000U, 0x4A003U, 0x4A002U, 0x40880U, 0x40881U, 0x02051U, 0x02050U, 0x40884U, 0x01182U, 0x01181U, 0x01180U, 0x00520U,
+    0x60200U, 0x00522U, 0x60202U, 0x09008U, 0x09009U, 0x0900AU, 0x0900BU, 0x09004U, 0x09005U, 0x30080U, 0x02060U, 0x09000U, 0x09001U,
+    0x09002U, 0x09003U, 0x41042U, 0x08482U, 0x41040U, 0x08480U, 0x00941U, 0x00940U, 0x41044U, 0x00942U, 0x09014U, 0x09015U, 0x02C04U,
+    0x08488U, 0x09010U, 0x09011U, 0x02C00U, 0x02C01U, 0x00540U, 0x0200AU, 0x02009U, 0x02008U, 0x08882U, 0x0200EU, 0x08880U, 0x0200CU,
+    0x02003U, 0x02002U, 0x02001U, 0x02000U, 0x02007U, 0x02006U, 0x02005U, 0x02004U, 0x0C200U, 0x0C201U, 0x41020U, 0x02018U, 0x00921U,
+    0x00920U, 0x41024U, 0x00922U, 0x02013U, 0x02012U, 0x02011U, 0x02010U, 0x02017U, 0x02016U, 0x02015U, 0x02014U, 0x41012U, 0x0202AU,
+    0x41010U, 0x02028U, 0x26000U, 0x00910U, 0x10600U, 0x10601U, 0x02023U, 0x02022U, 0x02021U, 0x02020U, 0x09040U, 0x40480U, 0x02025U,
+    0x02024U, 0x41002U, 0x00904U, 0x41000U, 0x41001U, 0x00901U, 0x00900U, 0x41004U, 0x00902U, 0x4100AU, 0x02032U, 0x41008U, 0x02030U,
+    0x00909U, 0x00908U, 0x28201U, 0x28200U, 0x00580U, 0x14004U, 0x00582U, 0x14006U, 0x14001U, 0x14000U, 0x08840U, 0x14002U, 0x40810U,
+    0x40811U, 0x30020U, 0x020C0U, 0x14009U, 0x14008U, 0x01111U, 0x01110U, 0x40808U, 0x40809U, 0x08421U, 0x08420U, 0x14011U, 0x14010U,
+    0x01109U, 0x01108U, 0x40800U, 0x40801U, 0x40802U, 0x01104U, 0x40804U, 0x01102U, 0x01101U, 0x01100U, 0x03801U, 0x03800U, 0x30008U,
+    0x08410U, 0x14021U, 0x14020U, 0x42100U, 0x42101U, 0x30002U, 0x30003U, 0x30000U, 0x30001U, 0x09080U, 0x40440U, 0x30004U, 0x30005U,
+    0x08403U, 0x08402U, 0x08401U, 0x08400U, 0x08407U, 0x08406U, 0x08405U, 0x08404U, 0x40820U, 0x40821U, 0x30010U, 0x08408U, 0x40824U,
+    0x01122U, 0x01121U, 0x01120U, 0x08806U, 0x0208AU, 0x08804U, 0x02088U, 0x08802U, 0x14040U, 0x08800U, 0x08801U, 0x02083U, 0x02082U,
+    0x02081U, 0x02080U, 0x20300U, 0x40420U, 0x08808U, 0x02084U, 0x03404U, 0x03405U, 0x08814U, 0x02098U, 0x03400U, 0x03401U, 0x08810U,
+    0x08811U, 0x40840U, 0x40841U, 0x02091U, 0x02090U, 0x40844U, 0x01142U, 0x01141U, 0x01140U, 0x04303U, 0x04302U, 0x04301U, 0x04300U,
+    0x40409U, 0x40408U, 0x08820U, 0x08821U, 0x40405U, 0x40404U, 0x30040U, 0x020A0U, 0x40401U, 0x40400U, 0x40403U, 0x40402U, 0x41082U,
+    0x08442U, 0x41080U, 0x08440U, 0x00981U, 0x00980U, 0x41084U, 0x00982U, 0x0A100U, 0x11200U, 0x0A102U, 0x11202U, 0x40411U, 0x40410U,
+    0x40413U, 0x40412U, 0x00600U, 0x00601U, 0x00602U, 0x00603U, 0x00604U, 0x00605U, 0x00606U, 0x00607U, 0x00608U, 0x05800U, 0x0060AU,
+    0x05802U, 0x200C0U, 0x12020U, 0x44100U, 0x44101U, 0x00610U, 0x00611U, 0x10901U, 0x10900U, 0x51000U, 0x51001U, 0x51002U, 0x10904U,
+    0x00618U, 0x05810U, 0x01285U, 0x01284U, 0x51008U, 0x01282U, 0x01281U, 0x01280U, 0x00620U, 0x60100U, 0x040C1U, 0x040C0U, 0x12009U,
+    0x12008U, 0x21800U, 0x21801U, 0x12005U, 0x12004U, 0x12007U, 0x12006U, 0x12001U, 0x12000U, 0x12003U, 0x12002U, 0x00630U, 0x00A44U,
+    0x040D1U, 0x040D0U, 0x00A41U, 0x00A40U, 0x21810U, 0x00A42U, 0x12015U, 0x12014U, 0x00000U, 0x12016U, 0x12011U, 0x12010U, 0x12013U,
+    0x12012U, 0x00640U, 0x00641U, 0x040A1U, 0x040A0U, 0x20088U, 0x20089U, 0x2008AU, 0x040A4U, 0x20084U, 0x20085U, 0x19000U, 0x02300U,
+    0x20080U, 0x20081U, 0x20082U, 0x20083U, 0x0C100U, 0x0C101U, 0x21401U, 0x21400U, 0x00A21U, 0x00A20U, 0x00A23U, 0x00A22U, 0x20094U,
+    0x20095U, 0x19010U, 0x21408U, 0x20090U, 0x20091U, 0x20092U, 0x28120U, 0x04083U, 0x04082U, 0x04081U, 0x04080U, 0x00A11U, 0x00A10U,
+    0x10500U, 0x04084U, 0x200A4U, 0x0408AU, 0x04089U, 0x04088U, 0x200A0U, 0x12040U, 0x200A2U, 0x12042U, 0x00A05U, 0x00A04U, 0x04091U,
+    0x04090U, 0x00A01U, 0x00A00U, 0x00A03U, 0x00A02U, 0x05404U, 0x00A0CU, 0x28105U, 0x28104U, 0x05400U, 0x00A08U, 0x28101U, 0x28100U,
+    0x00680U, 0x00681U, 0x04061U, 0x04060U, 0x20048U, 0x20049U, 0x2004AU, 0x04064U, 0x20044U, 0x20045U, 0x50401U, 0x50400U, 0x20040U,
+    0x20041U, 0x20042U, 0x01210U, 0x68002U, 0x68003U, 0x68000U, 0x68001U, 0x04C02U, 0x0120AU, 0x04C00U, 0x01208U, 0x20054U, 0x01206U,
+    0x01205U, 0x01204U, 0x20050U, 0x01202U, 0x01201U, 0x01200U, 0x18800U, 0x04042U, 0x04041U, 0x04040U, 0x42202U, 0x04046U, 0x42200U,
+    0x04044U, 0x20064U, 0x0404AU, 0x04049U, 0x04048U, 0x20060U, 0x12080U, 0x20062U, 0x12082U, 0x18810U, 0x04052U, 0x04051U, 0x04050U,
+    0x4C009U, 0x4C008U, 0x42210U, 0x04054U, 0x20C01U, 0x20C00U, 0x20C03U, 0x20C02U, 0x4C001U, 0x4C000U, 0x01221U, 0x01220U, 0x2000CU,
+    0x04022U, 0x04021U, 0x04020U, 0x20008U, 0x20009U, 0x2000AU, 0x04024U, 0x20004U, 0x20005U, 0x20006U, 0x04028U, 0x20000U, 0x20001U,
+    0x20002U, 0x20003U, 0x2001CU, 0x04032U, 0x04031U, 0x04030U, 0x20018U, 0x18400U, 0x2001AU, 0x18402U, 0x20014U, 0x20015U, 0x20016U,
+    0x01244U, 0x20010U, 0x20011U, 0x20012U, 0x01240U, 0x04003U, 0x04002U, 0x04001U, 0x04000U, 0x20028U, 0x04006U, 0x04005U, 0x04004U,
+    0x20024U, 0x0400AU, 0x04009U, 0x04008U, 0x20020U, 0x20021U, 0x20022U, 0x0400CU, 0x04013U, 0x04012U, 0x04011U, 0x04010U, 0x00A81U,
+    0x00A80U, 0x04015U, 0x04014U, 0x0A200U, 0x11100U, 0x04019U, 0x04018U, 0x20030U, 0x20031U, 0x50800U, 0x50801U, 0x00700U, 0x60020U,
+    0x10811U, 0x10810U, 0x4400AU, 0x60024U, 0x44008U, 0x44009U, 0x44006U, 0x02242U, 0x44004U, 0x02240U, 0x44002U, 0x44003U, 0x44000U,
+    0x44001U, 0x0C040U, 0x10802U, 0x10801U, 0x10800U, 0x0C044U, 0x10806U, 0x10805U, 0x10804U, 0x23000U, 0x23001U, 0x10809U, 0x10808U,
+    0x44012U, 0x44013U, 0x44010U, 0x44011U, 0x60001U, 0x60000U, 0x60003U, 0x60002U, 0x60005U, 0x60004U, 0x10440U, 0x10441U, 0x60009U,
+    0x60008U, 0x44024U, 0x6000AU, 0x09200U, 0x12100U, 0x44020U, 0x44021U, 0x60011U, 0x60010U, 0x10821U, 0x10820U, 0x07003U, 0x07002U,
+    0x07001U, 0x07000U, 0x23020U, 0x60018U, 0x28045U, 0x28044U, 0x09210U, 0x28042U, 0x28041U, 0x28040U, 0x0C010U, 0x0C011U, 0x02209U,
+    0x02208U, 0x10422U, 0x10423U, 0x10420U, 0x10421U, 0x02203U, 0x02202U, 0x02201U, 0x02200U, 0x20180U, 0x20181U, 0x44040U, 0x02204U,
+    0x0C000U, 0x0C001U, 0x0C002U, 0x10840U, 0x0C004U, 0x0C005U, 0x0C006U, 0x10844U, 0x0C008U, 0x0C009U, 0x02211U, 0x02210U, 0x0C00CU,
+    0x28022U, 0x28021U, 0x28020U, 0x60041U, 0x60040U, 0x10404U, 0x04180U, 0x10402U, 0x10403U, 0x10400U, 0x10401U, 0x02223U, 0x02222U,
+    0x02221U, 0x02220U, 0x1040AU, 0x28012U, 0x10408U, 0x28010U, 0x0C020U, 0x0C021U, 0x41200U, 0x41201U, 0x00B01U, 0x00B00U, 0x10410U,
+    0x28008U, 0x11081U, 0x11080U, 0x28005U, 0x28004U, 0x28003U, 0x28002U, 0x28001U, 0x28000U, 0x52040U, 0x14204U, 0x22405U, 0x22404U,
+    0x14201U, 0x14200U, 0x22401U, 0x22400U, 0x20144U, 0x20145U, 0x44084U, 0x022C0U, 0x20140U, 0x20141U, 0x44080U, 0x44081U, 0x40A08U,
+    0x10882U, 0x10881U, 0x10880U, 0x14211U, 0x14210U, 0x1A008U, 0x10884U, 0x40A00U, 0x40A01U, 0x40A02U, 0x01304U, 0x1A002U, 0x01302U,
+    0x1A000U, 0x01300U, 0x60081U, 0x60080U, 0x04141U, 0x04140U, 0x60085U, 0x60084U, 0x104C0U, 0x04144U, 0x06400U, 0x06401U, 0x30200U,
+    0x30201U, 0x06404U, 0x40640U, 0x30204U, 0x30205U, 0x08603U, 0x08602U, 0x08601U, 0x08600U, 0x00000U, 0x08606U, 0x08605U, 0x08604U,
+    0x11041U, 0x11040U, 0x30210U, 0x11042U, 0x11045U, 0x11044U, 0x1A020U, 0x01320U, 0x52000U, 0x52001U, 0x04121U, 0x04120U, 0x20108U,
+    0x20109U, 0x08A00U, 0x08A01U, 0x20104U, 0x20105U, 0x02281U, 0x02280U, 0x20100U, 0x20101U, 0x20102U, 0x20103U, 0x0C080U, 0x0C081U,
+    0x0C082U, 0x04130U, 0x0C084U, 0x06808U, 0x08A10U, 0x08A11U, 0x11021U, 0x11020U, 0x11023U, 0x11022U, 0x20110U, 0x06800U, 0x20112U,
+    0x06802U, 0x04103U, 0x04102U, 0x04101U, 0x04100U, 0x10482U, 0x04106U, 0x10480U, 0x04104U, 0x11011U, 0x11010U, 0x04109U, 0x04108U,
+    0x20120U, 0x40600U, 0x20122U, 0x40602U, 0x11009U, 0x11008U, 0x22800U, 0x04110U, 0x1100DU, 0x1100CU, 0x22804U, 0x04114U, 0x11001U,
+    0x11000U, 0x11003U, 0x11002U, 0x11005U, 0x11004U, 0x28081U, 0x28080U};
+
+#define X18             0x00040000   /* vector representation of X^{18} */
+#define X11             0x00000800   /* vector representation of X^{11} */
+#define MASK8           0xfffff800   /* auxiliary vector for testing */
+#define GENPOL          0x00000c75   /* generator polinomial, g(x) */
+
+unsigned int CGolay2087::getSyndrome1987(unsigned int pattern)
+/*
+ * Compute the syndrome corresponding to the given pattern, i.e., the
+ * remainder after dividing the pattern (when considering it as the vector
+ * representation of a polynomial) by the generator polynomial, GENPOL.
+ * In the program this pattern has several meanings: (1) pattern = infomation
+ * bits, when constructing the encoding table; (2) pattern = error pattern,
+ * when constructing the decoding table; and (3) pattern = received vector, to
+ * obtain its syndrome in decoding.
+ */
+{
+    unsigned int aux = X18;
+    
+    if (pattern >= X11) {
+        while (pattern & MASK8) {
+            while (!(aux & pattern))
+                aux = aux >> 1;
+            
+            pattern ^= (aux / X11) * GENPOL;
+        }
+    }
+    
+    return pattern;
+}
+
+unsigned char CGolay2087::decode(const unsigned char* data)
+{
+    assert(data != NULL);
+    
+    unsigned int code = (data[0U] << 11) + (data[1U] << 3) + (data[2U] >> 5);
+    unsigned int syndrome = getSyndrome1987(code);
+    unsigned int error_pattern = DECODING_TABLE_1987[syndrome];
+    
+    if (error_pattern != 0x00U)
+        code ^= error_pattern;
+    
+    return code >> 11;
+}
+
+void CGolay2087::encode(unsigned char* data)
+{
+    assert(data != NULL);
+    
+    unsigned int value = data[0U];
+    
+    unsigned int cksum = ENCODING_TABLE_2087[value];
+    
+    data[1U] = cksum & 0xFFU;
+    data[2U] = cksum >> 8;
+}
diff --git a/src/cgolay2087.h b/src/cgolay2087.h
new file mode 100644
index 0000000..22c5c16
--- /dev/null
+++ b/src/cgolay2087.h
@@ -0,0 +1,32 @@
+/*
+ *   Copyright (C) 2015 by Jonathan Naylor G4KLX
+ *
+ *   This program is free software; you can redistribute it and/or modify
+ *   it under the terms of the GNU General Public License as published by
+ *   the Free Software Foundation; either version 2 of the License, or
+ *   (at your option) any later version.
+ *
+ *   This program is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *   GNU General Public License for more details.
+ *
+ *   You should have received a copy of the GNU General Public License
+ *   along with this program; if not, write to the Free Software
+ *   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#ifndef Golay2087_H
+#define Golay2087_H
+
+class CGolay2087 {
+public:
+    static void encode(unsigned char* data);
+    
+    static unsigned char decode(const unsigned char* data);
+    
+private:
+    static unsigned int getSyndrome1987(unsigned int pattern);
+};
+
+#endif
diff --git a/src/chamming.cpp b/src/chamming.cpp
new file mode 100644
index 0000000..8a9b5f8
--- /dev/null
+++ b/src/chamming.cpp
@@ -0,0 +1,349 @@
+/*
+ *   Copyright (C) 2015,2016 by Jonathan Naylor G4KLX
+ *
+ *   This program is free software; you can redistribute it and/or modify
+ *   it under the terms of the GNU General Public License as published by
+ *   the Free Software Foundation; either version 2 of the License, or
+ *   (at your option) any later version.
+ *
+ *   This program is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *   GNU General Public License for more details.
+ *
+ *   You should have received a copy of the GNU General Public License
+ *   along with this program; if not, write to the Free Software
+ *   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include "chamming.h"
+
+#include <cstdio>
+#include <cassert>
+
+// Hamming (15,11,3) check a boolean data array
+bool CHamming::decode15113_1(bool* d)
+{
+    assert(d != NULL);
+    
+    // Calculate the parity it should have
+    bool c0 = d[0] ^ d[1] ^ d[2] ^ d[3] ^ d[4] ^ d[5] ^ d[6];
+    bool c1 = d[0] ^ d[1] ^ d[2] ^ d[3] ^ d[7] ^ d[8] ^ d[9];
+    bool c2 = d[0] ^ d[1] ^ d[4] ^ d[5] ^ d[7] ^ d[8] ^ d[10];
+    bool c3 = d[0] ^ d[2] ^ d[4] ^ d[6] ^ d[7] ^ d[9] ^ d[10];
+    
+    unsigned char n = 0U;
+    n |= (c0 != d[11]) ? 0x01U : 0x00U;
+    n |= (c1 != d[12]) ? 0x02U : 0x00U;
+    n |= (c2 != d[13]) ? 0x04U : 0x00U;
+    n |= (c3 != d[14]) ? 0x08U : 0x00U;
+    
+    switch (n)
+    {
+            // Parity bit errors
+        case 0x01U: d[11] = !d[11]; return true;
+        case 0x02U: d[12] = !d[12]; return true;
+        case 0x04U: d[13] = !d[13]; return true;
+        case 0x08U: d[14] = !d[14]; return true;
+            
+            // Data bit errors
+        case 0x0FU: d[0]  = !d[0];  return true;
+        case 0x07U: d[1]  = !d[1];  return true;
+        case 0x0BU: d[2]  = !d[2];  return true;
+        case 0x03U: d[3]  = !d[3];  return true;
+        case 0x0DU: d[4]  = !d[4];  return true;
+        case 0x05U: d[5]  = !d[5];  return true;
+        case 0x09U: d[6]  = !d[6];  return true;
+        case 0x0EU: d[7]  = !d[7];  return true;
+        case 0x06U: d[8]  = !d[8];  return true;
+        case 0x0AU: d[9]  = !d[9];  return true;
+        case 0x0CU: d[10] = !d[10]; return true;
+            
+            // No bit errors
+        default: return false;
+    }
+}
+
+void CHamming::encode15113_1(bool* d)
+{
+    assert(d != NULL);
+    
+    // Calculate the checksum this row should have
+    d[11] = d[0] ^ d[1] ^ d[2] ^ d[3] ^ d[4] ^ d[5] ^ d[6];
+    d[12] = d[0] ^ d[1] ^ d[2] ^ d[3] ^ d[7] ^ d[8] ^ d[9];
+    d[13] = d[0] ^ d[1] ^ d[4] ^ d[5] ^ d[7] ^ d[8] ^ d[10];
+    d[14] = d[0] ^ d[2] ^ d[4] ^ d[6] ^ d[7] ^ d[9] ^ d[10];
+}
+
+// Hamming (15,11,3) check a boolean data array
+bool CHamming::decode15113_2(bool* d)
+{
+    assert(d != NULL);
+    
+    // Calculate the checksum this row should have
+    bool c0 = d[0] ^ d[1] ^ d[2] ^ d[3] ^ d[5] ^ d[7] ^ d[8];
+    bool c1 = d[1] ^ d[2] ^ d[3] ^ d[4] ^ d[6] ^ d[8] ^ d[9];
+    bool c2 = d[2] ^ d[3] ^ d[4] ^ d[5] ^ d[7] ^ d[9] ^ d[10];
+    bool c3 = d[0] ^ d[1] ^ d[2] ^ d[4] ^ d[6] ^ d[7] ^ d[10];
+    
+    unsigned char n = 0x00U;
+    n |= (c0 != d[11]) ? 0x01U : 0x00U;
+    n |= (c1 != d[12]) ? 0x02U : 0x00U;
+    n |= (c2 != d[13]) ? 0x04U : 0x00U;
+    n |= (c3 != d[14]) ? 0x08U : 0x00U;
+    
+    switch (n) {
+            // Parity bit errors
+        case 0x01U: d[11] = !d[11]; return true;
+        case 0x02U: d[12] = !d[12]; return true;
+        case 0x04U: d[13] = !d[13]; return true;
+        case 0x08U: d[14] = !d[14]; return true;
+            
+            // Data bit errors
+        case 0x09U: d[0]  = !d[0];  return true;
+        case 0x0BU: d[1]  = !d[1];  return true;
+        case 0x0FU: d[2]  = !d[2];  return true;
+        case 0x07U: d[3]  = !d[3];  return true;
+        case 0x0EU: d[4]  = !d[4];  return true;
+        case 0x05U: d[5]  = !d[5];  return true;
+        case 0x0AU: d[6]  = !d[6];  return true;
+        case 0x0DU: d[7]  = !d[7];  return true;
+        case 0x03U: d[8]  = !d[8];  return true;
+        case 0x06U: d[9]  = !d[9];  return true;
+        case 0x0CU: d[10] = !d[10]; return true;
+            
+            // No bit errors
+        default: return false;
+    }
+}
+
+void CHamming::encode15113_2(bool* d)
+{
+    assert(d != NULL);
+    
+    // Calculate the checksum this row should have
+    d[11] = d[0] ^ d[1] ^ d[2] ^ d[3] ^ d[5] ^ d[7] ^ d[8];
+    d[12] = d[1] ^ d[2] ^ d[3] ^ d[4] ^ d[6] ^ d[8] ^ d[9];
+    d[13] = d[2] ^ d[3] ^ d[4] ^ d[5] ^ d[7] ^ d[9] ^ d[10];
+    d[14] = d[0] ^ d[1] ^ d[2] ^ d[4] ^ d[6] ^ d[7] ^ d[10];
+}
+
+// Hamming (13,9,3) check a boolean data array
+bool CHamming::decode1393(bool* d)
+{
+    assert(d != NULL);
+    
+    // Calculate the checksum this column should have
+    bool c0 = d[0] ^ d[1] ^ d[3] ^ d[5] ^ d[6];
+    bool c1 = d[0] ^ d[1] ^ d[2] ^ d[4] ^ d[6] ^ d[7];
+    bool c2 = d[0] ^ d[1] ^ d[2] ^ d[3] ^ d[5] ^ d[7] ^ d[8];
+    bool c3 = d[0] ^ d[2] ^ d[4] ^ d[5] ^ d[8];
+    
+    unsigned char n = 0x00U;
+    n |= (c0 != d[9])  ? 0x01U : 0x00U;
+    n |= (c1 != d[10]) ? 0x02U : 0x00U;
+    n |= (c2 != d[11]) ? 0x04U : 0x00U;
+    n |= (c3 != d[12]) ? 0x08U : 0x00U;
+    
+    switch (n) {
+            // Parity bit errors
+        case 0x01U: d[9]  = !d[9];  return true;
+        case 0x02U: d[10] = !d[10]; return true;
+        case 0x04U: d[11] = !d[11]; return true;
+        case 0x08U: d[12] = !d[12]; return true;
+            
+            // Data bit erros
+        case 0x0FU: d[0] = !d[0]; return true;
+        case 0x07U: d[1] = !d[1]; return true;
+        case 0x0EU: d[2] = !d[2]; return true;
+        case 0x05U: d[3] = !d[3]; return true;
+        case 0x0AU: d[4] = !d[4]; return true;
+        case 0x0DU: d[5] = !d[5]; return true;
+        case 0x03U: d[6] = !d[6]; return true;
+        case 0x06U: d[7] = !d[7]; return true;
+        case 0x0CU: d[8] = !d[8]; return true;
+            
+            // No bit errors
+        default: return false;
+    }
+}
+
+void CHamming::encode1393(bool* d)
+{
+    assert(d != NULL);
+    
+    // Calculate the checksum this column should have
+    d[9]  = d[0] ^ d[1] ^ d[3] ^ d[5] ^ d[6];
+    d[10] = d[0] ^ d[1] ^ d[2] ^ d[4] ^ d[6] ^ d[7];
+    d[11] = d[0] ^ d[1] ^ d[2] ^ d[3] ^ d[5] ^ d[7] ^ d[8];
+    d[12] = d[0] ^ d[2] ^ d[4] ^ d[5] ^ d[8];
+}
+
+// Hamming (10,6,3) check a boolean data array
+bool CHamming::decode1063(bool* d)
+{
+    assert(d != NULL);
+    
+    // Calculate the checksum this column should have
+    bool c0 = d[0] ^ d[1] ^ d[2] ^ d[5];
+    bool c1 = d[0] ^ d[1] ^ d[3] ^ d[5];
+    bool c2 = d[0] ^ d[2] ^ d[3] ^ d[4];
+    bool c3 = d[1] ^ d[2] ^ d[3] ^ d[4];
+    
+    unsigned char n = 0x00U;
+    n |= (c0 != d[6]) ? 0x01U : 0x00U;
+    n |= (c1 != d[7]) ? 0x02U : 0x00U;
+    n |= (c2 != d[8]) ? 0x04U : 0x00U;
+    n |= (c3 != d[9]) ? 0x08U : 0x00U;
+    
+    switch (n) {
+            // Parity bit errors
+        case 0x01U: d[6] = !d[6]; return true;
+        case 0x02U: d[7] = !d[7]; return true;
+        case 0x04U: d[8] = !d[8]; return true;
+        case 0x08U: d[9] = !d[9]; return true;
+            
+            // Data bit erros
+        case 0x07U: d[0] = !d[0]; return true;
+        case 0x0BU: d[1] = !d[1]; return true;
+        case 0x0DU: d[2] = !d[2]; return true;
+        case 0x0EU: d[3] = !d[3]; return true;
+        case 0x0CU: d[4] = !d[4]; return true;
+        case 0x03U: d[5] = !d[5]; return true;
+            
+            // No bit errors
+        default: return false;
+    }
+}
+
+void CHamming::encode1063(bool* d)
+{
+    assert(d != NULL);
+    
+    // Calculate the checksum this column should have
+    d[6] = d[0] ^ d[1] ^ d[2] ^ d[5];
+    d[7] = d[0] ^ d[1] ^ d[3] ^ d[5];
+    d[8] = d[0] ^ d[2] ^ d[3] ^ d[4];
+    d[9] = d[1] ^ d[2] ^ d[3] ^ d[4];
+}
+
+// A Hamming (16,11,4) Check
+bool CHamming::decode16114(bool* d)
+{
+    assert(d != NULL);
+    
+    // Calculate the checksum this column should have
+    bool c0 = d[0] ^ d[1] ^ d[2] ^ d[3] ^ d[5] ^ d[7] ^ d[8];
+    bool c1 = d[1] ^ d[2] ^ d[3] ^ d[4] ^ d[6] ^ d[8] ^ d[9];
+    bool c2 = d[2] ^ d[3] ^ d[4] ^ d[5] ^ d[7] ^ d[9] ^ d[10];
+    bool c3 = d[0] ^ d[1] ^ d[2] ^ d[4] ^ d[6] ^ d[7] ^ d[10];
+    bool c4 = d[0] ^ d[2] ^ d[5] ^ d[6] ^ d[8] ^ d[9] ^ d[10];
+    
+    // Compare these with the actual bits
+    unsigned char n = 0x00U;
+    n |= (c0 != d[11]) ? 0x01U : 0x00U;
+    n |= (c1 != d[12]) ? 0x02U : 0x00U;
+    n |= (c2 != d[13]) ? 0x04U : 0x00U;
+    n |= (c3 != d[14]) ? 0x08U : 0x00U;
+    n |= (c4 != d[15]) ? 0x10U : 0x00U;
+    
+    switch (n) {
+            // Parity bit errors
+        case 0x01U: d[11] = !d[11]; return true;
+        case 0x02U: d[12] = !d[12]; return true;
+        case 0x04U: d[13] = !d[13]; return true;
+        case 0x08U: d[14] = !d[14]; return true;
+        case 0x10U: d[15] = !d[15]; return true;
+            
+            // Data bit errors
+        case 0x19U: d[0]  = !d[0];  return true;
+        case 0x0BU: d[1]  = !d[1];  return true;
+        case 0x1FU: d[2]  = !d[2];  return true;
+        case 0x07U: d[3]  = !d[3];  return true;
+        case 0x0EU: d[4]  = !d[4];  return true;
+        case 0x15U: d[5]  = !d[5];  return true;
+        case 0x1AU: d[6]  = !d[6];  return true;
+        case 0x0DU: d[7]  = !d[7];  return true;
+        case 0x13U: d[8]  = !d[8];  return true;
+        case 0x16U: d[9]  = !d[9];  return true;
+        case 0x1CU: d[10] = !d[10]; return true;
+            
+            // No bit errors
+        case 0x00U: return true;
+            
+            // Unrecoverable errors
+        default: return false;
+    }
+}
+
+void CHamming::encode16114(bool* d)
+{
+    assert(d != NULL);
+    
+    d[11] = d[0] ^ d[1] ^ d[2] ^ d[3] ^ d[5] ^ d[7] ^ d[8];
+    d[12] = d[1] ^ d[2] ^ d[3] ^ d[4] ^ d[6] ^ d[8] ^ d[9];
+    d[13] = d[2] ^ d[3] ^ d[4] ^ d[5] ^ d[7] ^ d[9] ^ d[10];
+    d[14] = d[0] ^ d[1] ^ d[2] ^ d[4] ^ d[6] ^ d[7] ^ d[10];
+    d[15] = d[0] ^ d[2] ^ d[5] ^ d[6] ^ d[8] ^ d[9] ^ d[10];
+}
+
+// A Hamming (17,12,3) Check
+bool CHamming::decode17123(bool* d)
+{
+    assert(d != NULL);
+    
+    // Calculate the checksum this column should have
+    bool c0 = d[0] ^ d[1] ^ d[2] ^ d[3] ^ d[6] ^ d[7] ^ d[9];
+    bool c1 = d[0] ^ d[1] ^ d[2] ^ d[3] ^ d[4] ^ d[7] ^ d[8] ^ d[10];
+    bool c2 = d[1] ^ d[2] ^ d[3] ^ d[4] ^ d[5] ^ d[8] ^ d[9] ^ d[11];
+    bool c3 = d[0] ^ d[1] ^ d[4] ^ d[5] ^ d[7] ^ d[10];
+    bool c4 = d[0] ^ d[1] ^ d[2] ^ d[5] ^ d[6] ^ d[8] ^ d[11];
+    
+    // Compare these with the actual bits
+    unsigned char n = 0x00U;
+    n |= (c0 != d[12]) ? 0x01U : 0x00U;
+    n |= (c1 != d[13]) ? 0x02U : 0x00U;
+    n |= (c2 != d[14]) ? 0x04U : 0x00U;
+    n |= (c3 != d[15]) ? 0x08U : 0x00U;
+    n |= (c4 != d[16]) ? 0x10U : 0x00U;
+    
+    switch (n) {
+            // Parity bit errors
+        case 0x01U: d[12] = !d[12]; return true;
+        case 0x02U: d[13] = !d[13]; return true;
+        case 0x04U: d[14] = !d[14]; return true;
+        case 0x08U: d[15] = !d[15]; return true;
+        case 0x10U: d[16] = !d[16]; return true;
+            
+            // Data bit errors
+        case 0x1BU: d[0]  = !d[0];  return true;
+        case 0x1FU: d[1]  = !d[1];  return true;
+        case 0x17U: d[2]  = !d[2];  return true;
+        case 0x07U: d[3]  = !d[3];  return true;
+        case 0x0EU: d[4]  = !d[4];  return true;
+        case 0x1CU: d[5]  = !d[5];  return true;
+        case 0x11U: d[6]  = !d[6];  return true;
+        case 0x0BU: d[7]  = !d[7];  return true;
+        case 0x16U: d[8]  = !d[8];  return true;
+        case 0x05U: d[9]  = !d[9];  return true;
+        case 0x0AU: d[10] = !d[10]; return true;
+        case 0x14U: d[11] = !d[11]; return true;
+            
+            // No bit errors
+        case 0x00U: return true;
+            
+            // Unrecoverable errors
+        default: return false;
+    }
+}
+
+void CHamming::encode17123(bool* d)
+{
+    assert(d != NULL);
+    
+    d[12] = d[0] ^ d[1] ^ d[2] ^ d[3] ^ d[6] ^ d[7] ^ d[9];
+    d[13] = d[0] ^ d[1] ^ d[2] ^ d[3] ^ d[4] ^ d[7] ^ d[8] ^ d[10];
+    d[14] = d[1] ^ d[2] ^ d[3] ^ d[4] ^ d[5] ^ d[8] ^ d[9] ^ d[11];
+    d[15] = d[0] ^ d[1] ^ d[4] ^ d[5] ^ d[7] ^ d[10];
+    d[16] = d[0] ^ d[1] ^ d[2] ^ d[5] ^ d[6] ^ d[8] ^ d[11];
+}
diff --git a/src/chamming.h b/src/chamming.h
new file mode 100644
index 0000000..bf6b626
--- /dev/null
+++ b/src/chamming.h
@@ -0,0 +1,43 @@
+/*
+ *   Copyright (C) 2015,2016 by Jonathan Naylor G4KLX
+ *
+ *   This program is free software; you can redistribute it and/or modify
+ *   it under the terms of the GNU General Public License as published by
+ *   the Free Software Foundation; either version 2 of the License, or
+ *   (at your option) any later version.
+ *
+ *   This program is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *   GNU General Public License for more details.
+ *
+ *   You should have received a copy of the GNU General Public License
+ *   along with this program; if not, write to the Free Software
+ *   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#ifndef	Hamming_H
+#define	Hamming_H
+
+class CHamming {
+public:
+    static void encode15113_1(bool* d);
+    static bool decode15113_1(bool* d);
+    
+    static void encode15113_2(bool* d);
+    static bool decode15113_2(bool* d);
+    
+    static void encode1393(bool* d);
+    static bool decode1393(bool* d);
+    
+    static void encode1063(bool* d);
+    static bool decode1063(bool* d);
+    
+    static void encode16114(bool* d);
+    static bool decode16114(bool* d);
+    
+    static void encode17123(bool* d);
+    static bool decode17123(bool* d);
+};
+
+#endif
diff --git a/src/cip.h b/src/cip.h
index 3a65fd6..6c43288 100644
--- a/src/cip.h
+++ b/src/cip.h
@@ -44,7 +44,11 @@ public:
     
     // sockaddr
     void SetSockAddr(struct sockaddr_in *);
-    struct sockaddr_in *GetSockAddr(void)       { return &m_Addr; }
+    struct sockaddr_in *GetSockAddr(void)     { return &m_Addr; }
+    
+    // convertor
+    uint32 GetAddr(void) const                { return m_Addr.sin_addr.s_addr; }
+    uint16 GetPort(void) const                { return m_Addr.sin_port; }
     
     // operator
     bool operator ==(const CIp &) const;
diff --git a/src/cpacket.cpp b/src/cpacket.cpp
index f6b9154..b93d603 100644
--- a/src/cpacket.cpp
+++ b/src/cpacket.cpp
@@ -25,21 +25,36 @@
 #include "main.h"
 #include "cpacket.h"
 
+
+
 ////////////////////////////////////////////////////////////////////////////////////////
 // constructor
 
 CPacket::CPacket()
 {
     m_uiStreamId = 0;
-    m_uiPacketId = 0;
+    m_uiDstarPacketId = 0;
+    m_uiDmrPacketId = 0;
     m_uiModuleId = ' ';
     m_uiOriginId = ORIGIN_LOCAL;
 };
 
-CPacket::CPacket(uint16 sid, uint8 pid)
+CPacket::CPacket(uint16 sid, uint8 dstarpid)
 {
     m_uiStreamId = sid;
-    m_uiPacketId = pid;
+    m_uiDstarPacketId = dstarpid;
+    m_uiDmrPacketId = 0xFF;
+    m_uiDmrPacketSubid  = 0xFF;
+    m_uiModuleId = ' ';
+    m_uiOriginId = ORIGIN_LOCAL;
+};
+
+CPacket::CPacket(uint16 sid, uint8 dmrpid, uint8 dmrspid)
+{
+    m_uiStreamId = sid;
+    m_uiDmrPacketId = dmrpid;
+    m_uiDmrPacketSubid = dmrspid;
+    m_uiDstarPacketId = 0xFF;
     m_uiModuleId = ' ';
     m_uiOriginId = ORIGIN_LOCAL;
 };
@@ -51,3 +66,27 @@ CPacket *CPacket::Duplicate(void) const
 {
     return new CPacket(*this);
 }
+
+
+////////////////////////////////////////////////////////////////////////////////////////
+// pid conversion
+
+void CPacket::UpdatePids(uint32 pid)
+{
+    // called while phusing this packet in a stream queue
+    // so now packet sequence number is known and undefined pids can be updated
+    // this is needed as dtsar & dmt pids are different and cannot be
+    // derived from each other
+    
+    // dstar pid needs update ?
+    if (  m_uiDstarPacketId ==  0xFF )
+    {
+        m_uiDstarPacketId = (pid % 21);
+    }
+    // dmr pids need update ?
+    if ( m_uiDmrPacketId == 0xFF )
+    {
+        m_uiDmrPacketId = ((pid / 3) % 6);
+        m_uiDmrPacketSubid = ((pid % 3) + 1);
+    }
+}
diff --git a/src/cpacket.h b/src/cpacket.h
index 0f6d46f..b762896 100644
--- a/src/cpacket.h
+++ b/src/cpacket.h
@@ -40,7 +40,8 @@ class CPacket
 public:
     // constructor
     CPacket();
-    CPacket(uint16 sid, uint8 pid);
+    CPacket(uint16 sid, uint8 dstarpid);
+    CPacket(uint16 sid, uint8 dmrpid, uint8 dmrsubpid);
     
     // destructor
     virtual ~CPacket() {};
@@ -49,26 +50,33 @@ public:
     virtual CPacket *Duplicate(void) const;
     
     // identity
-    virtual bool IsDvHeader(void) const     { return false; }
-    virtual bool IsDvFrame(void) const      { return false; }
-    virtual bool IsLastPacket(void) const   { return false; }
+    virtual bool IsDvHeader(void) const             { return false; }
+    virtual bool IsDvFrame(void) const              { return false; }
+    virtual bool IsLastPacket(void) const           { return false; }
+    virtual bool HaveTranscodableAmbe(void) const   { return false; }
     
     // get
-    virtual bool IsValid(void) const        { return true; }
-    uint16 GetStreamId(void) const          { return m_uiStreamId; }
-    uint8  GetPacketId(void) const          { return m_uiPacketId; }
-    uint8  GetModuleId(void) const          { return m_uiModuleId; }
-    bool   IsLocalOrigin(void) const        { return (m_uiOriginId == ORIGIN_LOCAL); }
+    virtual bool IsValid(void) const                { return true; }
+    uint16 GetStreamId(void) const                  { return m_uiStreamId; }
+    uint8  GetPacketId(void) const                  { return m_uiDstarPacketId; }
+    uint8  GetDstarPacketId(void) const             { return m_uiDstarPacketId; }
+    uint8  GetDmrPacketId(void) const               { return m_uiDmrPacketId; }
+    uint8  GetDmrPacketSubid(void) const            { return m_uiDmrPacketSubid; }
+    uint8  GetModuleId(void) const                  { return m_uiModuleId; }
+    bool   IsLocalOrigin(void) const                { return (m_uiOriginId == ORIGIN_LOCAL); }
     
     // set
-    void SetModuleId(uint8 uiId)            { m_uiModuleId = uiId; }
-    void SetLocalOrigin(void)               { m_uiOriginId = ORIGIN_LOCAL; }
-    void SetRemotePeerOrigin(void)          { m_uiOriginId = ORIGIN_PEER; }
-
+    void UpdatePids(uint32);
+    void SetModuleId(uint8 uiId)                    { m_uiModuleId = uiId; }
+    void SetLocalOrigin(void)                       { m_uiOriginId = ORIGIN_LOCAL; }
+    void SetRemotePeerOrigin(void)                  { m_uiOriginId = ORIGIN_PEER; }
+    
 protected:
     // data
     uint16  m_uiStreamId;
-    uint8   m_uiPacketId;
+    uint8   m_uiDstarPacketId;
+    uint8   m_uiDmrPacketId;
+    uint8   m_uiDmrPacketSubid;
     uint8   m_uiModuleId;
     uint8   m_uiOriginId;
 };
diff --git a/src/cpacketqueue.h b/src/cpacketqueue.h
index 818dbd3..7f6a217 100644
--- a/src/cpacketqueue.h
+++ b/src/cpacketqueue.h
@@ -33,6 +33,8 @@
 ////////////////////////////////////////////////////////////////////////////////////////
 // CPacketQueue
 
+class CClient;
+
 class CPacketQueue : public std::queue<CPacket *>
 {
 public:
diff --git a/src/cpacketstream.cpp b/src/cpacketstream.cpp
index 0ad2a64..27776b7 100644
--- a/src/cpacketstream.cpp
+++ b/src/cpacketstream.cpp
@@ -32,7 +32,9 @@ CPacketStream::CPacketStream()
 {
     m_bOpen = false;
     m_uiStreamId = 0;
+    m_uiPacketCntr = 0;
     m_OwnerClient = NULL;
+    m_CodecStream = NULL;
 }
 
 ////////////////////////////////////////////////////////////////////////////////////////
@@ -48,9 +50,11 @@ bool CPacketStream::Open(const CDvHeaderPacket &DvHeader, CClient *client)
         // update status
         m_bOpen = true;
         m_uiStreamId = DvHeader.GetStreamId();
+        m_uiPacketCntr = 0;
         m_DvHeader = DvHeader;
         m_OwnerClient = client;
         m_LastPacketTime.Now();
+        m_CodecStream = g_Transcoder.GetStream(this, client->GetCodec());
         ok = true;
     }
     return ok;
@@ -62,6 +66,8 @@ void CPacketStream::Close(void)
     m_bOpen = false;
     m_uiStreamId = 0;
     m_OwnerClient = NULL;
+    g_Transcoder.ReleaseStream(m_CodecStream);
+    m_CodecStream = NULL;
 }
 
 ////////////////////////////////////////////////////////////////////////////////////////
@@ -69,8 +75,50 @@ void CPacketStream::Close(void)
 
 void CPacketStream::Push(CPacket *Packet)
 {
+    // update stream dependent packet data
     m_LastPacketTime.Now();
-    push(Packet);
+    Packet->UpdatePids(m_uiPacketCntr++);
+    // transcoder avaliable ?
+    if ( m_CodecStream != NULL )
+    {
+        // todo: verify no possibilty of double lock here
+        m_CodecStream->Lock();
+        {
+            // transcoder ready & frame need transcoding ?
+            if ( m_CodecStream->IsConnected() && Packet->HaveTranscodableAmbe() )
+            {
+                // yes, push packet to trancoder queue
+                // trancoder will push it after transcoding
+                // is completed
+                m_CodecStream->push(Packet);
+            }
+            else
+            {
+                // no, just bypass tarnscoder
+                push(Packet);
+            }
+        }
+        m_CodecStream->Unlock();
+    }
+    else
+    {
+        // otherwise, push direct push
+        push(Packet);
+    }
+}
+
+bool CPacketStream::IsEmpty(void) const
+{
+    bool bEmpty = empty();
+    
+    // also check no packets still in Codec stream's queue
+    if ( bEmpty && (m_CodecStream != NULL) )
+    {
+        bEmpty &= m_CodecStream->IsEmpty();
+    }
+
+    // done
+    return bEmpty;
 }
 
 ////////////////////////////////////////////////////////////////////////////////////////
diff --git a/src/cpacketstream.h b/src/cpacketstream.h
index a60a4bc..c910158 100644
--- a/src/cpacketstream.h
+++ b/src/cpacketstream.h
@@ -28,10 +28,12 @@
 #include "cpacketqueue.h"
 #include "ctimepoint.h"
 #include "cdvheaderpacket.h"
+#include "ctranscoder.h"
 
 ////////////////////////////////////////////////////////////////////////////////////////
 
-#define STREAM_TIMEOUT      (0.200)
+//#define STREAM_TIMEOUT      (0.600)
+#define STREAM_TIMEOUT      (1.600)
 
 ////////////////////////////////////////////////////////////////////////////////////////
 // class
@@ -52,6 +54,7 @@ public:
     // push & pop
     void Push(CPacket *);
     void Tickle(void)                               { m_LastPacketTime.Now(); }
+    bool IsEmpty(void) const;
     
     // get
     CClient         *GetOwnerClient(void)           { return m_OwnerClient; }
@@ -65,9 +68,11 @@ protected:
     // data
     bool                m_bOpen;
     uint16              m_uiStreamId;
+    uint32              m_uiPacketCntr;
     CClient             *m_OwnerClient;
     CTimePoint          m_LastPacketTime;
     CDvHeaderPacket     m_DvHeader;
+    CCodecStream        *m_CodecStream;
 };
 
 ////////////////////////////////////////////////////////////////////////////////////////
diff --git a/src/cpeers.cpp b/src/cpeers.cpp
index 35c5547..3a27282 100644
--- a/src/cpeers.cpp
+++ b/src/cpeers.cpp
@@ -85,7 +85,7 @@ void CPeers::AddPeer(CPeer *peer)
         // append peer to reflector peer list
         m_Peers.push_back(peer);
         std::cout << "New peer " << peer->GetCallsign() << " at " << peer->GetIp()
-                  << " added with protocol " << peer->GetProtocolName()  << std::endl;
+                  << " added with protocol " << peer->GetProtocol()  << std::endl;
         // and append all peer's client to reflector client list
         // it is double lock safe to lock Clients list after Peers list
         CClients *clients = g_Reflector.GetClients();
diff --git a/src/cprotocol.cpp b/src/cprotocol.cpp
index 2cb3b58..a5d2adb 100644
--- a/src/cprotocol.cpp
+++ b/src/cprotocol.cpp
@@ -140,7 +140,7 @@ void CProtocol::OnDvFramePacketIn(CDvFramePacket *Frame, const CIp *Ip)
     CPacketStream *stream = GetStream(Frame->GetStreamId(), Ip);
     if ( stream != NULL )
     {
-        //std::cout << "DV frame" << std::endl;
+        //std::cout << "DV frame" << "from "  << *Ip << std::endl;
         // and push
         stream->Lock();
         stream->Push(Frame);
@@ -241,4 +241,17 @@ bool CProtocol::IsSpace(char c) const
     return (c == ' ');
 }
 
+////////////////////////////////////////////////////////////////////////////////////////
+// DestId to Module helper
+
+char CProtocol::DmrDstIdToModule(uint32 tg) const
+{
+    return ((char)((tg % 26)-1) + 'A');
+}
+
+uint32 CProtocol::ModuleToDmrDestId(char m) const
+{
+    return (uint32)(m - 'A')+1;
+}
+
 
diff --git a/src/cprotocol.h b/src/cprotocol.h
index 265bec8..60ad0dc 100644
--- a/src/cprotocol.h
+++ b/src/cprotocol.h
@@ -33,6 +33,35 @@
 
 ////////////////////////////////////////////////////////////////////////////////////////
 
+// DMR defines
+// slot n'
+#define DMR_SLOT1                       1
+#define DMR_SLOT2                       2
+// call type
+#define DMR_GROUP_CALL                  0
+#define DMR_PRIVATE_CALL                1
+// frame type
+#define DMR_FRAMETYPE_VOICE             0
+#define DMR_FRAMETYPE_VOICESYNC         1
+#define DMR_FRAMETYPE_DATA              2
+#define DMR_FRAMETYPE_DATASYNC          3
+// data type
+#define DMR_DT_VOICE_PI_HEADER          0
+#define DMR_DT_VOICE_LC_HEADER          1
+#define DMR_DT_TERMINATOR_WITH_LC       2
+#define DMR_DT_CSBK                     3
+#define DMR_DT_DATA_HEADER              6
+#define DMR_DT_RATE_12_DATA             7
+#define DMR_DT_RATE_34_DATA             8
+#define DMR_DT_IDLE                     9
+#define DMR_DT_RATE_1_DATA              10
+// CRC masks
+#define DMR_VOICE_LC_HEADER_CRC_MASK    0x96
+#define DMR_TERMINATOR_WITH_LC_CRC_MASK 0x99
+#define DMR_PI_HEADER_CRC_MASK          0x69
+#define DMR_DATA_HEADER_CRC_MASK        0xCC
+#define DMR_CSBK_CRC_MASK               0xA5
+
 
 ////////////////////////////////////////////////////////////////////////////////////////
 // class
@@ -87,6 +116,10 @@ protected:
     bool IsNumber(char) const;
     bool IsLetter(char) const;
     bool IsSpace(char) const;
+    
+    // dmr DstId to Module helper
+    virtual char DmrDstIdToModule(uint32) const;
+    virtual uint32 ModuleToDmrDestId(char) const;
 
 protected:
     // socket
@@ -104,6 +137,9 @@ protected:
     
     // identity
     CCallsign       m_ReflectorCallsign;
+    
+    // debug
+    CTimePoint          m_DebugTimer;
 };
 
 ////////////////////////////////////////////////////////////////////////////////////////
diff --git a/src/cprotocols.cpp b/src/cprotocols.cpp
index 341cc97..53f5499 100644
--- a/src/cprotocols.cpp
+++ b/src/cprotocols.cpp
@@ -27,6 +27,8 @@
 #include "cdplusprotocol.h"
 #include "cdcsprotocol.h"
 #include "cxlxprotocol.h"
+#include "cdmrplusprotocol.h"
+#include "cdmrmmdvmprotocol.h"
 #include "cprotocols.h"
 
 
@@ -84,6 +86,16 @@ bool CProtocols::Init(void)
         delete m_Protocols[3];
         m_Protocols[3] = new CXlxProtocol;
         ok &= m_Protocols[3]->Init();
+        
+        // create and initialize DMRPLUS
+        delete m_Protocols[4];
+        m_Protocols[4] = new CDmrplusProtocol;
+        ok &= m_Protocols[4]->Init();
+        
+        // create and initialize DMRMMDVM
+        delete m_Protocols[5];
+        m_Protocols[5] = new CDmrmmdvmProtocol;
+        ok &= m_Protocols[5]->Init();
     }
     m_Mutex.unlock();
    
diff --git a/src/cqr1676.cpp b/src/cqr1676.cpp
new file mode 100644
index 0000000..a05ea3e
--- /dev/null
+++ b/src/cqr1676.cpp
@@ -0,0 +1,114 @@
+/*
+ *   Copyright (C) 2015,2016 by Jonathan Naylor G4KLX
+ *
+ *   This program is free software; you can redistribute it and/or modify
+ *   it under the terms of the GNU General Public License as published by
+ *   the Free Software Foundation; either version 2 of the License, or
+ *   (at your option) any later version.
+ *
+ *   This program is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *   GNU General Public License for more details.
+ *
+ *   You should have received a copy of the GNU General Public License
+ *   along with this program; if not, write to the Free Software
+ *   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include "cqr1676.h"
+
+#include <cstdio>
+#include <cassert>
+
+const unsigned int ENCODING_TABLE_1676[] =
+{0x0000U, 0x0273U, 0x04E5U, 0x0696U, 0x09C9U, 0x0BBAU, 0x0D2CU, 0x0F5FU, 0x11E2U, 0x1391U, 0x1507U, 0x1774U,
+    0x182BU, 0x1A58U, 0x1CCEU, 0x1EBDU, 0x21B7U, 0x23C4U, 0x2552U, 0x2721U, 0x287EU, 0x2A0DU, 0x2C9BU, 0x2EE8U,
+    0x3055U, 0x3226U, 0x34B0U, 0x36C3U, 0x399CU, 0x3BEFU, 0x3D79U, 0x3F0AU, 0x411EU, 0x436DU, 0x45FBU, 0x4788U,
+    0x48D7U, 0x4AA4U, 0x4C32U, 0x4E41U, 0x50FCU, 0x528FU, 0x5419U, 0x566AU, 0x5935U, 0x5B46U, 0x5DD0U, 0x5FA3U,
+    0x60A9U, 0x62DAU, 0x644CU, 0x663FU, 0x6960U, 0x6B13U, 0x6D85U, 0x6FF6U, 0x714BU, 0x7338U, 0x75AEU, 0x77DDU,
+    0x7882U, 0x7AF1U, 0x7C67U, 0x7E14U, 0x804FU, 0x823CU, 0x84AAU, 0x86D9U, 0x8986U, 0x8BF5U, 0x8D63U, 0x8F10U,
+    0x91ADU, 0x93DEU, 0x9548U, 0x973BU, 0x9864U, 0x9A17U, 0x9C81U, 0x9EF2U, 0xA1F8U, 0xA38BU, 0xA51DU, 0xA76EU,
+    0xA831U, 0xAA42U, 0xACD4U, 0xAEA7U, 0xB01AU, 0xB269U, 0xB4FFU, 0xB68CU, 0xB9D3U, 0xBBA0U, 0xBD36U, 0xBF45U,
+    0xC151U, 0xC322U, 0xC5B4U, 0xC7C7U, 0xC898U, 0xCAEBU, 0xCC7DU, 0xCE0EU, 0xD0B3U, 0xD2C0U, 0xD456U, 0xD625U,
+    0xD97AU, 0xDB09U, 0xDD9FU, 0xDFECU, 0xE0E6U, 0xE295U, 0xE403U, 0xE670U, 0xE92FU, 0xEB5CU, 0xEDCAU, 0xEFB9U,
+    0xF104U, 0xF377U, 0xF5E1U, 0xF792U, 0xF8CDU, 0xFABEU, 0xFC28U, 0xFE5BU};
+
+const unsigned int DECODING_TABLE_1576[] =
+{0x0000U, 0x0001U, 0x0002U, 0x0003U, 0x0004U, 0x0005U, 0x0006U, 0x4020U, 0x0008U, 0x0009U, 0x000AU, 0x000BU,
+    0x000CU, 0x000DU, 0x2081U, 0x2080U, 0x0010U, 0x0011U, 0x0012U, 0x0013U, 0x0014U, 0x0C00U, 0x0016U, 0x0C02U,
+    0x0018U, 0x0120U, 0x001AU, 0x0122U, 0x4102U, 0x0124U, 0x4100U, 0x4101U, 0x0020U, 0x0021U, 0x0022U, 0x4004U,
+    0x0024U, 0x4002U, 0x4001U, 0x4000U, 0x0028U, 0x0110U, 0x1800U, 0x1801U, 0x002CU, 0x400AU, 0x4009U, 0x4008U,
+    0x0030U, 0x0108U, 0x0240U, 0x0241U, 0x0034U, 0x4012U, 0x4011U, 0x4010U, 0x0101U, 0x0100U, 0x0103U, 0x0102U,
+    0x0105U, 0x0104U, 0x1401U, 0x1400U, 0x0040U, 0x0041U, 0x0042U, 0x0043U, 0x0044U, 0x0045U, 0x0046U, 0x4060U,
+    0x0048U, 0x0049U, 0x0301U, 0x0300U, 0x004CU, 0x1600U, 0x0305U, 0x0304U, 0x0050U, 0x0051U, 0x0220U, 0x0221U,
+    0x3000U, 0x4200U, 0x3002U, 0x4202U, 0x0058U, 0x1082U, 0x1081U, 0x1080U, 0x3008U, 0x4208U, 0x2820U, 0x1084U,
+    0x0060U, 0x0061U, 0x0210U, 0x0211U, 0x0480U, 0x0481U, 0x4041U, 0x4040U, 0x0068U, 0x2402U, 0x2401U, 0x2400U,
+    0x0488U, 0x3100U, 0x2810U, 0x2404U, 0x0202U, 0x0880U, 0x0200U, 0x0201U, 0x0206U, 0x0884U, 0x0204U, 0x0205U,
+    0x0141U, 0x0140U, 0x0208U, 0x0209U, 0x2802U, 0x0144U, 0x2800U, 0x2801U, 0x0080U, 0x0081U, 0x0082U, 0x0A00U,
+    0x0084U, 0x0085U, 0x2009U, 0x2008U, 0x0088U, 0x0089U, 0x2005U, 0x2004U, 0x2003U, 0x2002U, 0x2001U, 0x2000U,
+    0x0090U, 0x0091U, 0x0092U, 0x1048U, 0x0602U, 0x0C80U, 0x0600U, 0x0601U, 0x0098U, 0x1042U, 0x1041U, 0x1040U,
+    0x2013U, 0x2012U, 0x2011U, 0x2010U, 0x00A0U, 0x00A1U, 0x00A2U, 0x4084U, 0x0440U, 0x0441U, 0x4081U, 0x4080U,
+    0x6000U, 0x1200U, 0x6002U, 0x1202U, 0x6004U, 0x2022U, 0x2021U, 0x2020U, 0x0841U, 0x0840U, 0x2104U, 0x0842U,
+    0x2102U, 0x0844U, 0x2100U, 0x2101U, 0x0181U, 0x0180U, 0x0B00U, 0x0182U, 0x5040U, 0x0184U, 0x2108U, 0x2030U,
+    0x00C0U, 0x00C1U, 0x4401U, 0x4400U, 0x0420U, 0x0421U, 0x0422U, 0x4404U, 0x0900U, 0x0901U, 0x1011U, 0x1010U,
+    0x0904U, 0x2042U, 0x2041U, 0x2040U, 0x0821U, 0x0820U, 0x1009U, 0x1008U, 0x4802U, 0x0824U, 0x4800U, 0x4801U,
+    0x1003U, 0x1002U, 0x1001U, 0x1000U, 0x0501U, 0x0500U, 0x1005U, 0x1004U, 0x0404U, 0x0810U, 0x1100U, 0x1101U,
+    0x0400U, 0x0401U, 0x0402U, 0x0403U, 0x040CU, 0x0818U, 0x1108U, 0x1030U, 0x0408U, 0x0409U, 0x040AU, 0x2060U,
+    0x0801U, 0x0800U, 0x0280U, 0x0802U, 0x0410U, 0x0804U, 0x0412U, 0x0806U, 0x0809U, 0x0808U, 0x1021U, 0x1020U,
+    0x5000U, 0x2200U, 0x5002U, 0x2202U};
+
+#define X14             0x00004000   /* vector representation of X^{14} */
+#define X8              0x00000100   /* vector representation of X^{8} */
+#define MASK7           0xffffff00   /* auxiliary vector for testing */
+#define GENPOL          0x00000139   /* generator polinomial, g(x) */
+
+unsigned int CQR1676::getSyndrome1576(unsigned int pattern)
+/*
+ * Compute the syndrome corresponding to the given pattern, i.e., the
+ * remainder after dividing the pattern (when considering it as the vector
+ * representation of a polynomial) by the generator polynomial, GENPOL.
+ * In the program this pattern has several meanings: (1) pattern = infomation
+ * bits, when constructing the encoding table; (2) pattern = error pattern,
+ * when constructing the decoding table; and (3) pattern = received vector, to
+ * obtain its syndrome in decoding.
+ */
+{
+    unsigned int aux = X14;
+    
+    if (pattern >= X8) {
+        while (pattern & MASK7) {
+            while (!(aux & pattern))
+                aux = aux >> 1;
+            
+            pattern ^= (aux / X8) * GENPOL;
+        }
+    }
+    
+    return pattern;
+}
+
+// Compute the EMB against a precomputed list of correct words
+void CQR1676::encode(unsigned char* data)
+{
+    assert(data != NULL);
+    
+    unsigned int value = (data[0U] >> 1) & 0x7FU;
+    unsigned int cksum = ENCODING_TABLE_1676[value];
+    
+    data[0U] = cksum >> 8;
+    data[1U] = cksum & 0xFFU;
+}
+
+unsigned char CQR1676::decode(const unsigned char* data)
+{
+    assert(data != NULL);
+    
+    unsigned int code = (data[0U] << 7) + (data[1U] >> 1);
+    unsigned int syndrome = getSyndrome1576(code);
+    unsigned int error_pattern = DECODING_TABLE_1576[syndrome];
+    
+    code ^= error_pattern;
+    
+    return code >> 7;
+}
diff --git a/src/cqr1676.h b/src/cqr1676.h
new file mode 100644
index 0000000..59dd524
--- /dev/null
+++ b/src/cqr1676.h
@@ -0,0 +1,32 @@
+/*
+ *   Copyright (C) 2015 by Jonathan Naylor G4KLX
+ *
+ *   This program is free software; you can redistribute it and/or modify
+ *   it under the terms of the GNU General Public License as published by
+ *   the Free Software Foundation; either version 2 of the License, or
+ *   (at your option) any later version.
+ *
+ *   This program is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *   GNU General Public License for more details.
+ *
+ *   You should have received a copy of the GNU General Public License
+ *   along with this program; if not, write to the Free Software
+ *   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#ifndef QR1676_H
+#define QR1676_H
+
+class CQR1676 {
+public:
+    static void encode(unsigned char* data);
+    
+    static unsigned char decode(const unsigned char* data);
+    
+private:
+    static unsigned int getSyndrome1576(unsigned int pattern);
+};
+
+#endif
diff --git a/src/creflector.cpp b/src/creflector.cpp
index 20e186c..9ede4fc 100644
--- a/src/creflector.cpp
+++ b/src/creflector.cpp
@@ -26,7 +26,8 @@
 #include <string.h>
 #include "creflector.h"
 #include "cgatekeeper.h"
-
+#include "cdmriddir.h"
+#include "ctranscoder.h"
 
 ////////////////////////////////////////////////////////////////////////////////////////
 // constructor
@@ -40,10 +41,16 @@ CReflector::CReflector()
     {
         m_RouterThreads[i] = NULL;
     }
+#ifdef DEBUG_DUMPFILE
+    m_DebugFile.open("/Users/jeanluc/Desktop/dmrdebug.txt");
+#endif
 }
 
 CReflector::CReflector(const CCallsign &callsign)
 {
+#ifdef DEBUG_DUMPFILE
+    m_DebugFile.close();
+#endif
     m_bStopThreads = false;
     m_XmlReportThread = NULL;
     m_JsonReportThread = NULL;
@@ -93,7 +100,13 @@ bool CReflector::Start(void)
 
     // init gate keeper
     ok &= g_GateKeeper.Init();
-
+    
+    // init dmrid directory
+    g_DmridDir.RefreshContent();
+    
+    // init the transcoder
+    g_Transcoder.Init();
+    
     // create protocols
     ok &= m_Protocols.Init();
     
@@ -116,7 +129,7 @@ bool CReflector::Start(void)
     {
         m_Protocols.Close();
     }
-
+    
     // done
     return ok;
 }
@@ -154,6 +167,9 @@ void CReflector::Stop(void)
     // close protocols
     m_Protocols.Close();
 
+    // close transcoder
+    g_Transcoder.Close();
+    
     // close gatekeeper
     g_GateKeeper.Close();
 }
@@ -169,11 +185,11 @@ bool CReflector::IsStreaming(char module)
 CPacketStream *CReflector::OpenStream(CDvHeaderPacket *DvHeader, CClient *client)
 {
     CPacketStream *retStream = NULL;
-
+    
     // clients MUST have bee locked by the caller
     // so we can freely access it within the fuction
-
-    // check if streamid is valid
+    
+    // check sid is not NULL
     if ( DvHeader->GetStreamId() != 0 )
     {
         // check if client is valid candidate
@@ -196,21 +212,21 @@ CPacketStream *CReflector::OpenStream(CDvHeaderPacket *DvHeader, CClient *client
                         // stream open, mark client as master
                         // so that it can't be deleted
                         client->SetMasterOfModule(module);
-
+                        
                         // update last heard time
                         client->Heard();
                         retStream = stream;
-
+                        
                         // and push header packet
                         stream->Push(DvHeader);
-
+                        
                         // report
                         std::cout << "Opening stream on module " << module << " for client " << client->GetCallsign()
                                   << " with sid " << DvHeader->GetStreamId() << std::endl;
-
+                        
                         // notify
                         g_Reflector.OnStreamOpen(stream->GetUserCallsign());
-
+                        
                     }
                     // unlock now
                     stream->Unlock();
@@ -224,12 +240,7 @@ CPacketStream *CReflector::OpenStream(CDvHeaderPacket *DvHeader, CClient *client
             }
         }
     }
-    else
-    {
-        // report
-        std::cout << "Detected null stream id for client " << client->GetCallsign() << std::endl;
-    }
-
+    
     // done
     return retStream;
 }
@@ -240,11 +251,14 @@ void CReflector::CloseStream(CPacketStream *stream)
     if ( stream != NULL )
     {
         // wait queue is empty
-        // the following waut's forever
+        // this waits forever
         bool bEmpty = false;
         do
         {
             stream->Lock();
+            // do not use stream->IsEmpty() has this "may" never succeed
+            // and anyway, the DvLastFramPacket short-circuit the transcoder
+            // loop queues
             bEmpty = stream->empty();
             stream->Unlock();
             if ( !bEmpty )
@@ -253,12 +267,12 @@ void CReflector::CloseStream(CPacketStream *stream)
                 CTimePoint::TaskSleepFor(10);
             }
         } while (!bEmpty);
-
-        // lock it
-        stream->Lock();
-
+        
         // lock clients
         GetClients();
+        
+        // lock stream
+        stream->Lock();
 
         // get and check the master
         CClient *client = stream->GetOwnerClient();
@@ -273,15 +287,18 @@ void CReflector::CloseStream(CPacketStream *stream)
             std::cout << "Closing stream of module " << GetStreamModule(stream) << std::endl;
         }
 
-        // stop the queue
+        // release clients
+        ReleaseClients();
+        
+        // unlock before closing
+        // to avoid double lock in assiociated
+        // codecstream close/thread-join
+        stream->Unlock();
+        
+        // and stop the queue
         stream->Close();
 
 
-        // release clients
-        ReleaseClients();
-
-        // and unlock
-        stream->Unlock();
     }
 }
 
@@ -345,9 +362,11 @@ void CReflector::RouterThread(CReflector *This, CPacketStream *streamIn)
             delete packet;
             packet = NULL;
         }
-
-        // wait a bit
-        CTimePoint::TaskSleepFor(10);
+        else
+        {
+            // wait a bit
+            CTimePoint::TaskSleepFor(10);
+        }
     }
 }
 
@@ -576,12 +595,12 @@ void CReflector::WriteXmlFile(std::ofstream &xmlFile)
 {
     // write header
     xmlFile << "<?xml version=\"1.0\" encoding=\"UTF-8\"?>" << std::endl;
-
+    
     // software version
     char sz[64];
     ::sprintf(sz, "%d.%d.%d", VERSION_MAJOR, VERSION_MINOR, VERSION_REVISION);
     xmlFile << "<Version>" << sz << "</Version>" << std::endl;
-
+    
     // linked peers
     xmlFile << "<" << m_Callsign << "linked peers>" << std::endl;
     // lock
@@ -610,7 +629,7 @@ void CReflector::WriteXmlFile(std::ofstream &xmlFile)
     // unlock
     ReleaseClients();
     xmlFile << "</" << m_Callsign << "linked nodes>" << std::endl;
-
+    
     // last heard users
     xmlFile << "<" << m_Callsign << "heard users>" << std::endl;
     // lock
diff --git a/src/creflector.h b/src/creflector.h
index 51cb18d..8c9a9d0 100644
--- a/src/creflector.h
+++ b/src/creflector.h
@@ -58,7 +58,9 @@ public:
     void SetCallsign(const CCallsign &callsign)     { m_Callsign = callsign; }
     const CCallsign &GetCallsign(void) const        { return m_Callsign; }
     void SetListenIp(const CIp &ip)                 { m_Ip = ip; }
+    void SetTranscoderIp(const CIp &ip)             { m_AmbedIp = ip; }
     const CIp &GetListenIp(void) const              { return m_Ip; }
+    const CIp &GetTrasncoderIp(void) const          { return m_AmbedIp; }
     
     // operation
     bool Start(void);
@@ -118,6 +120,7 @@ protected:
     // identity
     CCallsign       m_Callsign;
     CIp             m_Ip;
+    CIp             m_AmbedIp;
     
     // objects
     CUsers          m_Users;            // sorted list of lastheard stations
@@ -136,6 +139,11 @@ protected:
     
     // notifications
     CNotificationQueue  m_Notifications;
+    
+public:
+#ifdef DEBUG_DUMPFILE
+    std::ofstream        m_DebugFile;
+#endif
 };
 
 ////////////////////////////////////////////////////////////////////////////////////////
diff --git a/src/crs129.cpp b/src/crs129.cpp
new file mode 100644
index 0000000..182a438
--- /dev/null
+++ b/src/crs129.cpp
@@ -0,0 +1,130 @@
+/*
+ *   Copyright (C) 2015 by Jonathan Naylor G4KLX
+ *
+ *   This program is free software; you can redistribute it and/or modify
+ *   it under the terms of the GNU General Public License as published by
+ *   the Free Software Foundation; either version 2 of the License, or
+ *   (at your option) any later version.
+ *
+ *   This program is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *   GNU General Public License for more details.
+ *
+ *   You should have received a copy of the GNU General Public License
+ *   along with this program; if not, write to the Free Software
+ *   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include "crs129.h"
+
+#include <cstdio>
+#include <cassert>
+#include <cstring>
+
+const unsigned int NPAR = 3U;
+
+/* Maximum degree of various polynomials. */
+//const unsigned int MAXDEG = NPAR * 2U;
+
+/* Generator Polynomial */
+const unsigned char POLY[] = {64U, 56U, 14U, 1U, 0U, 0U, 0U, 0U, 0U, 0U, 0U, 0U};
+
+const unsigned char EXP_TABLE[] = {
+    0x01U, 0x02U, 0x04U, 0x08U, 0x10U, 0x20U, 0x40U, 0x80U, 0x1DU, 0x3AU, 0x74U, 0xE8U, 0xCDU, 0x87U, 0x13U, 0x26U,
+    0x4CU, 0x98U, 0x2DU, 0x5AU, 0xB4U, 0x75U, 0xEAU, 0xC9U, 0x8FU, 0x03U, 0x06U, 0x0CU, 0x18U, 0x30U, 0x60U, 0xC0U,
+    0x9DU, 0x27U, 0x4EU, 0x9CU, 0x25U, 0x4AU, 0x94U, 0x35U, 0x6AU, 0xD4U, 0xB5U, 0x77U, 0xEEU, 0xC1U, 0x9FU, 0x23U,
+    0x46U, 0x8CU, 0x05U, 0x0AU, 0x14U, 0x28U, 0x50U, 0xA0U, 0x5DU, 0xBAU, 0x69U, 0xD2U, 0xB9U, 0x6FU, 0xDEU, 0xA1U,
+    0x5FU, 0xBEU, 0x61U, 0xC2U, 0x99U, 0x2FU, 0x5EU, 0xBCU, 0x65U, 0xCAU, 0x89U, 0x0FU, 0x1EU, 0x3CU, 0x78U, 0xF0U,
+    0xFDU, 0xE7U, 0xD3U, 0xBBU, 0x6BU, 0xD6U, 0xB1U, 0x7FU, 0xFEU, 0xE1U, 0xDFU, 0xA3U, 0x5BU, 0xB6U, 0x71U, 0xE2U,
+    0xD9U, 0xAFU, 0x43U, 0x86U, 0x11U, 0x22U, 0x44U, 0x88U, 0x0DU, 0x1AU, 0x34U, 0x68U, 0xD0U, 0xBDU, 0x67U, 0xCEU,
+    0x81U, 0x1FU, 0x3EU, 0x7CU, 0xF8U, 0xEDU, 0xC7U, 0x93U, 0x3BU, 0x76U, 0xECU, 0xC5U, 0x97U, 0x33U, 0x66U, 0xCCU,
+    0x85U, 0x17U, 0x2EU, 0x5CU, 0xB8U, 0x6DU, 0xDAU, 0xA9U, 0x4FU, 0x9EU, 0x21U, 0x42U, 0x84U, 0x15U, 0x2AU, 0x54U,
+    0xA8U, 0x4DU, 0x9AU, 0x29U, 0x52U, 0xA4U, 0x55U, 0xAAU, 0x49U, 0x92U, 0x39U, 0x72U, 0xE4U, 0xD5U, 0xB7U, 0x73U,
+    0xE6U, 0xD1U, 0xBFU, 0x63U, 0xC6U, 0x91U, 0x3FU, 0x7EU, 0xFCU, 0xE5U, 0xD7U, 0xB3U, 0x7BU, 0xF6U, 0xF1U, 0xFFU,
+    0xE3U, 0xDBU, 0xABU, 0x4BU, 0x96U, 0x31U, 0x62U, 0xC4U, 0x95U, 0x37U, 0x6EU, 0xDCU, 0xA5U, 0x57U, 0xAEU, 0x41U,
+    0x82U, 0x19U, 0x32U, 0x64U, 0xC8U, 0x8DU, 0x07U, 0x0EU, 0x1CU, 0x38U, 0x70U, 0xE0U, 0xDDU, 0xA7U, 0x53U, 0xA6U,
+    0x51U, 0xA2U, 0x59U, 0xB2U, 0x79U, 0xF2U, 0xF9U, 0xEFU, 0xC3U, 0x9BU, 0x2BU, 0x56U, 0xACU, 0x45U, 0x8AU, 0x09U,
+    0x12U, 0x24U, 0x48U, 0x90U, 0x3DU, 0x7AU, 0xF4U, 0xF5U, 0xF7U, 0xF3U, 0xFBU, 0xEBU, 0xCBU, 0x8BU, 0x0BU, 0x16U,
+    0x2CU, 0x58U, 0xB0U, 0x7DU, 0xFAU, 0xE9U, 0xCFU, 0x83U, 0x1BU, 0x36U, 0x6CU, 0xD8U, 0xADU, 0x47U, 0x8EU, 0x01U,
+    0x02U, 0x04U, 0x08U, 0x10U, 0x20U, 0x40U, 0x80U, 0x1DU, 0x3AU, 0x74U, 0xE8U, 0xCDU, 0x87U, 0x13U, 0x26U, 0x4CU,
+    0x98U, 0x2DU, 0x5AU, 0xB4U, 0x75U, 0xEAU, 0xC9U, 0x8FU, 0x03U, 0x06U, 0x0CU, 0x18U, 0x30U, 0x60U, 0xC0U, 0x9DU,
+    0x27U, 0x4EU, 0x9CU, 0x25U, 0x4AU, 0x94U, 0x35U, 0x6AU, 0xD4U, 0xB5U, 0x77U, 0xEEU, 0xC1U, 0x9FU, 0x23U, 0x46U,
+    0x8CU, 0x05U, 0x0AU, 0x14U, 0x28U, 0x50U, 0xA0U, 0x5DU, 0xBAU, 0x69U, 0xD2U, 0xB9U, 0x6FU, 0xDEU, 0xA1U, 0x5FU,
+    0xBEU, 0x61U, 0xC2U, 0x99U, 0x2FU, 0x5EU, 0xBCU, 0x65U, 0xCAU, 0x89U, 0x0FU, 0x1EU, 0x3CU, 0x78U, 0xF0U, 0xFDU,
+    0xE7U, 0xD3U, 0xBBU, 0x6BU, 0xD6U, 0xB1U, 0x7FU, 0xFEU, 0xE1U, 0xDFU, 0xA3U, 0x5BU, 0xB6U, 0x71U, 0xE2U, 0xD9U,
+    0xAFU, 0x43U, 0x86U, 0x11U, 0x22U, 0x44U, 0x88U, 0x0DU, 0x1AU, 0x34U, 0x68U, 0xD0U, 0xBDU, 0x67U, 0xCEU, 0x81U,
+    0x1FU, 0x3EU, 0x7CU, 0xF8U, 0xEDU, 0xC7U, 0x93U, 0x3BU, 0x76U, 0xECU, 0xC5U, 0x97U, 0x33U, 0x66U, 0xCCU, 0x85U,
+    0x17U, 0x2EU, 0x5CU, 0xB8U, 0x6DU, 0xDAU, 0xA9U, 0x4FU, 0x9EU, 0x21U, 0x42U, 0x84U, 0x15U, 0x2AU, 0x54U, 0xA8U,
+    0x4DU, 0x9AU, 0x29U, 0x52U, 0xA4U, 0x55U, 0xAAU, 0x49U, 0x92U, 0x39U, 0x72U, 0xE4U, 0xD5U, 0xB7U, 0x73U, 0xE6U,
+    0xD1U, 0xBFU, 0x63U, 0xC6U, 0x91U, 0x3FU, 0x7EU, 0xFCU, 0xE5U, 0xD7U, 0xB3U, 0x7BU, 0xF6U, 0xF1U, 0xFFU, 0xE3U,
+    0xDBU, 0xABU, 0x4BU, 0x96U, 0x31U, 0x62U, 0xC4U, 0x95U, 0x37U, 0x6EU, 0xDCU, 0xA5U, 0x57U, 0xAEU, 0x41U, 0x82U,
+    0x19U, 0x32U, 0x64U, 0xC8U, 0x8DU, 0x07U, 0x0EU, 0x1CU, 0x38U, 0x70U, 0xE0U, 0xDDU, 0xA7U, 0x53U, 0xA6U, 0x51U,
+    0xA2U, 0x59U, 0xB2U, 0x79U, 0xF2U, 0xF9U, 0xEFU, 0xC3U, 0x9BU, 0x2BU, 0x56U, 0xACU, 0x45U, 0x8AU, 0x09U, 0x12U,
+    0x24U, 0x48U, 0x90U, 0x3DU, 0x7AU, 0xF4U, 0xF5U, 0xF7U, 0xF3U, 0xFBU, 0xEBU, 0xCBU, 0x8BU, 0x0BU, 0x16U, 0x2CU,
+    0x58U, 0xB0U, 0x7DU, 0xFAU, 0xE9U, 0xCFU, 0x83U, 0x1BU, 0x36U, 0x6CU, 0xD8U, 0xADU, 0x47U, 0x8EU, 0x01U, 0x00U};
+
+const unsigned char LOG_TABLE[] = {
+    0x00U, 0x00U, 0x01U, 0x19U, 0x02U, 0x32U, 0x1AU, 0xC6U, 0x03U, 0xDFU, 0x33U, 0xEEU, 0x1BU, 0x68U, 0xC7U, 0x4BU,
+    0x04U, 0x64U, 0xE0U, 0x0EU, 0x34U, 0x8DU, 0xEFU, 0x81U, 0x1CU, 0xC1U, 0x69U, 0xF8U, 0xC8U, 0x08U, 0x4CU, 0x71U,
+    0x05U, 0x8AU, 0x65U, 0x2FU, 0xE1U, 0x24U, 0x0FU, 0x21U, 0x35U, 0x93U, 0x8EU, 0xDAU, 0xF0U, 0x12U, 0x82U, 0x45U,
+    0x1DU, 0xB5U, 0xC2U, 0x7DU, 0x6AU, 0x27U, 0xF9U, 0xB9U, 0xC9U, 0x9AU, 0x09U, 0x78U, 0x4DU, 0xE4U, 0x72U, 0xA6U,
+    0x06U, 0xBFU, 0x8BU, 0x62U, 0x66U, 0xDDU, 0x30U, 0xFDU, 0xE2U, 0x98U, 0x25U, 0xB3U, 0x10U, 0x91U, 0x22U, 0x88U,
+    0x36U, 0xD0U, 0x94U, 0xCEU, 0x8FU, 0x96U, 0xDBU, 0xBDU, 0xF1U, 0xD2U, 0x13U, 0x5CU, 0x83U, 0x38U, 0x46U, 0x40U,
+    0x1EU, 0x42U, 0xB6U, 0xA3U, 0xC3U, 0x48U, 0x7EU, 0x6EU, 0x6BU, 0x3AU, 0x28U, 0x54U, 0xFAU, 0x85U, 0xBAU, 0x3DU,
+    0xCAU, 0x5EU, 0x9BU, 0x9FU, 0x0AU, 0x15U, 0x79U, 0x2BU, 0x4EU, 0xD4U, 0xE5U, 0xACU, 0x73U, 0xF3U, 0xA7U, 0x57U,
+    0x07U, 0x70U, 0xC0U, 0xF7U, 0x8CU, 0x80U, 0x63U, 0x0DU, 0x67U, 0x4AU, 0xDEU, 0xEDU, 0x31U, 0xC5U, 0xFEU, 0x18U,
+    0xE3U, 0xA5U, 0x99U, 0x77U, 0x26U, 0xB8U, 0xB4U, 0x7CU, 0x11U, 0x44U, 0x92U, 0xD9U, 0x23U, 0x20U, 0x89U, 0x2EU,
+    0x37U, 0x3FU, 0xD1U, 0x5BU, 0x95U, 0xBCU, 0xCFU, 0xCDU, 0x90U, 0x87U, 0x97U, 0xB2U, 0xDCU, 0xFCU, 0xBEU, 0x61U,
+    0xF2U, 0x56U, 0xD3U, 0xABU, 0x14U, 0x2AU, 0x5DU, 0x9EU, 0x84U, 0x3CU, 0x39U, 0x53U, 0x47U, 0x6DU, 0x41U, 0xA2U,
+    0x1FU, 0x2DU, 0x43U, 0xD8U, 0xB7U, 0x7BU, 0xA4U, 0x76U, 0xC4U, 0x17U, 0x49U, 0xECU, 0x7FU, 0x0CU, 0x6FU, 0xF6U,
+    0x6CU, 0xA1U, 0x3BU, 0x52U, 0x29U, 0x9DU, 0x55U, 0xAAU, 0xFBU, 0x60U, 0x86U, 0xB1U, 0xBBU, 0xCCU, 0x3EU, 0x5AU,
+    0xCBU, 0x59U, 0x5FU, 0xB0U, 0x9CU, 0xA9U, 0xA0U, 0x51U, 0x0BU, 0xF5U, 0x16U, 0xEBU, 0x7AU, 0x75U, 0x2CU, 0xD7U,
+    0x4FU, 0xAEU, 0xD5U, 0xE9U, 0xE6U, 0xE7U, 0xADU, 0xE8U, 0x74U, 0xD6U, 0xF4U, 0xEAU, 0xA8U, 0x50U, 0x58U, 0xAFU};
+
+/* multiplication using logarithms */
+static unsigned char gmult(unsigned char a, unsigned char b)
+{
+    if (a == 0U || b == 0U)
+        return 0U;
+    
+    unsigned int i = LOG_TABLE[a];
+    unsigned int j = LOG_TABLE[b];
+    
+    return EXP_TABLE[i + j];
+}
+
+/* Simulate a LFSR with generator polynomial for n byte RS code.
+ * Pass in a pointer to the data array, and amount of data.
+ *
+ * The parity bytes are deposited into parity.
+ */
+void CRS129::encode(const unsigned char* msg, unsigned int nbytes, unsigned char* parity)
+{
+    assert(msg != NULL);
+    assert(parity != NULL);
+    
+    for (unsigned int i = 0U; i < NPAR + 1U; i++)
+        parity[i] = 0x00U;
+    
+    for (unsigned int i = 0U; i < nbytes; i++) {
+        unsigned char dbyte = msg[i] ^ parity[NPAR - 1U];
+        
+        for (int j = NPAR - 1; j > 0; j--)
+            parity[j] = parity[j - 1] ^ ::gmult(POLY[j], dbyte);
+        
+        parity[0] = ::gmult(POLY[0], dbyte);
+    }
+}
+
+// Reed-Solomon (12,9) check
+bool CRS129::check(const unsigned char* in)
+{
+    assert(in != NULL);
+    
+    unsigned char parity[4U];
+    encode(in, 9U, parity);
+    
+    return in[9U] == parity[2U] && in[10U] == parity[1U] && in[11U] == parity[0U];
+}
+
diff --git a/src/crs129.h b/src/crs129.h
new file mode 100644
index 0000000..57fb848
--- /dev/null
+++ b/src/crs129.h
@@ -0,0 +1,30 @@
+/*
+ *   Copyright (C) 2015 by Jonathan Naylor G4KLX
+ *
+ *   This program is free software; you can redistribute it and/or modify
+ *   it under the terms of the GNU General Public License as published by
+ *   the Free Software Foundation; either version 2 of the License, or
+ *   (at your option) any later version.
+ *
+ *   This program is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *   GNU General Public License for more details.
+ *
+ *   You should have received a copy of the GNU General Public License
+ *   along with this program; if not, write to the Free Software
+ *   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#if !defined(RS129_H)
+#define	RS129_H
+
+class CRS129
+{
+public:
+    static bool check(const unsigned char* in);
+    
+    static void encode(const unsigned char* msg, unsigned int nbytes, unsigned char* parity);
+};
+
+#endif
diff --git a/src/csemaphore.cpp b/src/csemaphore.cpp
new file mode 100644
index 0000000..3b91e82
--- /dev/null
+++ b/src/csemaphore.cpp
@@ -0,0 +1,72 @@
+//
+//  csemaphore.cpp
+//  xlxd
+//
+//  Created by Jean-Luc Deltombe (LX3JL) on 16/04/2017.
+//  Copyright © 2015 Jean-Luc Deltombe (LX3JL). All rights reserved.
+//
+// ----------------------------------------------------------------------------
+//    This file is part of xlxd.
+//
+//    xlxd is free software: you can redistribute it and/or modify
+//    it under the terms of the GNU General Public License as published by
+//    the Free Software Foundation, either version 3 of the License, or
+//    (at your option) any later version.
+//
+//    xlxd is distributed in the hope that it will be useful,
+//    but WITHOUT ANY WARRANTY; without even the implied warranty of
+//    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+//    GNU General Public License for more details.
+//
+//    You should have received a copy of the GNU General Public License
+//    along with Foobar.  If not, see <http://www.gnu.org/licenses/>.
+// ----------------------------------------------------------------------------
+
+#include "main.h"
+#include "csemaphore.h"
+
+////////////////////////////////////////////////////////////////////////////////////////
+// constructor
+
+CSemaphore::CSemaphore()
+{
+    // Initialized as locked.
+    m_Count = 0;
+}
+
+////////////////////////////////////////////////////////////////////////////////////////
+// operation
+
+void CSemaphore::Reset(void)
+{
+    std::unique_lock<decltype(m_Mutex)> lock(m_Mutex);
+    m_Count = 0;
+}
+
+void CSemaphore::Notify(void)
+{
+    std::unique_lock<std::mutex> lock(m_Mutex);
+    m_Count++;
+    m_Condition.notify_one();
+}
+
+void CSemaphore::Wait(void)
+{
+    std::unique_lock<std::mutex> lock(m_Mutex);
+    m_Condition.wait(lock, [&]{ return m_Count > 0; });
+    m_Count--;
+}
+
+bool CSemaphore::WaitFor(uint ms)
+{
+    std::chrono::milliseconds timespan(ms);
+    std::unique_lock<decltype(m_Mutex)> lock(m_Mutex);
+    auto ok = m_Condition.wait_for(lock, timespan, [&]{ return m_Count > 0; });
+    if ( ok )
+    {
+        m_Count--;
+    }
+    return ok;
+    
+}
+
diff --git a/src/csemaphore.h b/src/csemaphore.h
new file mode 100644
index 0000000..0729512
--- /dev/null
+++ b/src/csemaphore.h
@@ -0,0 +1,56 @@
+//
+//  csemaphore.h
+//  xlxd
+//
+//  Created by Jean-Luc Deltombe (LX3JL) on 16/04/2017.
+//  Copyright © 2015 Jean-Luc Deltombe (LX3JL). All rights reserved.
+//
+// ----------------------------------------------------------------------------
+//    This file is part of xlxd.
+//
+//    xlxd is free software: you can redistribute it and/or modify
+//    it under the terms of the GNU General Public License as published by
+//    the Free Software Foundation, either version 3 of the License, or
+//    (at your option) any later version.
+//
+//    xlxd is distributed in the hope that it will be useful,
+//    but WITHOUT ANY WARRANTY; without even the implied warranty of
+//    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+//    GNU General Public License for more details.
+//
+//    You should have received a copy of the GNU General Public License
+//    along with Foobar.  If not, see <http://www.gnu.org/licenses/>.
+// ----------------------------------------------------------------------------
+
+#ifndef csemaphore_h
+#define csemaphore_h
+
+
+////////////////////////////////////////////////////////////////////////////////////////
+// class
+
+class CSemaphore
+{
+public:
+    // constructor
+    CSemaphore();
+    
+    // destructor
+    virtual ~CSemaphore() {};
+    
+    // operation
+    void Reset(void);
+    void Notify(void);
+    void Wait(void);
+    bool WaitFor(uint);
+    
+protected:
+    // data
+    std::mutex              m_Mutex;
+    std::condition_variable m_Condition;
+    size_t                  m_Count;
+
+};
+
+////////////////////////////////////////////////////////////////////////////////////////
+#endif /* csemaphore_h */
diff --git a/src/ctimepoint.h b/src/ctimepoint.h
index 9a9e5a2..209e8c7 100644
--- a/src/ctimepoint.h
+++ b/src/ctimepoint.h
@@ -39,7 +39,7 @@ public:
     virtual ~CTimePoint() {}
     
     // operation
-    void   Now(void)                  { m_TimePoint = std::chrono::steady_clock::now(); }
+    void   Now(void)                        { m_TimePoint = std::chrono::steady_clock::now(); }
     double DurationSinceNow(void) const;
     
     // task
diff --git a/src/ctranscoder.cpp b/src/ctranscoder.cpp
new file mode 100644
index 0000000..e8082bd
--- /dev/null
+++ b/src/ctranscoder.cpp
@@ -0,0 +1,407 @@
+//
+//  ctranscoder.cpp
+//  xlxd
+//
+//  Created by Jean-Luc Deltombe (LX3JL) on 13/04/2017.
+//  Copyright © 2015 Jean-Luc Deltombe (LX3JL). All rights reserved.
+//
+// ----------------------------------------------------------------------------
+//    This file is part of xlxd.
+//
+//    xlxd is free software: you can redistribute it and/or modify
+//    it under the terms of the GNU General Public License as published by
+//    the Free Software Foundation, either version 3 of the License, or
+//    (at your option) any later version.
+//
+//    xlxd is distributed in the hope that it will be useful,
+//    but WITHOUT ANY WARRANTY; without even the implied warranty of
+//    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+//    GNU General Public License for more details.
+//
+//    You should have received a copy of the GNU General Public License
+//    along with Foobar.  If not, see <http://www.gnu.org/licenses/>.
+// ----------------------------------------------------------------------------
+
+#include "main.h"
+#include "creflector.h"
+#include "ctranscoder.h"
+
+////////////////////////////////////////////////////////////////////////////////////////
+// define
+
+// status
+#define STATUS_IDLE                 0
+#define STATUS_LOGGED               1
+
+// timeout
+#define AMBED_OPENSTREAM_TIMEOUT    200     // in ms
+
+////////////////////////////////////////////////////////////////////////////////////////
+
+CTranscoder g_Transcoder;
+
+
+////////////////////////////////////////////////////////////////////////////////////////
+// constructor
+
+CTranscoder::CTranscoder()
+{
+    m_bStopThread = false;
+    m_pThread = NULL;
+    m_Streams.reserve(12);
+    m_bConnected = false;
+    m_LastKeepaliveTime.Now();
+    m_LastActivityTime.Now();
+    m_bStreamOpened = false;
+    m_StreamidOpenStream = 0;
+    m_PortOpenStream = 0;
+}
+
+////////////////////////////////////////////////////////////////////////////////////////
+// destructor
+
+CTranscoder::~CTranscoder()
+{
+    // close all streams
+    m_Mutex.lock();
+    {
+        for ( int i = 0; i < m_Streams.size(); i++ )
+        {
+            delete m_Streams[i];
+        }
+        m_Streams.clear();
+        
+    }
+    m_Mutex.unlock();
+    
+    // kill threads
+    m_bStopThread = true;
+    if ( m_pThread != NULL )
+    {
+        m_pThread->join();
+        delete m_pThread;
+    }    
+}
+
+////////////////////////////////////////////////////////////////////////////////////////
+// initialization
+
+bool CTranscoder::Init(void)
+{
+    bool ok;
+    
+    // reset stop flag
+    m_bStopThread = false;
+
+    // create server's IP
+    m_Ip = g_Reflector.GetTrasncoderIp();
+    
+    // create our socket
+    ok = m_Socket.Open(TRANSCODER_PORT);
+    if ( ok )
+    {
+        // start  thread;
+        m_pThread = new std::thread(CTranscoder::Thread, this);
+    }
+    else
+    {
+        std::cout << "Error opening socket on port UDP" << TRANSCODER_PORT << " on ip " << g_Reflector.GetListenIp() << std::endl;
+    }
+
+    // done
+    return ok;
+}
+
+void CTranscoder::Close(void)
+{
+    // close socket
+    m_Socket.Close();
+    
+    // close all streams
+    m_Mutex.lock();
+    {
+        for ( int i = 0; i < m_Streams.size(); i++ )
+        {
+            delete m_Streams[i];
+        }
+        m_Streams.clear();
+        
+    }
+    m_Mutex.unlock();
+    
+    // kill threads
+    m_bStopThread = true;
+    if ( m_pThread != NULL )
+    {
+        m_pThread->join();
+        delete m_pThread;
+        m_pThread = NULL;
+    }
+}
+
+////////////////////////////////////////////////////////////////////////////////////////
+// thread
+
+void CTranscoder::Thread(CTranscoder *This)
+{
+    while ( !This->m_bStopThread )
+    {
+        This->Task();
+    }
+}
+
+void CTranscoder::Task(void)
+{
+    CBuffer     Buffer;
+    CIp         Ip;
+    uint16      StreamId;
+    uint16      Port;
+    
+    // anything coming in from codec server ?
+    //if ( (m_Socket.Receive(&Buffer, &Ip, 20) != -1) && (Ip == m_Ip) )
+    if ( m_Socket.Receive(&Buffer, &Ip, 20) != -1 )
+    {
+        m_LastActivityTime.Now();
+        
+        // crack packet
+        if ( IsValidStreamDescrPacket(Buffer, &StreamId, &Port) )
+        {
+            //std::cout << "Transcoder stream " << (int) StreamId << " descr packet " << std::endl;
+            m_bStreamOpened = true;
+            m_StreamidOpenStream = StreamId;
+            m_PortOpenStream = Port;
+            m_SemaphoreOpenStream.Notify();
+        }
+        else if ( IsValidNoStreamAvailablePacket(Buffer) )
+        {
+            m_bStreamOpened = false;
+            m_SemaphoreOpenStream.Notify();
+        }
+        else if ( IsValidKeepAlivePacket(Buffer) )
+        {
+            if ( !m_bConnected )
+            {
+                std::cout << "Transcoder connected at " << Ip << std::endl;
+            }
+            m_bConnected = true;
+        }
+        
+    }
+    
+    // handle end of streaming timeout
+    //CheckStreamsTimeout();
+    
+    // handle queue from reflector
+    //HandleQueue();
+    
+    // keep client alive
+    if ( m_LastKeepaliveTime.DurationSinceNow() > TRANSCODER_KEEPALIVE_PERIOD )
+    {
+        //
+        HandleKeepalives();
+        
+        // update time
+        m_LastKeepaliveTime.Now();
+    }
+ }
+
+////////////////////////////////////////////////////////////////////////////////////////
+// manage streams
+
+CCodecStream *CTranscoder::GetStream(CPacketStream *PacketStream, uint8 uiCodecIn)
+{
+    CBuffer     Buffer;
+    
+    CCodecStream *stream = NULL;
+    
+    // do we need transcoding
+    if ( uiCodecIn != CODEC_NONE )
+    {
+        // are we connected to server
+        if ( m_bConnected )
+        {
+            // yes, post openstream request
+            EncodeOpenstreamPacket(&Buffer, uiCodecIn, (uiCodecIn == CODEC_AMBEPLUS) ? CODEC_AMBE2PLUS : CODEC_AMBEPLUS);
+            m_Socket.Send(Buffer, m_Ip, TRANSCODER_PORT);
+            
+            // wait relpy here
+            if ( m_SemaphoreOpenStream.WaitFor(AMBED_OPENSTREAM_TIMEOUT) )
+            {
+                if ( m_bStreamOpened )
+                {
+                    std::cout << "ambed openstream ok" << std::endl;
+                
+                    // create stream object
+                    stream = new CCodecStream(PacketStream, m_StreamidOpenStream, uiCodecIn, (uiCodecIn == CODEC_AMBEPLUS) ? CODEC_AMBE2PLUS : CODEC_AMBEPLUS);
+                    
+                    // init it
+                    if ( stream->Init(m_PortOpenStream) )
+                    {
+                        // and append to list
+                        Lock();
+                        m_Streams.push_back(stream);
+                        Unlock();
+                    }
+                    else
+                    {
+                        // send close packet
+                        EncodeClosestreamPacket(&Buffer, stream->GetStreamId());
+                        m_Socket.Send(Buffer, m_Ip, TRANSCODER_PORT);
+                        // and delete
+                        delete stream;
+                        stream = NULL;
+                    }
+                }
+                else
+                {
+                    std::cout << "ambed openstream failed (no suitable channel available)" << std::endl;
+                }
+            }
+            else
+            {
+                std::cout << "ambed openstream timeout" << std::endl;
+            }
+            
+        }
+    }
+    return stream;
+}
+
+void CTranscoder::ReleaseStream(CCodecStream *stream)
+{
+    CBuffer Buffer;
+    
+    if ( stream != NULL )
+    {
+        // look for the stream
+        bool found = false;
+        Lock();
+        {
+            for ( int i = 0; (i < m_Streams.size()) && !found; i++ )
+            {
+                // compare object pointers
+                if ( (m_Streams[i]) ==  stream )
+                {
+                    // send close packet
+                    EncodeClosestreamPacket(&Buffer, m_Streams[i]->GetStreamId());
+                    m_Socket.Send(Buffer, m_Ip, TRANSCODER_PORT);
+                    
+                    // display stats
+                    if ( m_Streams[i]->GetPingMin() >= 0.0 )
+                    {
+                        char sz[256];
+                        sprintf(sz, "ambed stats (ms) : %.1f/%.1f/%.1f",
+                                m_Streams[i]->GetPingMin() * 1000.0,
+                                m_Streams[i]->GetPingAve() * 1000.0,
+                                m_Streams[i]->GetPingMax() * 1000.0);
+                        std::cout << sz << std::endl;
+                    }
+                    if ( m_Streams[i]->GetTimeoutPackets() > 0 )
+                    {
+                        char sz[256];
+                        sprintf(sz, "ambed %d of %d packets timed out",
+                                m_Streams[i]->GetTimeoutPackets(),
+                                m_Streams[i]->GetTotalPackets());
+                        std::cout << sz << std::endl;
+                    }
+
+                    // and close it
+                    m_Streams[i]->Close();
+                    delete m_Streams[i];
+                    m_Streams.erase(m_Streams.begin()+i);
+                    found = true;
+                }
+            }
+        }
+        Unlock();
+    }
+}
+
+////////////////////////////////////////////////////////////////////////////////////////
+// keepalive helpers
+
+void CTranscoder::HandleKeepalives(void)
+{
+    CBuffer keepalive;
+    
+    // send keepalive
+    EncodeKeepAlivePacket(&keepalive);
+    m_Socket.Send(keepalive, m_Ip, TRANSCODER_PORT);
+    
+    // check if still with us
+    if ( m_bConnected && (m_LastActivityTime.DurationSinceNow() >= TRANSCODER_KEEPALIVE_TIMEOUT) )
+    {
+        // no, disconnect
+        m_bConnected = false;
+        std::cout << "Transcoder keepalive timeout" << std::endl;
+    }
+}
+
+////////////////////////////////////////////////////////////////////////////////////////
+// packet decoding helpers
+
+bool CTranscoder::IsValidKeepAlivePacket(const CBuffer &Buffer)
+{
+    uint8 tag[] = { 'A','M','B','E','D','P','O','N','G' };
+    
+    bool valid = false;
+    if ( (Buffer.size() == 9) && (Buffer.Compare(tag, sizeof(tag)) == 0) )
+    {
+        valid = true;
+    }
+    return valid;
+}
+
+bool CTranscoder::IsValidStreamDescrPacket(const CBuffer &Buffer, uint16 *Id, uint16 *Port)
+{
+    uint8 tag[] = { 'A','M','B','E','D','S','T','D' };
+    
+    bool valid = false;
+    if ( (Buffer.size() == 14) && (Buffer.Compare(tag, sizeof(tag)) == 0) )
+    {
+        *Id = *(uint16 *)(&Buffer.data()[8]);
+        *Port = *(uint16 *)(&Buffer.data()[10]);
+        // uint8 CodecIn = Buffer.data()[12];
+        // uint8 CodecOut = Buffer.data()[13];
+        valid = true;
+    }
+    return valid;
+}
+
+bool CTranscoder::IsValidNoStreamAvailablePacket(const CBuffer&Buffer)
+{
+    uint8 tag[] = { 'A','M','B','E','D','B','U','S','Y' };
+    
+    return  ( (Buffer.size() == 9) && (Buffer.Compare(tag, sizeof(tag)) == 0) );
+}
+
+
+////////////////////////////////////////////////////////////////////////////////////////
+// packet encoding helpers
+
+void CTranscoder::EncodeKeepAlivePacket(CBuffer *Buffer)
+{
+    uint8 tag[] = { 'A','M','B','E','D','P','I','N','G' };
+    
+    Buffer->Set(tag, sizeof(tag));
+    Buffer->Append((uint8 *)(const char *)g_Reflector.GetCallsign(), CALLSIGN_LEN);
+}
+
+void CTranscoder::EncodeOpenstreamPacket(CBuffer *Buffer, uint8 uiCodecIn, uint8 uiCodecOut)
+{
+    uint8 tag[] = { 'A','M','B','E','D','O','S' };
+
+    Buffer->Set(tag, sizeof(tag));
+    Buffer->Append((uint8 *)(const char *)g_Reflector.GetCallsign(), CALLSIGN_LEN);
+    Buffer->Append((uint8)uiCodecIn);
+    Buffer->Append((uint8)uiCodecOut);
+}
+
+void CTranscoder::EncodeClosestreamPacket(CBuffer *Buffer, uint16 uiStreamId)
+{
+    uint8 tag[] = { 'A','M','B','E','D','C','S' };
+    
+    Buffer->Set(tag, sizeof(tag));
+    Buffer->Append((uint16)uiStreamId);
+}
+
diff --git a/src/ctranscoder.h b/src/ctranscoder.h
new file mode 100644
index 0000000..b45b727
--- /dev/null
+++ b/src/ctranscoder.h
@@ -0,0 +1,107 @@
+//
+//  ctranscoder.h
+//  xlxd
+//
+//  Created by Jean-Luc Deltombe (LX3JL) on 13/04/2017.
+//  Copyright © 2015 Jean-Luc Deltombe (LX3JL). All rights reserved.
+//
+// ----------------------------------------------------------------------------
+//    This file is part of xlxd.
+//
+//    xlxd is free software: you can redistribute it and/or modify
+//    it under the terms of the GNU General Public License as published by
+//    the Free Software Foundation, either version 3 of the License, or
+//    (at your option) any later version.
+//
+//    xlxd is distributed in the hope that it will be useful,
+//    but WITHOUT ANY WARRANTY; without even the implied warranty of
+//    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+//    GNU General Public License for more details.
+//
+//    You should have received a copy of the GNU General Public License
+//    along with Foobar.  If not, see <http://www.gnu.org/licenses/>.
+// ----------------------------------------------------------------------------
+
+#ifndef ctranscoder_h
+#define ctranscoder_h
+
+#include "csemaphore.h"
+#include "ccodecstream.h"
+#include "cudpsocket.h"
+
+////////////////////////////////////////////////////////////////////////////////////////
+// define
+
+
+////////////////////////////////////////////////////////////////////////////////////////
+// class
+
+class CPacketStream;
+
+class CTranscoder
+{
+public:
+    // constructor
+    CTranscoder();
+    
+    // destructor
+    virtual ~CTranscoder();
+
+    // initialization
+    bool Init(void);
+    void Close(void);
+
+    // locks
+    void Lock(void)                     { m_Mutex.lock(); }
+    void Unlock(void)                   { m_Mutex.unlock(); }
+    
+    // manage streams
+    CCodecStream *GetStream(CPacketStream *, uint8);
+    void ReleaseStream(CCodecStream *);
+    
+    // task
+    static void Thread(CTranscoder *);
+    void Task(void);
+
+protected:
+    // keepalive helpers
+    void HandleKeepalives(void);
+
+    // packet decoding helpers
+    bool IsValidKeepAlivePacket(const CBuffer &);
+    bool IsValidStreamDescrPacket(const CBuffer &, uint16 *, uint16 *);
+    bool IsValidNoStreamAvailablePacket(const CBuffer&);
+
+    // packet encoding helpers
+    void EncodeKeepAlivePacket(CBuffer *);
+    void EncodeOpenstreamPacket(CBuffer *, uint8, uint8);
+    void EncodeClosestreamPacket(CBuffer *, uint16);
+    
+protected:
+    // streams
+    std::mutex                  m_Mutex;
+    std::vector<CCodecStream *> m_Streams;
+
+    // sync objects for Openstream
+    CSemaphore      m_SemaphoreOpenStream;
+    bool            m_bStreamOpened;
+    uint16          m_StreamidOpenStream;
+    uint16          m_PortOpenStream;
+    
+    // thread
+    bool            m_bStopThread;
+    std::thread     *m_pThread;
+
+    // socket
+    CIp             m_Ip;
+    CUdpSocket      m_Socket;
+    bool            m_bConnected;
+
+    // time
+    CTimePoint      m_LastKeepaliveTime;
+    CTimePoint      m_LastActivityTime;
+};
+
+
+////////////////////////////////////////////////////////////////////////////////////////
+#endif /* ctranscoder_h */
diff --git a/src/cutils.cpp b/src/cutils.cpp
new file mode 100644
index 0000000..d86d79b
--- /dev/null
+++ b/src/cutils.cpp
@@ -0,0 +1,73 @@
+/*
+ *	Copyright (C) 2009,2014,2015,2016 Jonathan Naylor, G4KLX
+ *
+ *	This program is free software; you can redistribute it and/or modify
+ *	it under the terms of the GNU General Public License as published by
+ *	the Free Software Foundation; version 2 of the License.
+ *
+ *	This program is distributed in the hope that it will be useful,
+ *	but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *	GNU General Public License for more details.
+ */
+
+#include "cutils.h"
+
+#include <cstdio>
+#include <cassert>
+
+void CUtils::byteToBitsBE(unsigned char byte, bool* bits)
+{
+    assert(bits != NULL);
+    
+    bits[0U] = (byte & 0x80U) == 0x80U;
+    bits[1U] = (byte & 0x40U) == 0x40U;
+    bits[2U] = (byte & 0x20U) == 0x20U;
+    bits[3U] = (byte & 0x10U) == 0x10U;
+    bits[4U] = (byte & 0x08U) == 0x08U;
+    bits[5U] = (byte & 0x04U) == 0x04U;
+    bits[6U] = (byte & 0x02U) == 0x02U;
+    bits[7U] = (byte & 0x01U) == 0x01U;
+}
+
+void CUtils::byteToBitsLE(unsigned char byte, bool* bits)
+{
+    assert(bits != NULL);
+    
+    bits[0U] = (byte & 0x01U) == 0x01U;
+    bits[1U] = (byte & 0x02U) == 0x02U;
+    bits[2U] = (byte & 0x04U) == 0x04U;
+    bits[3U] = (byte & 0x08U) == 0x08U;
+    bits[4U] = (byte & 0x10U) == 0x10U;
+    bits[5U] = (byte & 0x20U) == 0x20U;
+    bits[6U] = (byte & 0x40U) == 0x40U;
+    bits[7U] = (byte & 0x80U) == 0x80U;
+}
+
+void CUtils::bitsToByteBE(const bool* bits, unsigned char& byte)
+{
+    assert(bits != NULL);
+    
+    byte  = bits[0U] ? 0x80U : 0x00U;
+    byte |= bits[1U] ? 0x40U : 0x00U;
+    byte |= bits[2U] ? 0x20U : 0x00U;
+    byte |= bits[3U] ? 0x10U : 0x00U;
+    byte |= bits[4U] ? 0x08U : 0x00U;
+    byte |= bits[5U] ? 0x04U : 0x00U;
+    byte |= bits[6U] ? 0x02U : 0x00U;
+    byte |= bits[7U] ? 0x01U : 0x00U;
+}
+
+void CUtils::bitsToByteLE(const bool* bits, unsigned char& byte)
+{
+    assert(bits != NULL);
+    
+    byte  = bits[0U] ? 0x01U : 0x00U;
+    byte |= bits[1U] ? 0x02U : 0x00U;
+    byte |= bits[2U] ? 0x04U : 0x00U;
+    byte |= bits[3U] ? 0x08U : 0x00U;
+    byte |= bits[4U] ? 0x10U : 0x00U;
+    byte |= bits[5U] ? 0x20U : 0x00U;
+    byte |= bits[6U] ? 0x40U : 0x00U;
+    byte |= bits[7U] ? 0x80U : 0x00U;
+}
diff --git a/src/cutils.h b/src/cutils.h
new file mode 100644
index 0000000..555a774
--- /dev/null
+++ b/src/cutils.h
@@ -0,0 +1,30 @@
+/*
+ *	Copyright (C) 2009,2014,2015 by Jonathan Naylor, G4KLX
+ *
+ *	This program is free software; you can redistribute it and/or modify
+ *	it under the terms of the GNU General Public License as published by
+ *	the Free Software Foundation; version 2 of the License.
+ *
+ *	This program is distributed in the hope that it will be useful,
+ *	but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *	GNU General Public License for more details.
+ */
+
+#ifndef	Utils_H
+#define	Utils_H
+
+#include <string>
+
+class CUtils {
+public:
+    static void byteToBitsBE(unsigned char byte, bool* bits);
+    static void byteToBitsLE(unsigned char byte, bool* bits);
+    
+    static void bitsToByteBE(const bool* bits, unsigned char& byte);
+    static void bitsToByteLE(const bool* bits, unsigned char& byte);
+    
+private:
+};
+
+#endif
diff --git a/src/main.cpp b/src/main.cpp
index f629b66..830f74e 100644
--- a/src/main.cpp
+++ b/src/main.cpp
@@ -87,16 +87,17 @@ int main(int argc, const char * argv[])
 #endif
 
     // check arguments
-    if ( argc != 3 )
+    if ( argc != 4 )
     {
-        std::cout << "Usage: xlxd callsign ip" << std::endl;
-        std::cout << "example: xlxd XLX999 192.168.178.212" << std::endl;
+        std::cout << "Usage: xlxd callsign xlxdip ambedip" << std::endl;
+        std::cout << "example: xlxd XLX999 192.168.178.212 127.0.0.1" << std::endl;
         return 1;
     }
 
     // initialize reflector
     g_Reflector.SetCallsign(argv[1]);
     g_Reflector.SetListenIp(CIp(argv[2]));
+    g_Reflector.SetTranscoderIp(CIp(CIp(argv[3])));
   
     // and let it run
     if ( !g_Reflector.Start() )
@@ -116,8 +117,14 @@ int main(int argc, const char * argv[])
     }
 #else
     // wait any key
-    //for (;;);
-    std::cin.get();
+    for (;;)
+    {
+        std::cin.get();
+#ifdef DEBUG_DUMPFILE
+        g_Reflector.m_DebugFile.close();
+        g_Reflector.m_DebugFile.open("/Users/jeanluc/Desktop/dmrdebug.txt");
+#endif
+    }
 #endif
     
     // and wait for end
diff --git a/src/main.h b/src/main.h
index d4b39bc..566d986 100644
--- a/src/main.h
+++ b/src/main.h
@@ -27,6 +27,7 @@
 
 #include <vector>
 #include <array>
+#include <map>
 #include <queue>
 #include <chrono>
 #include <thread>
@@ -46,76 +47,115 @@
 
 // version -----------------------------------------------------
 
-#define VERSION_MAJOR               1
-#define VERSION_MINOR               4
-#define VERSION_REVISION            3
+#define VERSION_MAJOR                   2
+#define VERSION_MINOR                   0
+#define VERSION_REVISION                0
 
 // global ------------------------------------------------------
 
 #define RUN_AS_DAEMON
 #define JSON_MONITOR
 //#define NO_ERROR_ON_XML_OPEN_FAIL
+//#define DEBUG_DUMPFILE
 
 // reflector ---------------------------------------------------
 
-#define NB_OF_MODULES               8
+#define NB_OF_MODULES                 10
+//#define NB_OF_MODULES                   NB_MODULES_MAX
 
 // protocols ---------------------------------------------------
 
-#define NB_OF_PROTOCOLS             4
+#define NB_OF_PROTOCOLS                 6
 
-#define PROTOCOL_ANY                -1
-#define PROTOCOL_NONE               0
-#define PROTOCOL_DEXTRA             1
-#define PROTOCOL_DPLUS              2
-#define PROTOCOL_DCS                3
-#define PROTOCOL_XLX                4
+#define PROTOCOL_ANY                    -1
+#define PROTOCOL_NONE                   0
+#define PROTOCOL_DEXTRA                 1
+#define PROTOCOL_DPLUS                  2
+#define PROTOCOL_DCS                    3
+#define PROTOCOL_XLX                    4
+#define PROTOCOL_DMRPLUS                5
+#define PROTOCOL_DMRMMDVM               6
 
 // DExtra
-#define DEXTRA_PORT                 30001                           // UDP port
-#define DEXTRA_KEEPALIVE_PERIOD     3                               // in seconds
-#define DEXTRA_KEEPALIVE_TIMEOUT    (DEXTRA_KEEPALIVE_PERIOD*10)    // in seconds
+#define DEXTRA_PORT                     30001                               // UDP port
+#define DEXTRA_KEEPALIVE_PERIOD         3                                   // in seconds
+#define DEXTRA_KEEPALIVE_TIMEOUT        (DEXTRA_KEEPALIVE_PERIOD*10)        // in seconds
 
 // DPlus
-#define DPLUS_PORT                  20001                           // UDP port
-#define DPLUS_KEEPALIVE_PERIOD      1                               // in seconds
-#define DPLUS_KEEPALIVE_TIMEOUT     (DPLUS_KEEPALIVE_PERIOD*10)     // in seconds
+#define DPLUS_PORT                      20001                               // UDP port
+#define DPLUS_KEEPALIVE_PERIOD          1                                   // in seconds
+#define DPLUS_KEEPALIVE_TIMEOUT         (DPLUS_KEEPALIVE_PERIOD*10)         // in seconds
 
 // DCS
-#define DCS_PORT                    30051                           // UDP port
-#define DCS_KEEPALIVE_PERIOD        1                               // in seconds
-#define DCS_KEEPALIVE_TIMEOUT       (DCS_KEEPALIVE_PERIOD*30)       // in seconds
+#define DCS_PORT                        30051                               // UDP port
+#define DCS_KEEPALIVE_PERIOD            1                                   // in seconds
+#define DCS_KEEPALIVE_TIMEOUT           (DCS_KEEPALIVE_PERIOD*30)           // in seconds
 
 // XLX
-#define XLX_PORT                    10002                           // UDP port
-#define XLX_KEEPALIVE_PERIOD        1                               // in seconds
-#define XLX_KEEPALIVE_TIMEOUT       (XLX_KEEPALIVE_PERIOD*30)       // in seconds
-#define XLX_RECONNECT_PERIOD        5                               // in seconds
+#define XLX_PORT                        10002                               // UDP port
+#define XLX_KEEPALIVE_PERIOD            1                                   // in seconds
+#define XLX_KEEPALIVE_TIMEOUT           (XLX_KEEPALIVE_PERIOD*30)           // in seconds
+#define XLX_RECONNECT_PERIOD            5                                   // in seconds
+
+// DMRPlus (dongle)
+#define DMRPLUS_PORT                    8880                                // UDP port
+#define DMRPLUS_KEEPALIVE_PERIOD        1                                   // in seconds
+#define DMRPLUS_KEEPALIVE_TIMEOUT       (DMRPLUS_KEEPALIVE_PERIOD*10)       // in seconds
+#define DMRPLUS_REFLECTOR_SLOT          DMR_SLOT2
+#define DMRPLUS_REFLECTOR_COLOUR        1
+
+// DMRMmdvm
+#define DMRMMDVM_PORT                   62030                               // UDP port
+#define DMRMMDVM_KEEPALIVE_PERIOD       10                                  // in seconds
+#define DMRMMDVM_KEEPALIVE_TIMEOUT      (DMRMMDVM_KEEPALIVE_PERIOD*10)      // in seconds
+#define DMRMMDVM_REFLECTOR_SLOT         DMR_SLOT2
+#define DMRMMDVM_REFLECTOR_COLOUR       1
+
+// Transcoder server --------------------------------------------
+
+#define TRANSCODER_PORT                 10100                               // UDP port
+#define TRANSCODER_KEEPALIVE_PERIOD     5                                   // in seconds
+#define TRANSCODER_KEEPALIVE_TIMEOUT    30                                  // in seconds
+#define TRANSCODER_AMBEPACKET_TIMEOUT   400                                 // in ms
+
+// codec --------------------------------------------------------
+
+#define CODEC_NONE          0
+#define CODEC_AMBEPLUS      1                                               // DStar
+#define CODEC_AMBE2PLUS     2                                               // DMR
+
+
+// DMRid database -----------------------------------------------
+
+#define DMRIDDB_USE_RLX_SERVER          0
+#define DMRIDDB_PATH                    "/xlxd/dmrid.dat"
+
 
 // xml & json reporting -----------------------------------------
 
-#define LASTHEARD_USERS_MAX_SIZE    100
-#define XML_UPDATE_PERIOD           10                              // in seconds
-#define JSON_UPDATE_PERIOD          10                              // in seconds
-#define JSON_PORT                   10001
+#define LASTHEARD_USERS_MAX_SIZE        100
+#define XML_UPDATE_PERIOD               10                              // in seconds
+#define JSON_UPDATE_PERIOD              10                              // in seconds
+#define JSON_PORT                       10001
 
 // system paths -------------------------------------------------
 
-#define XML_PATH                    "/var/log/xlxd.xml"
-#define WHITELIST_PATH              "/xlxd/xlxd.whitelist"
-#define BLACKLIST_PATH              "/xlxd/xlxd.blacklist"
-#define INTERLINKLIST_PATH          "/xlxd/xlxd.interlink"
+#define XML_PATH                        "/var/log/xlxd.xml"
+#define WHITELIST_PATH                  "/xlxd/xlxd.whitelist"
+#define BLACKLIST_PATH                  "/xlxd/xlxd.blacklist"
+#define INTERLINKLIST_PATH              "/xlxd/xlxd.interlink"
+#define DEBUGDUMP_PATH                  "/var/log/xlxd.debug"
 
 // system constants ---------------------------------------------
 
-#define NB_MODULES_MAX              26
+#define NB_MODULES_MAX                  26
 
 ////////////////////////////////////////////////////////////////////////////////////////
 // typedefs
 
 typedef unsigned char           uint8;
 typedef unsigned short          uint16;
-typedef unsigned long           uint32;
+typedef unsigned int            uint32;
 typedef unsigned int            uint;
 
 
@@ -125,8 +165,11 @@ typedef unsigned int            uint;
 #define MIN(a,b) 				((a) < (b))?(a):(b)
 #define MAX(a,b) 				((a) > (b))?(a):(b)
 #define MAKEWORD(low, high)		((uint16)(((uint8)(low)) | (((uint16)((uint8)(high))) << 8)))
+#define MAKEDWORD(low, high)	((uint32)(((uint16)(low)) | (((uint32)((uint16)(high))) << 16)))
 #define LOBYTE(w)				((uint8)(uint16)(w & 0x00FF))
 #define HIBYTE(w)				((uint8)((((uint16)(w)) >> 8) & 0xFF))
+#define LOWORD(dw)				((uint16)(uint32)(dw & 0x0000FFFF))
+#define HIWORD(dw)				((uint16)((((uint32)(dw)) >> 16) & 0xFFFF))
 
 ////////////////////////////////////////////////////////////////////////////////////////
 // global objects
@@ -137,5 +180,12 @@ extern CReflector  g_Reflector;
 class CGateKeeper;
 extern CGateKeeper g_GateKeeper;
 
+class CDmridDir;
+extern CDmridDir   g_DmridDir;
+
+class CTranscoder;
+extern CTranscoder g_Transcoder;
+
+
 ////////////////////////////////////////////////////////////////////////////////////////
 #endif /* main_h */