aboutsummaryrefslogtreecommitdiffstats
path: root/src/nmt
diff options
context:
space:
mode:
authorAndreas Eversberg <jolly@eversberg.eu>2017-07-15 21:49:28 +0200
committerAndreas Eversberg <jolly@eversberg.eu>2017-08-08 12:52:29 +0200
commit0c9de251bedc16e51a1b5f5dc2735fa878708098 (patch)
tree7b2176be70afdbf4548189f39ad169ea2ccee514 /src/nmt
parentcd9cb9a1070f008b676cd5925f74d47f13dd5122 (diff)
NMT: Implement Hagelbarger Code
This will correct burst errors of received messages. If the message is too corrupted, it will be ignored, because some element may not match then. The digits and line signals are checked for consistency, since they are repeated serveral times in a message.
Diffstat (limited to 'src/nmt')
-rw-r--r--src/nmt/Makefile.am1
-rw-r--r--src/nmt/frame.c176
-rw-r--r--src/nmt/hagelbarger.c87
-rw-r--r--src/nmt/hagelbarger.h4
-rw-r--r--src/nmt/nmt.c91
5 files changed, 200 insertions, 159 deletions
diff --git a/src/nmt/Makefile.am b/src/nmt/Makefile.am
index 91d5cff..1f738b9 100644
--- a/src/nmt/Makefile.am
+++ b/src/nmt/Makefile.am
@@ -8,6 +8,7 @@ nmt_SOURCES = \
countries.c \
transaction.c \
dsp.c \
+ hagelbarger.c \
frame.c \
dms.c \
sms.c \
diff --git a/src/nmt/frame.c b/src/nmt/frame.c
index bde6e8d..8cad3ca 100644
--- a/src/nmt/frame.c
+++ b/src/nmt/frame.c
@@ -27,6 +27,7 @@
#include "../common/timer.h"
#include "nmt.h"
#include "frame.h"
+#include "hagelbarger.h"
uint64_t nmt_encode_channel(int channel, int power)
{
@@ -969,161 +970,25 @@ static void assemble_frame(frame_t *frame, uint8_t *digits, int debug)
}
}
-/* encode digits of a frame to 166 bits */
-static void encode_digits(const uint8_t *digits, char *bits)
-{
- uint8_t x[64];
- int i;
- uint8_t digit;
-
- /* copy bit sync and frame sync */
- memcpy(bits, "10101010101010111100010010", 26);
- bits += 26;
-
- for (i = 0; i < 16; i++) {
- digit = *digits++;
- x[(i << 2) + 0] = (digit >> 3) & 1;
- x[(i << 2) + 1] = (digit >> 2) & 1;
- x[(i << 2) + 2] = (digit >> 1) & 1;
- x[(i << 2) + 3] = digit & 1;
- }
-
- /* parity check bits */
- for (i = 0; i < 3; i++)
- bits[(i << 1)] = '1' - x[i];
- for (i = 3; i < 64; i++)
- bits[(i << 1)] = '1' - (x[i] ^ x[i - 3]);
- for (i = 64; i < 67; i++)
- bits[(i << 1)] = '1' - x[i - 3];
- for (i = 67; i < 70; i++)
- bits[(i << 1)] = '1';
-
- /* information bits */
- for (i = 0; i < 6; i++)
- bits[(i << 1) + 1] = '0';
- for (i = 6; i < 70; i++)
- bits[(i << 1) + 1] = x[i - 6] + '0';
-}
-
-/* debug parity check */
-//#define DEBUG_DECODE
-
-/* decode digits from 140 bits (not including sync) */
-// FIXME: do real convolutional decoding
-static int decode_digits(uint8_t *digits, const char *bits, int callack)
-{
- uint8_t x[64];
- int i, short_frame = 0;
-
- /* information bits */
- for (i = 0; i < 6; i++) {
- if (bits[(i << 1) + 1] != '0') {
-#ifdef DEBUG_DECODE
- PDEBUG(DFRAME, DEBUG_DEBUG, "Frame bad at information bit #%d.\n", i);
-#endif
- return -1;
- }
- }
- for (i = 6; i < 70; i++)
- x[i - 6] = bits[(i << 1) + 1] - '0';
-
- /* create digits */
- for (i = 0; i < 16; i++) {
- digits[i] = ((x[(i << 2) + 0] & 1) << 3)
- | ((x[(i << 2) + 1] & 1) << 2)
- | ((x[(i << 2) + 2] & 1) << 1)
- | (x[(i << 2) + 3] & 1);
- }
-
- /* check for short frame */
- if (callack && (digits[3] == 1 || digits[3] == 15)) {
- digits[13] = 0;
- digits[14] = 0;
- digits[15] = 0;
- short_frame = 1;
-#ifdef DEBUG_DECODE
- PDEBUG(DFRAME, DEBUG_DEBUG, "Received shortened frame\n");
-#endif
- }
-
- /* parity check bits */
- for (i = 0; i < 3; i++) {
- if (bits[(i << 1)] != '1' - x[i]) {
-#ifdef DEBUG_DECODE
- PDEBUG(DFRAME, DEBUG_DEBUG, "Frame bad at parity bit #%d.\n", i);
-#endif
- return -1;
- }
- }
-#if 0
-#warning this check does not work, since we get error even at bit 50
- for (i = 3; i < ((short_frame) ? 52 : 64); i++) {
- if (bits[(i << 1)] != '1' - (x[i] ^ x[i - 3])) {
- /* According to NMT Doc 450-3, bits after Y(114) shall
- * be omitted for short frame. It would make more sense
- * to stop after Y(116), so only the last three digits
- * are omitted and not the last bit of digit13 also.
- * Tests have showed that it we receive correctly up to
- * Y(116), but we ignore an error, if only up to Y(114)
- * is received.
- */
- if (short_frame && i == 51) {
- PDEBUG(DFRAME, DEBUG_DEBUG, "Frame bad after bit Y(114), ignoring.\n");
- digits[13] = 0;
- break;
- }
-#ifdef DEBUG_DECODE
- PDEBUG(DFRAME, DEBUG_DEBUG, "Frame bad at parity bit #%d.\n", i);
-#endif
- return -1;
- }
- }
-#endif
- /* Tests showed corrupt frame at parity #50 (i == 50)
- * We just ignore whatever we receive after 48 bits of checksum.
- * This is not the correct approach, but in case of a corrupt
- * frame 10.a, we would drop it, if it failes later checks.
- */
- for (i = 3; i < ((short_frame) ? 48 : 64); i++) {
- if (bits[(i << 1)] != '1' - (x[i] ^ x[i - 3])) {
-#ifdef DEBUG_DECODE
- PDEBUG(DFRAME, DEBUG_DEBUG, "Frame bad at parity bit #%d.\n", i);
-#endif
- return -1;
- }
- }
- if (short_frame)
- return 0;
- for (i = 64; i < 67; i++) {
- if (bits[(i << 1)] != '1' - x[i - 3]) {
-#ifdef DEBUG_DECODE
- PDEBUG(DFRAME, DEBUG_DEBUG, "Frame bad at parity bit #%d.\n", i);
-#endif
- return -1;
- }
- }
- for (i = 67; i < 70; i++) {
- if (bits[(i << 1)] != '1') {
-#ifdef DEBUG_DECODE
- PDEBUG(DFRAME, DEBUG_DEBUG, "Frame bad at parity bit #%d.\n", i);
-#endif
- return -1;
- }
- }
-
- return 0;
-}
-
/* encode frame to bits
* debug can be turned on or off
*/
const char *encode_frame(frame_t *frame, int debug)
{
- uint8_t digits[16];
+ uint8_t digits[16], message[9], code[18];
static char bits[166];
+ int i;
assemble_frame(frame, digits, debug);
- encode_digits(digits, bits);
+
+ /* hagelbarger code */
+ message[8] = 0x00;
+ for (i = 0; i < 8; i++)
+ message[i] = (digits[i * 2] << 4) | digits[i * 2 + 1];
+ hagelbarger_encode(message, code, 72);
+ memcpy(bits, "10101010101010111100010010", 26);
+ for (i = 0; i < 140; i++)
+ bits[i + 26] = ((code[i / 8] >> (7 - (i & 7))) & 1) + '0';
return bits;
}
@@ -1131,12 +996,19 @@ const char *encode_frame(frame_t *frame, int debug)
/* decode bits to frame */
int decode_frame(frame_t *frame, const char *bits, enum nmt_direction direction, int callack)
{
- uint8_t digits[16];
- int rc;
+ uint8_t digits[16], message[8], code[19];
+ int i;
+
+ /* hagelbarger code */
+ memset(code, 0x00, sizeof(code));
+ for (i = 0; i < 140; i++)
+ code[i / 8] |= (bits[i] & 1) << (7 - (i & 7));
+ hagelbarger_decode(code, message, 64);
+ for (i = 0; i < 8; i++) {
+ digits[i * 2] = message[i] >> 4;
+ digits[i * 2 + 1] = message[i] & 0x0f;
+ }
- rc = decode_digits(digits, bits, callack);
- if (rc < 0)
- return rc;
disassemble_frame(frame, digits, direction, callack);
return 0;
diff --git a/src/nmt/hagelbarger.c b/src/nmt/hagelbarger.c
new file mode 100644
index 0000000..f6ac2cd
--- /dev/null
+++ b/src/nmt/hagelbarger.c
@@ -0,0 +1,87 @@
+/* Hagelbarger (6,19) code
+ *
+ * A burst up to 6 encoded bits may be corrupt, to correct them.
+ * After corrupt bits, a minimum of 19 bits must be correct to correct
+ * another burst of corrupted bits.
+ *
+ * There is no parity check, so it is required to check all information
+ * elements of each message. Messages that contain signals or digits are
+ * protected by repeating the digits in the information element.
+ *
+ * (C) 2017 by Andreas Eversberg <jolly@eversberg.eu>
+ * All Rights Reserved
+ *
+ * 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 3 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, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "stdint.h"
+
+/* To encode NMT message: (MSB first)
+ * Use input with 9 bytes, the last byte must be 0x00.
+ * Use output with 18 bytes, ignore the last four (lower) bits of last byte.
+ * Use length of 72.
+ */
+void hagelbarger_encode(const uint8_t *input, uint8_t *output, int length)
+{
+ uint8_t reg = 0x00, data, check;
+ int i;
+
+ for (i = 0; i < length; i++) {
+ /* get data from input (MSB first) */
+ data = (input[i / 8] >> (7 - (i & 7))) & 1;
+ /* push data into shift register (LSB first) */
+ reg = (reg << 1) | data;
+ /* get data bit from register */
+ data = (reg >> 6) & 1;
+ /* calc check bit from register */
+ check = (reg + (reg >> 3) + 1) & 1;
+ /* put check bit and data bit to output (MSB first) */
+ output[i / 4] = (output[i / 4] << 2) | (check << 1) | data;
+ }
+}
+
+/* To decode NMT message: (MSB first)
+ * Use input with 19 bytes, the unused last 12 (lower) bits must be zero.
+ * Use output with 8 bytes.
+ * Use length of 64.
+ */
+void hagelbarger_decode(const uint8_t *input, uint8_t *output, int length)
+{
+ uint16_t reg_data = 0x00, reg_check = 0x00, data, check, r_parity, s_parity;
+ int i;
+
+ length += 10;
+
+ for (i = 0; i < length; i++) {
+ /* get check bit from input (MSB first) */
+ check = (input[i / 4] >> (7 - (i & 3) * 2)) & 1;
+ /* get data bit from input (MSB first) */
+ data = (input[i / 4] >> (6 - (i & 3) * 2)) & 1;
+ /* push check bit into shift register (LSB first) */
+ reg_check = (reg_check << 1) | check;
+ /* push data bit into shift register (LSB first) */
+ reg_data = (reg_data << 1) | data;
+ /* calculate parity */
+ r_parity = (reg_data + (reg_data >> 3) + (reg_check >> 6) + 1) & 1;
+ s_parity = ((reg_data >> 3) + (reg_data >> 6) + (reg_check >> 9) + 1) & 1;
+ /* flip message bit, if both parity checks fail */
+ /* use 4th bit that will be shifted to 5th bit next loop */
+ if (r_parity && s_parity)
+ reg_data ^= 0x0008;
+ /* put message bit to output (MSB first) */
+ if (i >= 10)
+ output[(i - 10) / 8] = (output[(i - 10) / 8] << 1) | ((reg_data >> 4) & 1);
+ }
+}
+
diff --git a/src/nmt/hagelbarger.h b/src/nmt/hagelbarger.h
new file mode 100644
index 0000000..9076afc
--- /dev/null
+++ b/src/nmt/hagelbarger.h
@@ -0,0 +1,4 @@
+
+void hagelbarger_encode(const uint8_t *input, uint8_t *output, int length);
+void hagelbarger_decode(const uint8_t *input, uint8_t *output, int length);
+
diff --git a/src/nmt/nmt.c b/src/nmt/nmt.c
index 09f40e9..809a168 100644
--- a/src/nmt/nmt.c
+++ b/src/nmt/nmt.c
@@ -829,15 +829,21 @@ static void rx_mo_dialing(nmt_t *nmt, frame_t *frame)
/* max digits received */
if (len + 1 == sizeof(nmt->dialing))
break;
- /* received odd digit, but be already have odd number of digits */
if ((len & 1)) {
- if (nmt->rx_frame_count > 1)
+ /* received odd digit, but be already have odd number of digits */
+ if (nmt->rx_frame_count > 1) /* we lost even digit */
goto missing_digit;
break;
- } else if (len) {
- if (nmt->rx_frame_count > 3)
+ } else if (len) { /* complain only after first digit */
+ /* received odd digit, and we have even number of digits */
+ if (nmt->rx_frame_count > 3) /* we lost even digit */
goto missing_digit;
}
+ if ((frame->digit >> 12) != 0x00) /* digit 0x0 0x0, x, x, x */
+ goto not_right_position;
+ if (((frame->digit >> 8) & 0xf) != ((frame->digit >> 4) & 0xf)
+ || ((frame->digit >> 4) & 0xf) != (frame->digit & 0xf))
+ goto not_consistent_digit;
nmt->dialing[len] = nmt_value2digit(frame->digit);
nmt->dialing[len + 1] = '\0';
PDEBUG_CHAN(DNMT, DEBUG_INFO, "Received (odd) digit %c.\n", nmt->dialing[len]);
@@ -857,15 +863,24 @@ static void rx_mo_dialing(nmt_t *nmt, frame_t *frame)
/* max digits received */
if (len + 1 == sizeof(nmt->dialing))
break;
- /* received odd digit, but be already have odd number of digits */
+ /* received even digit, but no digit yet, so we lost first odd digit */
+ if (!len)
+ goto missing_digit;
if (!(len & 1)) {
- if (len && nmt->rx_frame_count > 1)
+ /* received even digit, but be already have even number of digits */
+ if (nmt->rx_frame_count > 1) /* we lost odd digit */
goto missing_digit;
break;
} else {
- if (nmt->rx_frame_count > 3)
+ /* received even digit, and we have odd number of digits */
+ if (nmt->rx_frame_count > 3) /* we lost odd digit */
goto missing_digit;
}
+ if ((frame->digit >> 12) != 0xff) /* digit 0xf 0xf, x, x, x */
+ goto not_right_position;
+ if (((frame->digit >> 8) & 0xf) != ((frame->digit >> 4) & 0xf)
+ || ((frame->digit >> 4) & 0xf) != (frame->digit & 0xf))
+ goto not_consistent_digit;
nmt->dialing[len] = nmt_value2digit(frame->digit);
nmt->dialing[len + 1] = '\0';
PDEBUG_CHAN(DNMT, DEBUG_INFO, "Received (even) digit %c.\n", nmt->dialing[len]);
@@ -906,6 +921,15 @@ static void rx_mo_dialing(nmt_t *nmt, frame_t *frame)
missing_digit:
PDEBUG_CHAN(DNMT, DEBUG_NOTICE, "Missing digit, aborting.\n");
nmt_release(nmt);
+ return;
+
+not_right_position:
+ PDEBUG_CHAN(DNMT, DEBUG_NOTICE, "Position information of digit does not match, ignoring due to corrupt frame.\n");
+ return;
+
+not_consistent_digit:
+ PDEBUG_CHAN(DNMT, DEBUG_NOTICE, "Digit repetition in frame does not match, ignoring due to corrupt frame.\n");
+ return;
}
static void tx_mo_complete(nmt_t *nmt, frame_t *frame)
@@ -1102,6 +1126,13 @@ static void rx_mt_autoanswer(nmt_t *nmt, frame_t *frame)
break;
if (!match_subscriber(trans, frame))
break;
+ if (((frame->line_signal >> 16) & 0xf) != ((frame->line_signal >> 12) & 0xf)
+ || ((frame->line_signal >> 12) & 0xf) != ((frame->line_signal >> 8) & 0xf)
+ || ((frame->line_signal >> 8) & 0xf) != ((frame->line_signal >> 4) & 0xf)
+ || ((frame->line_signal >> 4) & 0xf) != (frame->line_signal & 0xf)) {
+ PDEBUG_CHAN(DNMT, DEBUG_NOTICE, "Line signal repetition in frame does not match, ignoring due to corrupt frame.\n");
+ break;
+ }
if ((frame->line_signal & 0xf) != 12)
break;
PDEBUG_CHAN(DNMT, DEBUG_INFO, "Received acknowledge to autoanswer.\n");
@@ -1153,6 +1184,13 @@ static void rx_mt_ringing(nmt_t *nmt, frame_t *frame)
break;
if (!match_subscriber(trans, frame))
break;
+ if (((frame->line_signal >> 16) & 0xf) != ((frame->line_signal >> 12) & 0xf)
+ || ((frame->line_signal >> 12) & 0xf) != ((frame->line_signal >> 8) & 0xf)
+ || ((frame->line_signal >> 8) & 0xf) != ((frame->line_signal >> 4) & 0xf)
+ || ((frame->line_signal >> 4) & 0xf) != (frame->line_signal & 0xf)) {
+ PDEBUG_CHAN(DNMT, DEBUG_NOTICE, "Line signal repetition in frame does not match, ignoring due to corrupt frame.\n");
+ break;
+ }
if ((frame->line_signal & 0xf) != 14)
break;
PDEBUG_CHAN(DNMT, DEBUG_INFO, "Received 'answer' from phone.\n");
@@ -1240,6 +1278,13 @@ static void rx_mt_release(nmt_t *nmt, frame_t *frame)
break;
if (!match_subscriber(trans, frame))
break;
+ if (((frame->line_signal >> 16) & 0xf) != ((frame->line_signal >> 12) & 0xf)
+ || ((frame->line_signal >> 12) & 0xf) != ((frame->line_signal >> 8) & 0xf)
+ || ((frame->line_signal >> 8) & 0xf) != ((frame->line_signal >> 4) & 0xf)
+ || ((frame->line_signal >> 4) & 0xf) != (frame->line_signal & 0xf)) {
+ PDEBUG_CHAN(DNMT, DEBUG_NOTICE, "Line signal repetition in frame does not match, ignoring due to corrupt frame.\n");
+ break;
+ }
if ((frame->line_signal & 0xf) != 1)
break;
PDEBUG_CHAN(DNMT, DEBUG_INFO, "Received release guard.\n");
@@ -1312,6 +1357,13 @@ static void rx_active(nmt_t *nmt, frame_t *frame)
break;
if (!match_subscriber(trans, frame))
break;
+ if (((frame->line_signal >> 16) & 0xf) != ((frame->line_signal >> 12) & 0xf)
+ || ((frame->line_signal >> 12) & 0xf) != ((frame->line_signal >> 8) & 0xf)
+ || ((frame->line_signal >> 8) & 0xf) != ((frame->line_signal >> 4) & 0xf)
+ || ((frame->line_signal >> 4) & 0xf) != (frame->line_signal & 0xf)) {
+ PDEBUG_CHAN(DNMT, DEBUG_NOTICE, "Line signal repetition in frame does not match, ignoring due to corrupt frame.\n");
+ break;
+ }
switch ((frame->line_signal & 0xf)) {
case 5:
PDEBUG_CHAN(DNMT, DEBUG_NOTICE, "Register Recall is not supported.\n");
@@ -1344,6 +1396,15 @@ static void rx_active(nmt_t *nmt, frame_t *frame)
break;
if ((nmt->mft_num & 1))
break;
+ if ((frame->digit >> 12) != 0x00) {
+ PDEBUG_CHAN(DNMT, DEBUG_NOTICE, "Position information of digit does not match, ignoring due to corrupt frame.\n");
+ break;
+ }
+ if (((frame->digit >> 8) & 0xf) != ((frame->digit >> 4) & 0xf)
+ || ((frame->digit >> 4) & 0xf) != (frame->digit & 0xf)) {
+ PDEBUG_CHAN(DNMT, DEBUG_NOTICE, "Digit repetition in frame does not match, ignoring due to corrupt frame.\n");
+ break;
+ }
digit = nmt_value2digit(frame->digit);
dtmf_set_tone(&nmt->dtmf, digit);
PDEBUG_CHAN(DNMT, DEBUG_INFO, "Received (odd) digit %c.\n", digit);
@@ -1358,6 +1419,15 @@ static void rx_active(nmt_t *nmt, frame_t *frame)
break;
if (!(nmt->mft_num & 1))
break;
+ if ((frame->digit >> 12) != 0xff) {
+ PDEBUG_CHAN(DNMT, DEBUG_NOTICE, "Position information of digit does not match, ignoring due to corrupt frame.\n");
+ break;
+ }
+ if (((frame->digit >> 8) & 0xf) != ((frame->digit >> 4) & 0xf)
+ || ((frame->digit >> 4) & 0xf) != (frame->digit & 0xf)) {
+ PDEBUG_CHAN(DNMT, DEBUG_NOTICE, "Digit repetition in frame does not match, ignoring due to corrupt frame.\n");
+ break;
+ }
digit = nmt_value2digit(frame->digit);
dtmf_set_tone(&nmt->dtmf, digit);
PDEBUG_CHAN(DNMT, DEBUG_INFO, "Received (even) digit %c.\n", digit);
@@ -1431,6 +1501,13 @@ void nmt_receive_frame(nmt_t *nmt, const char *bits, double quality, double leve
return;
if (!match_subscriber(nmt->trans, &frame))
return;
+ if (((frame.line_signal >> 16) & 0xf) != ((frame.line_signal >> 12) & 0xf)
+ || ((frame.line_signal >> 12) & 0xf) != ((frame.line_signal >> 8) & 0xf)
+ || ((frame.line_signal >> 8) & 0xf) != ((frame.line_signal >> 4) & 0xf)
+ || ((frame.line_signal >> 4) & 0xf) != (frame.line_signal & 0xf)) {
+ PDEBUG_CHAN(DNMT, DEBUG_NOTICE, "Line signal repetition in frame does not match, ignoring due to corrupt frame.\n");
+ return;
+ }
PDEBUG_CHAN(DNMT, DEBUG_INFO, "Received clearing by mobile phone in state %s.\n", nmt_state_name(nmt->state));
nmt_new_state(nmt, STATE_MO_RELEASE);
nmt->tx_frame_count = 0;