[PATCH] osmo-bts[master]: osmo-bts-trx: add error concealment unit for GSM-FR

This is merely a historical archive of years 2008-2021, before the migration to mailman3.

A maintained and still updated list archive can be found at https://lists.osmocom.org/hyperkitty/list/gerrit-log@lists.osmocom.org/.

dexter gerrit-no-reply at lists.osmocom.org
Fri Dec 8 15:04:01 UTC 2017


Hello Neels Hofmeyr, Jenkins Builder,

I'd like you to reexamine a change.  Please visit

    https://gerrit.osmocom.org/5214

to look at the new patch set (#2).

osmo-bts-trx: add error concealment unit for GSM-FR

When a bad GSM-FR voice frame is received, the frame replaced
ith a silence frame. This may cause unpleasant audio effects.

Add a functionality (see ecu_fr.c, ecu_fr_conceal() and
ecu_fr_reset() to craft a replacement frame from the last
known good frame.

Add unit test (ecu_fr_test.c)

Integrate into osmo-bts-trx

Change-Id: Iae9e69a9578ae305bca42f834694af96a29084e6
---
M configure.ac
M include/osmo-bts/Makefile.am
A include/osmo-bts/ecu_fr.h
M include/osmo-bts/gsm_data_shared.h
M src/common/Makefile.am
A src/common/ecu_fr.c
M src/osmo-bts-trx/scheduler_trx.c
M tests/Makefile.am
A tests/ecu_fr/Makefile.am
A tests/ecu_fr/ecu_fr_test.c
A tests/ecu_fr/ecu_fr_test.ok
M tests/testsuite.at
12 files changed, 818 insertions(+), 5 deletions(-)


  git pull ssh://gerrit.osmocom.org:29418/osmo-bts refs/changes/14/5214/2

diff --git a/configure.ac b/configure.ac
index e3ea9ca..0d1a882 100644
--- a/configure.ac
+++ b/configure.ac
@@ -180,4 +180,5 @@
     tests/tx_power/Makefile
     tests/power/Makefile
     tests/meas/Makefile
+    tests/ecu_fr/Makefile
     Makefile)
diff --git a/include/osmo-bts/Makefile.am b/include/osmo-bts/Makefile.am
index a15ce3d..86bcdf1 100644
--- a/include/osmo-bts/Makefile.am
+++ b/include/osmo-bts/Makefile.am
@@ -2,4 +2,4 @@
 		 oml.h paging.h rsl.h signal.h vty.h amr.h pcu_if.h pcuif_proto.h \
 		 handover.h msg_utils.h tx_power.h control_if.h cbch.h l1sap.h \
 		 power_control.h scheduler.h scheduler_backend.h phy_link.h \
-		 dtx_dl_amr_fsm.h
+		 dtx_dl_amr_fsm.h ecu_fr.h
diff --git a/include/osmo-bts/ecu_fr.h b/include/osmo-bts/ecu_fr.h
new file mode 100644
index 0000000..f33f508
--- /dev/null
+++ b/include/osmo-bts/ecu_fr.h
@@ -0,0 +1,10 @@
+#pragma once
+
+/* Context information for the full rate ECU states */
+struct ecu_fr_ctx {
+	bool subsequent_lost_frame;
+	uint8_t frame_backup[GSM_FR_BYTES];
+};
+
+void ecu_fr_reset(struct ecu_fr_ctx *ctx, uint8_t *frame);
+void ecu_fr_conceal(struct ecu_fr_ctx *ctx, uint8_t *frame);
diff --git a/include/osmo-bts/gsm_data_shared.h b/include/osmo-bts/gsm_data_shared.h
index a05e4ca..37d6e0b 100644
--- a/include/osmo-bts/gsm_data_shared.h
+++ b/include/osmo-bts/gsm_data_shared.h
@@ -19,6 +19,7 @@
 #include <osmocom/gsm/protocol/gsm_12_21.h>
 
 #include <osmocom/abis/e1_input.h>
+#include <osmo-bts/ecu_fr.h>
 
 #ifndef ROLE_BSC
 #include <osmocom/gsm/lapdm.h>
@@ -381,6 +382,8 @@
 	} ms_power_ctrl;
 
 	struct msgb *pending_rel_ind_msg;
+
+	struct ecu_fr_ctx ecu_fr_ctx;
 #endif
 };
 
diff --git a/src/common/Makefile.am b/src/common/Makefile.am
index 67f3e80..d2d6ec6 100644
--- a/src/common/Makefile.am
+++ b/src/common/Makefile.am
@@ -12,6 +12,6 @@
 		   load_indication.c pcu_sock.c handover.c msg_utils.c \
 		   tx_power.c bts_ctrl_commands.c bts_ctrl_lookup.c \
 		   l1sap.c cbch.c power_control.c main.c phy_link.c \
-		   dtx_dl_amr_fsm.c
+		   dtx_dl_amr_fsm.c ecu_fr.c
 
 libl1sched_a_SOURCES = scheduler.c
diff --git a/src/common/ecu_fr.c b/src/common/ecu_fr.c
new file mode 100644
index 0000000..92228cd
--- /dev/null
+++ b/src/common/ecu_fr.c
@@ -0,0 +1,415 @@
+/* (C) 2017 by sysmocom - s.f.m.c. GmbH
+ * All Rights Reserved
+ *
+ * Author: Philipp Maier
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Affero 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 Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include <stdint.h>
+
+#include <osmo-bts/gsm_data.h>
+#include <osmo-bts/logging.h>
+#include <osmocom/core/talloc.h>
+#include <osmocom/core/utils.h>
+#include <osmocom/codec/codec.h>
+#include <osmocom/core/bitvec.h>
+#include <osmo-bts/ecu_fr.h>
+
+/* See also GSM 6.11, chapter 6 Example solution */
+#define GSM610_XMAXC_REDUCE 4
+#define GSM610_XMAXC_LEN 6
+
+/* See also RFC 3551 Table 3: GSM payload format: */
+enum gsm610_bit_offsets {
+	GSM610_SIGNATURE_0,
+	GSM610_SIGNATURE_1,
+	GSM610_SIGNATURE_2,
+	GSM610_SIGNATURE_3,
+	GSM610_LARC0_0,
+	GSM610_LARC0_1,
+	GSM610_LARC0_2,
+	GSM610_LARC0_3,
+	GSM610_LARC0_4,
+	GSM610_LARC0_5,
+	GSM610_LARC1_0,
+	GSM610_LARC1_1,
+	GSM610_LARC1_2,
+	GSM610_LARC1_3,
+	GSM610_LARC1_4,
+	GSM610_LARC1_5,
+	GSM610_LARC2_0,
+	GSM610_LARC2_1,
+	GSM610_LARC2_2,
+	GSM610_LARC2_3,
+	GSM610_LARC2_4,
+	GSM610_LARC3_0,
+	GSM610_LARC3_1,
+	GSM610_LARC3_2,
+	GSM610_LARC3_3,
+	GSM610_LARC3_4,
+	GSM610_LARC4_0,
+	GSM610_LARC4_1,
+	GSM610_LARC4_2,
+	GSM610_LARC4_3,
+	GSM610_LARC5_0,
+	GSM610_LARC5_1,
+	GSM610_LARC5_2,
+	GSM610_LARC5_3,
+	GSM610_LARC6_0,
+	GSM610_LARC6_1,
+	GSM610_LARC6_2,
+	GSM610_LARC7_0,
+	GSM610_LARC7_1,
+	GSM610_LARC7_2,
+	GSM610_NC0_0,
+	GSM610_NC0_1,
+	GSM610_NC0_2,
+	GSM610_NC0_3,
+	GSM610_NC0_4,
+	GSM610_NC0_5,
+	GSM610_NC0_6,
+	GSM610_BC0_0,
+	GSM610_BC0_1,
+	GSM610_MC0_0,
+	GSM610_MC0_1,
+	GSM610_XMAXC00,
+	GSM610_XMAXC01,
+	GSM610_XMAXC02,
+	GSM610_XMAXC03,
+	GSM610_XMAXC04,
+	GSM610_XMAXC05,
+	GSM610_XMC0_0,
+	GSM610_XMC0_1,
+	GSM610_XMC0_2,
+	GSM610_XMC1_0,
+	GSM610_XMC1_1,
+	GSM610_XMC1_2,
+	GSM610_XMC2_0,
+	GSM610_XMC2_1,
+	GSM610_XMC2_2,
+	GSM610_XMC3_0,
+	GSM610_XMC3_1,
+	GSM610_XMC3_2,
+	GSM610_XMC4_0,
+	GSM610_XMC4_1,
+	GSM610_XMC4_2,
+	GSM610_XMC5_0,
+	GSM610_XMC5_1,
+	GSM610_XMC5_2,
+	GSM610_XMC6_0,
+	GSM610_XMC6_1,
+	GSM610_XMC6_2,
+	GSM610_XMC7_0,
+	GSM610_XMC7_1,
+	GSM610_XMC7_2,
+	GSM610_XMC8_0,
+	GSM610_XMC8_1,
+	GSM610_XMC8_2,
+	GSM610_XMC9_0,
+	GSM610_XMC9_1,
+	GSM610_XMC9_2,
+	GSM610_XMC10_0,
+	GSM610_XMC10_1,
+	GSM610_XMC10_2,
+	GSM610_XMC11_0,
+	GSM610_XMC11_1,
+	GSM610_XMC11_2,
+	GSM610_XMC12_0,
+	GSM610_XMC12_1,
+	GSM610_XCM12_2,
+	GSM610_NC1_0,
+	GSM610_NC1_1,
+	GSM610_NC1_2,
+	GSM610_NC1_3,
+	GSM610_NC1_4,
+	GSM610_NC1_5,
+	GSM610_NC1_6,
+	GSM610_BC1_0,
+	GSM610_BC1_1,
+	GSM610_MC1_0,
+	GSM610_MC1_1,
+	GSM610_XMAXC10,
+	GSM610_XMAXC11,
+	GSM610_XMAXC12,
+	GSM610_XMAXC13,
+	GSM610_XMAXC14,
+	GSM610_XMAX15,
+	GSM610_XMC13_0,
+	GSM610_XMC13_1,
+	GSM610_XMC13_2,
+	GSM610_XMC14_0,
+	GSM610_XMC14_1,
+	GSM610_XMC14_2,
+	GSM610_XMC15_0,
+	GSM610_XMC15_1,
+	GSM610_XMC15_2,
+	GSM610_XMC16_0,
+	GSM610_XMC16_1,
+	GSM610_XMC16_2,
+	GSM610_XMC17_0,
+	GSM610_XMC17_1,
+	GSM610_XMC17_2,
+	GSM610_XMC18_0,
+	GSM610_XMC18_1,
+	GSM610_XMC18_2,
+	GSM610_XMC19_0,
+	GSM610_XMC19_1,
+	GSM610_XMC19_2,
+	GSM610_XMC20_0,
+	GSM610_XMC20_1,
+	GSM610_XMC20_2,
+	GSM610_XMC21_0,
+	GSM610_XMC21_1,
+	GSM610_XMC21_2,
+	GSM610_XMC22_0,
+	GSM610_XMC22_1,
+	GSM610_XMC22_2,
+	GSM610_XMC23_0,
+	GSM610_XMC23_1,
+	GSM610_XMC23_2,
+	GSM610_XMC24_0,
+	GSM610_XMC24_1,
+	GSM610_XMC24_2,
+	GSM610_XMC25_0,
+	GSM610_XMC25_1,
+	GSM610_XMC25_2,
+	GSM610_NC2_0,
+	GSM610_NC2_1,
+	GSM610_NC2_2,
+	GSM610_NC2_3,
+	GSM610_NC2_4,
+	GSM610_NC2_5,
+	GSM610_NC2_6,
+	GSM610_BC2_0,
+	GSM610_BC2_1,
+	GSM610_MC2_0,
+	GSM610_MC2_1,
+	GSM610_XMAXC20,
+	GSM610_XMAXC21,
+	GSM610_XMAXC22,
+	GSM610_XMAXC23,
+	GSM610_XMAXC24,
+	GSM610_XMAXC25,
+	GSM610_XMC26_0,
+	GSM610_XMC26_1,
+	GSM610_XMC26_2,
+	GSM610_XMC27_0,
+	GSM610_XMC27_1,
+	GSM610_XMC27_2,
+	GSM610_XMC28_0,
+	GSM610_XMC28_1,
+	GSM610_XMC28_2,
+	GSM610_XMC29_0,
+	GSM610_XMC29_1,
+	GSM610_XMC29_2,
+	GSM610_XMC30_0,
+	GSM610_XMC30_1,
+	GSM610_XMC30_2,
+	GSM610_XMC31_0,
+	GSM610_XMC31_1,
+	GSM610_XMC31_2,
+	GSM610_XMC32_0,
+	GSM610_XMC32_1,
+	GSM610_XMC32_2,
+	GSM610_XMC33_0,
+	GSM610_XMC33_1,
+	GSM610_XMC33_2,
+	GSM610_XMC34_0,
+	GSM610_XMC34_1,
+	GSM610_XMC34_2,
+	GSM610_XMC35_0,
+	GSM610_XMC35_1,
+	GSM610_XMC35_2,
+	GSM610_XMC36_0,
+	GSM610_XMC36_1,
+	GSM610_XMC36_2,
+	GSM610_XMC37_0,
+	GSM610_XMC37_1,
+	GSM610_XMC37_2,
+	GSM610_XMC38_0,
+	GSM610_XMC38_1,
+	GSM610_XMC38_2,
+	GSM610_NC3_0,
+	GSM610_NC3_1,
+	GSM610_NC3_2,
+	GSM610_NC3_3,
+	GSM610_NC3_4,
+	GSM610_NC3_5,
+	GSM610_NC3_6,
+	GSM610_BC3_0,
+	GSM610_BC3_1,
+	GSM610_MC3_0,
+	GSM610_MC3_1,
+	GSM610_XMAXC30,
+	GSM610_XMAXC31,
+	GSM610_XMAXC32,
+	GSM610_XMAXC33,
+	GSM610_XMAXC34,
+	GSM610_XMAXC35,
+	GSM610_XMC39_0,
+	GSM610_XMC39_1,
+	GSM610_XMC39_2,
+	GSM610_XMC40_0,
+	GSM610_XMC40_1,
+	GSM610_XMC40_2,
+	GSM610_XMC41_0,
+	GSM610_XMC41_1,
+	GSM610_XMC41_2,
+	GSM610_XMC42_0,
+	GSM610_XMC42_1,
+	GSM610_XMC42_2,
+	GSM610_XMC43_0,
+	GSM610_XMC43_1,
+	GSM610_XMC43_2,
+	GSM610_XMC44_0,
+	GSM610_XMC44_1,
+	GSM610_XMC44_2,
+	GSM610_XMC45_0,
+	GSM610_XMC45_1,
+	GSM610_XMC45_2,
+	GSM610_XMC46_0,
+	GSM610_XMC46_1,
+	GSM610_XMC46_2,
+	GSM610_XMC47_0,
+	GSM610_XMC47_1,
+	GSM610_XMC47_2,
+	GSM610_XMC48_0,
+	GSM610_XMC48_1,
+	GSM610_XMC48_2,
+	GSM610_XMC49_0,
+	GSM610_XMC49_1,
+	GSM610_XMC49_2,
+	GSM610_XMC50_0,
+	GSM610_XMC50_1,
+	GSM610_XMC50_2,
+	GSM610_XMC51_0,
+	GSM610_XMC51_1,
+	GSM610_XMC51_2
+};
+
+/* Reduce the XMAXC field. When the XMAXC field reaches
+ * zero, then the function will return true */
+static bool reduce_xmaxcr(struct bitvec *frame_bitvec, unsigned int index)
+{
+	unsigned int field_index;
+	uint64_t field;
+	bool silent = true;
+
+	field_index = index;
+	field = bitvec_read_field(frame_bitvec, &field_index, GSM610_XMAXC_LEN);
+	if (field > GSM610_XMAXC_REDUCE)
+		field -= GSM610_XMAXC_REDUCE;
+	else
+		field = 0;
+	if (field != 0)
+		silent = false;
+	field_index = index;
+	bitvec_write_field(frame_bitvec, &field_index, field, GSM610_XMAXC_LEN);
+
+	return silent;
+}
+
+/* Reduce all XMAXC fields in the frame. When all XMAXC fields reach
+ * zero, then the function will return true */
+static bool reduce_xmaxcr_all(struct bitvec *frame_bitvec)
+{
+	bool silent = false;
+
+	if (reduce_xmaxcr(frame_bitvec, GSM610_XMAXC00))
+		silent = true;
+	if (reduce_xmaxcr(frame_bitvec, GSM610_XMAXC10))
+		silent = true;
+	if (reduce_xmaxcr(frame_bitvec, GSM610_XMAXC20))
+		silent = true;
+	if (reduce_xmaxcr(frame_bitvec, GSM610_XMAXC30))
+		silent = true;
+	return silent;
+}
+
+/* Use certain modifications to conceal the errors in a full rate frame */
+static void conceal_frame(uint8_t *frame)
+{
+	struct bitvec *frame_bitvec;
+	bool silent;
+	int rc;
+
+	/* In case we already deal with a silent frame,
+	 * there is nothing to, we just abort immediately */
+	if (osmo_fr_check_sid(frame, GSM_FR_BYTES)) {
+		goto leave_nofree;
+	}
+
+	/* Convert to bitvec */
+	frame_bitvec = bitvec_alloc(GSM_FR_BYTES, NULL);
+	if (!frame_bitvec) {
+		LOGP(DL1P, LOGL_ERROR, "Unpacking GSM-FR frame failed, no memory to store the resulting bitvec!\n");
+		goto leave_nofree;
+	}
+	rc = bitvec_unpack(frame_bitvec, frame);
+	if (rc != GSM_FR_BYTES) {
+		LOGP(DL1P, LOGL_ERROR, "Unpacking GSM-FR frame failed, wrong number of bits unpacked!\n");
+		goto leave;
+	}
+
+	/* Fudge frame parameters */
+	silent = reduce_xmaxcr_all(frame_bitvec);
+
+	/* If we reached silence level, mute the frame
+	 * completely, this also means that we can
+	 * save the bitvec_pack operation */
+	if (silent) {
+		memset(frame, 0, GSM_FR_BYTES);
+		frame[0] = 0xd0;
+		goto leave;
+	}
+
+	/* Convert back to packed byte form */
+	rc = bitvec_pack(frame_bitvec, frame);
+	if (rc != GSM_FR_BYTES) {
+		LOGP(DL1P, LOGL_ERROR, "Packing GSM-FR frame failed, wrong number of bits packed!\n");
+		goto leave;
+	}
+
+leave:
+	bitvec_free(frame_bitvec);
+leave_nofree:
+	return;
+}
+
+/* To be called whan a good frame is received. This function will then create
+ * a backup of the frame and reset its internal state */
+void ecu_fr_reset(struct ecu_fr_ctx *ctx, uint8_t *frame)
+{
+	ctx->subsequent_lost_frame = false;
+	memcpy(ctx->frame_backup, frame, sizeof(ctx->frame_backup));
+}
+
+/* To be called when a bad frame is received. This function will then generate
+ * a replacement frame that can be used to conceal the dropout */
+void ecu_fr_conceal(struct ecu_fr_ctx *ctx, uint8_t *frame)
+{
+	/* For subsequent frames we run the error concealment
+	 * functions on the backed up frame before we restore
+	 * the backup. */
+	if (ctx->subsequent_lost_frame) {
+		conceal_frame(ctx->frame_backup);
+	}
+
+	/* Restore the backed up frame and set flag in case
+	 * we receive even more bad frames */
+	memcpy(frame, ctx->frame_backup, sizeof(ctx->frame_backup));
+	ctx->subsequent_lost_frame = true;
+}
diff --git a/src/osmo-bts-trx/scheduler_trx.c b/src/osmo-bts-trx/scheduler_trx.c
index d3928f1..dfcd031 100644
--- a/src/osmo-bts-trx/scheduler_trx.c
+++ b/src/osmo-bts-trx/scheduler_trx.c
@@ -968,6 +968,7 @@
 	uint8_t tch_data[128]; /* just to be safe */
 	int rc, amr = 0;
 	int n_errors, n_bits_total;
+	bool bfi_flag = false;
 	struct gsm_lchan *lchan =
 		get_lchan_by_chan_nr(l1t->trx, trx_chan_desc[chan].chan_nr | tn);
 
@@ -1058,11 +1059,13 @@
 	if (rc < 0) {
 		LOGL1S(DL1P, LOGL_NOTICE, l1t, tn, chan, fn, "Received bad data (%u/%u)\n",
 			fn % l1ts->mf_period, l1ts->mf_period);
+		bfi_flag = true;
 		goto bfi;
 	}
 	if (rc < 4) {
 		LOGL1S(DL1P, LOGL_NOTICE, l1t, tn, chan, fn, "Received bad data (%u/%u) "
 			"with invalid codec mode %d\n", fn % l1ts->mf_period, l1ts->mf_period, rc);
+		bfi_flag = true;
 		goto bfi;
 	}
 
@@ -1079,8 +1082,7 @@
 			case GSM48_CMODE_SPEECH_V1: /* FR */
 				if (lchan->tch.dtx.ul_sid)
 					return 0; /* DTXu: pause in progress */
-				memset(tch_data, 0, GSM_FR_BYTES);
-				tch_data[0] = 0xd0;
+				ecu_fr_conceal(&lchan->ecu_fr_ctx, tch_data);
 				rc = GSM_FR_BYTES;
 				break;
 			case GSM48_CMODE_SPEECH_EFR: /* EFR */
@@ -1108,6 +1110,9 @@
 	if (rsl_cmode != RSL_CMOD_SPD_SPEECH)
 		return 0;
 
+	if (!bfi_flag && tch_mode == GSM48_CMODE_SPEECH_V1)
+		ecu_fr_reset(&lchan->ecu_fr_ctx, tch_data);
+
 	/* TCH or BFI */
 	return _sched_compose_tch_ind(l1t, tn, (fn + GSM_HYPERFRAME - 7) % GSM_HYPERFRAME, chan,
 		tch_data, rc);
diff --git a/tests/Makefile.am b/tests/Makefile.am
index 1eb28d6..e77a355 100644
--- a/tests/Makefile.am
+++ b/tests/Makefile.am
@@ -1,4 +1,4 @@
-SUBDIRS = paging cipher agch misc handover tx_power power meas
+SUBDIRS = paging cipher agch misc handover tx_power power meas ecu_fr
 
 if ENABLE_SYSMOBTS
 SUBDIRS += sysmobts
diff --git a/tests/ecu_fr/Makefile.am b/tests/ecu_fr/Makefile.am
new file mode 100644
index 0000000..6dc7549
--- /dev/null
+++ b/tests/ecu_fr/Makefile.am
@@ -0,0 +1,8 @@
+AM_CPPFLAGS = $(all_includes) -I$(top_srcdir)/include
+AM_CFLAGS = -Wall $(LIBOSMOCORE_CFLAGS) $(LIBOSMOGSM_CFLAGS) $(LIBOSMOCODEC_CFLAGS)$(LIBOSMOTRAU_CFLAGS)
+LDADD = $(LIBOSMOCORE_LIBS) $(LIBOSMOGSM_LIBS) $(LIBOSMOCODEC_LIBS) $(LIBOSMOTRAU_LIBS) $(LIBOSMOABIS_LIBS)
+noinst_PROGRAMS = ecu_fr_test
+EXTRA_DIST = ecu_fr_test.ok
+
+ecu_fr_test_SOURCES = ecu_fr_test.c
+ecu_fr_test_LDADD = $(top_builddir)/src/common/libbts.a $(LDADD)
diff --git a/tests/ecu_fr/ecu_fr_test.c b/tests/ecu_fr/ecu_fr_test.c
new file mode 100644
index 0000000..bc1cc56
--- /dev/null
+++ b/tests/ecu_fr/ecu_fr_test.c
@@ -0,0 +1,171 @@
+#include <stdint.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <getopt.h>
+#include <limits.h>
+#include <sched.h>
+#include <sys/signal.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/ioctl.h>
+
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <net/if.h>
+
+#include <osmocom/core/talloc.h>
+#include <osmocom/core/application.h>
+#include <osmocom/vty/telnet_interface.h>
+#include <osmocom/vty/logging.h>
+
+#include <osmo-bts/gsm_data.h>
+#include <osmo-bts/logging.h>
+#include <osmo-bts/ecu_fr.h>
+
+/* Set with sample fullrate voice frames and some intentional dropouts. */
+char *fr_voice[] = {
+	"d9aa93ae63de00471a91b95b8660471392b4a2daa037628f391c624039258dc723",
+	"d8eb83699a66c036ec89b7246e6034dc8d48948620589b7256e3a6603b2371b8da",
+	"d967abaa1cbe4035238da6ace4c036d46ec69ba600391c4eb8a2b040591c6a3924",
+	"d8e8a42662c240472469b91bd2e0452291b6dba600495b8e38dcb020491a71c91b",
+	"da2aac1ddbb00036e46e26dcec6039138db923822047137248e3560048e38dc8e3",
+	"d929ab2a9b5240395b6dc72ba020469c8d551c5440349c9148e36a4036a372471b",
+	"d9eb93215bb8a0271c69c724682036db71c71a94a0372491b72bee4044eb71b923",
+	"d9ab9aa19abc40391b6e5ae2ee40471b91c6dbe820492291b8e4b84036e47238db",
+	"d96b9be9db782044e371b55cb200389491c69b8ea034e271c8d3808038ec6db8e3",
+	"d9aa9365e3f060375c6db6ebc4c02764b1c51b78a0571c91a723de6049248dc8dd",
+	"BAD",
+	"d9ea9c219ce60046e38d3724e0c034e56e36eb7e0038d471b8dcb260491b8dbb23",
+	"d9e89be9d9e0a0391b6dd6a4624029247138e3a2a04713922524de0036db69d91c",
+	"d9699422a2b6a048dd90c91c6a802b6259395c8880575b4a58e4ac20269d7248d4",
+	"d967ac5b1baae0371c71b8ab9c804a9e8e58a55a8038626ec8dcb640395c7244dc",
+	"d9e8a3e262e68027638db52b88a038634e471a7ec049136e3b1bc8402923adcad2",
+	"d8eab36e1bbe0046e34d491b608035137658d3524044e48e375cdac0472b9238d4",
+	"d9689ba5e3d260491b516adb5e4027256e27227ee0351c8e549a5c60492471971b",
+	"BAD",
+	"BAD",
+	"d8e6a2e1d3d2605b1376c8d35280392451391cbc80392a71b6db8aa049238dc8ab",
+	"d9a87ba1a3982048eb8a471cac00472b4e391bbc40292489b71cc200495b8d3ae3",
+	"d9278b2a1ba4c0475b8dc722d6e0491b5228da70204ae36dc71d94a056a29236e3",
+	"d9ec9be2129520392335598c50c04b5bad3d4ba680789b69df5a5aa0469cd1b4da",
+	"d8ea932623e660669b8e4a9dd8a03aa32a76e466e028d396cc9bbe4047256dc8e5",
+	"d96a94215aa0403aab713f22e8e024e68db91ab6a027abd1a55b6e804aec9146e4",
+	"d867ac21e270a0350d6ac91a724037247246d2a6c0396c89d6dc562049244e48d5",
+	"d8a9b460d3b48026a4ad471b7c20452491b69bbc803ae48db722ee00292491a8db",
+	"d928a3e1d3b24036e37244abf02047634d371b74c047637148a29ac03b234e38e3",
+	"d9ab9b21d2e0c0471c693aec54e044dbae46dc7c20391badb724ee8038e469bb15",
+	"d9a99361a276403b1a6ad6dcd40026e489c8e3bc40371c4dc564e2c036e28eb963",
+	"BAD",
+	"BAD",
+	"BAD",
+	"BAD",
+	"BAD",
+	"BAD",
+	"d92c8b6d5aee4034ebb22724862047145634a5c0a038e371b8e4a880485c89dd25",
+	"d8e78b29e3c6c038dba9d91beca04723ad491cda80471471b6ec7ae03b1396b91b",
+	"d8a78b25e37a0022dd8a46dc68a0351bad391bde2046e56dd8dc96c038e396d89b",
+	"d8a88c255ab6e038e38e48dbde8038ad8dc8db8ec0376372b564b44038e49234dc",
+	"d9708ce6a39ce049646646a2c1a0272496b29a66c037db562863ace0795b55b2e3",
+	"d8ee9bea5ae4003ae371b713eae05adc91995a5ea064dcc9571e786026ed51c52c",
+	"d9299421d2944036ed69b8e572a048e36d551cd480571d4ec95be680356c69c763",
+	"d92aab696190c046e26e392cae0026a376a8dc662048d291b75b54c04ad3ae3b1b",
+	"d8e7a469627a6038e289cb1baca0569b8db6dddec026dc8e38e5dc803722722d23",
+	"d8a88c299b64c03a548a58e37420272c6dd76b92c0471c9236dbc0e0551c71c713",
+	"BAD",
+	"d7299c19a3be8024e58ea7a49f20a522963ad976e0a76ecd92b38500cb62aa4c94",
+	"d7eb6c6262eee02b2c42e79a60a0aa55aed68a7f00ad358e10fad960e55a39396d",
+	"d970858dd2ab61d91355ebc15ca1a6a7ca48a05cc0dae66f2523c2a1bad3825daa",
+	"d8f0844a23ad20da50d6de025e81c37392b9039cc0c764c1bd1e94c1b699736a98",
+	"d9708ce6a39ce049646646a2c1a0272496b29a66c037db562863ace0795b55b2e3",
+	"d9299421d2944036ed69b8e572a048e36d551cd480571d4ec95be680356c69c763",
+	"d9299421d2944036ed69b8e572a048e36d551cd480571d4ec95be680356c69c763",
+	"d9299421d2944036ed69b8e572a048e36d551cd480571d4ec95be680356c69c763",
+	"d2577a1cda50004924924924500049249249245000492492492450004923924924",
+	NULL
+};
+
+/* Start with a good voice frame and then simulate 20 consecutive bad frames,
+ * watch how the error concealment decreases the XMAXC parameters */
+void test_fr_concealment(void *ctx)
+{
+	char frame_hex[] =
+	    "d9ec9be212901f802335598c501f805bad3d4ba01f809b69df5a501f809cd1b4da";
+	uint8_t *frame;
+	unsigned int len;
+	int i;
+	struct ecu_fr_ctx ecu_fr_ctx;
+
+	len = strlen(frame_hex);
+	frame = talloc_zero_size(ctx, GSM_FR_BYTES);
+	len = osmo_hexparse(frame_hex, frame, GSM_FR_BYTES);
+	OSMO_ASSERT(len == GSM_FR_BYTES);
+
+	printf("         Start with: %s\n",
+	       osmo_hexdump_nospc(frame, GSM_FR_BYTES));
+
+	/* Reset the ECU with the proposed known good frame */
+	ecu_fr_reset(&ecu_fr_ctx, frame);
+
+	/* Now pretend that we do not receive any good frames anymore */
+	for (i = 0; i < 20; i++) {
+		ecu_fr_conceal(&ecu_fr_ctx, frame);
+		printf("conceal: %02i, result: %s\n", i,
+		       osmo_hexdump_nospc(frame, GSM_FR_BYTES));
+	}
+
+	talloc_free(frame);
+}
+
+/* Simulate a real life situation, voce frames with a few dropouts */
+void test_fr_concealment_realistic()
+{
+	uint8_t frame[GSM_FR_BYTES];
+	unsigned int len;
+	unsigned int id = 0;
+	struct ecu_fr_ctx ecu_fr_ctx;
+
+	while (1) {
+		/* Detect end of test samples */
+		if (fr_voice[id] == NULL)
+			return;
+
+		printf("Frame No. %03i:\n", id);
+		len = strlen(fr_voice[id]) / 2;
+
+		if (len != GSM_FR_BYTES) {
+			memset(frame, 0, GSM_FR_BYTES);
+			ecu_fr_conceal(&ecu_fr_ctx, frame);
+			printf(" * input:  (bad)\n");
+		} else {
+			osmo_hexparse(fr_voice[id], frame, GSM_FR_BYTES);
+			printf(" * input:  %s\n",
+			       osmo_hexdump_nospc(frame, GSM_FR_BYTES));
+			ecu_fr_reset(&ecu_fr_ctx, frame);
+		}
+		printf(" * output: %s\n",
+		       osmo_hexdump_nospc(frame, GSM_FR_BYTES));
+		id++;
+	}
+}
+
+int main(int argc, char **argv)
+{
+	void *tall_bts_ctx;
+
+	tall_bts_ctx = talloc_named_const(NULL, 1, "OsmoBTS context");
+	msgb_talloc_ctx_init(tall_bts_ctx, 0);
+
+	bts_log_init(NULL);
+	osmo_stderr_target->categories[DL1C].loglevel = LOGL_DEBUG;
+
+	test_fr_concealment(tall_bts_ctx);
+	test_fr_concealment_realistic();
+
+	printf("Success\n");
+	talloc_report_full(tall_bts_ctx, stderr);
+	printf("Talloc total blocks: %lu\n", talloc_total_blocks(tall_bts_ctx));
+	OSMO_ASSERT(talloc_total_blocks(tall_bts_ctx) == 2);
+
+	return 0;
+}
diff --git a/tests/ecu_fr/ecu_fr_test.ok b/tests/ecu_fr/ecu_fr_test.ok
new file mode 100644
index 0000000..fab883e
--- /dev/null
+++ b/tests/ecu_fr/ecu_fr_test.ok
@@ -0,0 +1,194 @@
+         Start with: d9ec9be212901f802335598c501f805bad3d4ba01f809b69df5a501f809cd1b4da
+conceal: 00, result: d9ec9be212901f802335598c501f805bad3d4ba01f809b69df5a501f809cd1b4da
+conceal: 01, result: d9ec9be212901d802335598c501d805bad3d4ba01d809b69df5a501d809cd1b4da
+conceal: 02, result: d9ec9be212901b802335598c501b805bad3d4ba01b809b69df5a501b809cd1b4da
+conceal: 03, result: d9ec9be2129019802335598c5019805bad3d4ba019809b69df5a5019809cd1b4da
+conceal: 04, result: d9ec9be2129017802335598c5017805bad3d4ba017809b69df5a5017809cd1b4da
+conceal: 05, result: d9ec9be2129015802335598c5015805bad3d4ba015809b69df5a5015809cd1b4da
+conceal: 06, result: d9ec9be2129013802335598c5013805bad3d4ba013809b69df5a5013809cd1b4da
+conceal: 07, result: d9ec9be2129011802335598c5011805bad3d4ba011809b69df5a5011809cd1b4da
+conceal: 08, result: d9ec9be212900f802335598c500f805bad3d4ba00f809b69df5a500f809cd1b4da
+conceal: 09, result: d9ec9be212900d802335598c500d805bad3d4ba00d809b69df5a500d809cd1b4da
+conceal: 10, result: d9ec9be212900b802335598c500b805bad3d4ba00b809b69df5a500b809cd1b4da
+conceal: 11, result: d9ec9be2129009802335598c5009805bad3d4ba009809b69df5a5009809cd1b4da
+conceal: 12, result: d9ec9be2129007802335598c5007805bad3d4ba007809b69df5a5007809cd1b4da
+conceal: 13, result: d9ec9be2129005802335598c5005805bad3d4ba005809b69df5a5005809cd1b4da
+conceal: 14, result: d9ec9be2129003802335598c5003805bad3d4ba003809b69df5a5003809cd1b4da
+conceal: 15, result: d9ec9be2129001802335598c5001805bad3d4ba001809b69df5a5001809cd1b4da
+conceal: 16, result: d00000000000000000000000000000000000000000000000000000000000000000
+conceal: 17, result: d00000000000000000000000000000000000000000000000000000000000000000
+conceal: 18, result: d00000000000000000000000000000000000000000000000000000000000000000
+conceal: 19, result: d00000000000000000000000000000000000000000000000000000000000000000
+Frame No. 000:
+ * input:  d9aa93ae63de00471a91b95b8660471392b4a2daa037628f391c624039258dc723
+ * output: d9aa93ae63de00471a91b95b8660471392b4a2daa037628f391c624039258dc723
+Frame No. 001:
+ * input:  d8eb83699a66c036ec89b7246e6034dc8d48948620589b7256e3a6603b2371b8da
+ * output: d8eb83699a66c036ec89b7246e6034dc8d48948620589b7256e3a6603b2371b8da
+Frame No. 002:
+ * input:  d967abaa1cbe4035238da6ace4c036d46ec69ba600391c4eb8a2b040591c6a3924
+ * output: d967abaa1cbe4035238da6ace4c036d46ec69ba600391c4eb8a2b040591c6a3924
+Frame No. 003:
+ * input:  d8e8a42662c240472469b91bd2e0452291b6dba600495b8e38dcb020491a71c91b
+ * output: d8e8a42662c240472469b91bd2e0452291b6dba600495b8e38dcb020491a71c91b
+Frame No. 004:
+ * input:  da2aac1ddbb00036e46e26dcec6039138db923822047137248e3560048e38dc8e3
+ * output: da2aac1ddbb00036e46e26dcec6039138db923822047137248e3560048e38dc8e3
+Frame No. 005:
+ * input:  d929ab2a9b5240395b6dc72ba020469c8d551c5440349c9148e36a4036a372471b
+ * output: d929ab2a9b5240395b6dc72ba020469c8d551c5440349c9148e36a4036a372471b
+Frame No. 006:
+ * input:  d9eb93215bb8a0271c69c724682036db71c71a94a0372491b72bee4044eb71b923
+ * output: d9eb93215bb8a0271c69c724682036db71c71a94a0372491b72bee4044eb71b923
+Frame No. 007:
+ * input:  d9ab9aa19abc40391b6e5ae2ee40471b91c6dbe820492291b8e4b84036e47238db
+ * output: d9ab9aa19abc40391b6e5ae2ee40471b91c6dbe820492291b8e4b84036e47238db
+Frame No. 008:
+ * input:  d96b9be9db782044e371b55cb200389491c69b8ea034e271c8d3808038ec6db8e3
+ * output: d96b9be9db782044e371b55cb200389491c69b8ea034e271c8d3808038ec6db8e3
+Frame No. 009:
+ * input:  d9aa9365e3f060375c6db6ebc4c02764b1c51b78a0571c91a723de6049248dc8dd
+ * output: d9aa9365e3f060375c6db6ebc4c02764b1c51b78a0571c91a723de6049248dc8dd
+Frame No. 010:
+ * input:  (bad)
+ * output: d9aa9365e3f060375c6db6ebc4c02764b1c51b78a0571c91a723de6049248dc8dd
+Frame No. 011:
+ * input:  d9ea9c219ce60046e38d3724e0c034e56e36eb7e0038d471b8dcb260491b8dbb23
+ * output: d9ea9c219ce60046e38d3724e0c034e56e36eb7e0038d471b8dcb260491b8dbb23
+Frame No. 012:
+ * input:  d9e89be9d9e0a0391b6dd6a4624029247138e3a2a04713922524de0036db69d91c
+ * output: d9e89be9d9e0a0391b6dd6a4624029247138e3a2a04713922524de0036db69d91c
+Frame No. 013:
+ * input:  d9699422a2b6a048dd90c91c6a802b6259395c8880575b4a58e4ac20269d7248d4
+ * output: d9699422a2b6a048dd90c91c6a802b6259395c8880575b4a58e4ac20269d7248d4
+Frame No. 014:
+ * input:  d967ac5b1baae0371c71b8ab9c804a9e8e58a55a8038626ec8dcb640395c7244dc
+ * output: d967ac5b1baae0371c71b8ab9c804a9e8e58a55a8038626ec8dcb640395c7244dc
+Frame No. 015:
+ * input:  d9e8a3e262e68027638db52b88a038634e471a7ec049136e3b1bc8402923adcad2
+ * output: d9e8a3e262e68027638db52b88a038634e471a7ec049136e3b1bc8402923adcad2
+Frame No. 016:
+ * input:  d8eab36e1bbe0046e34d491b608035137658d3524044e48e375cdac0472b9238d4
+ * output: d8eab36e1bbe0046e34d491b608035137658d3524044e48e375cdac0472b9238d4
+Frame No. 017:
+ * input:  d9689ba5e3d260491b516adb5e4027256e27227ee0351c8e549a5c60492471971b
+ * output: d9689ba5e3d260491b516adb5e4027256e27227ee0351c8e549a5c60492471971b
+Frame No. 018:
+ * input:  (bad)
+ * output: d9689ba5e3d260491b516adb5e4027256e27227ee0351c8e549a5c60492471971b
+Frame No. 019:
+ * input:  (bad)
+ * output: d00000000000000000000000000000000000000000000000000000000000000000
+Frame No. 020:
+ * input:  d8e6a2e1d3d2605b1376c8d35280392451391cbc80392a71b6db8aa049238dc8ab
+ * output: d8e6a2e1d3d2605b1376c8d35280392451391cbc80392a71b6db8aa049238dc8ab
+Frame No. 021:
+ * input:  d9a87ba1a3982048eb8a471cac00472b4e391bbc40292489b71cc200495b8d3ae3
+ * output: d9a87ba1a3982048eb8a471cac00472b4e391bbc40292489b71cc200495b8d3ae3
+Frame No. 022:
+ * input:  d9278b2a1ba4c0475b8dc722d6e0491b5228da70204ae36dc71d94a056a29236e3
+ * output: d9278b2a1ba4c0475b8dc722d6e0491b5228da70204ae36dc71d94a056a29236e3
+Frame No. 023:
+ * input:  d9ec9be2129520392335598c50c04b5bad3d4ba680789b69df5a5aa0469cd1b4da
+ * output: d9ec9be2129520392335598c50c04b5bad3d4ba680789b69df5a5aa0469cd1b4da
+Frame No. 024:
+ * input:  d8ea932623e660669b8e4a9dd8a03aa32a76e466e028d396cc9bbe4047256dc8e5
+ * output: d8ea932623e660669b8e4a9dd8a03aa32a76e466e028d396cc9bbe4047256dc8e5
+Frame No. 025:
+ * input:  d96a94215aa0403aab713f22e8e024e68db91ab6a027abd1a55b6e804aec9146e4
+ * output: d96a94215aa0403aab713f22e8e024e68db91ab6a027abd1a55b6e804aec9146e4
+Frame No. 026:
+ * input:  d867ac21e270a0350d6ac91a724037247246d2a6c0396c89d6dc562049244e48d5
+ * output: d867ac21e270a0350d6ac91a724037247246d2a6c0396c89d6dc562049244e48d5
+Frame No. 027:
+ * input:  d8a9b460d3b48026a4ad471b7c20452491b69bbc803ae48db722ee00292491a8db
+ * output: d8a9b460d3b48026a4ad471b7c20452491b69bbc803ae48db722ee00292491a8db
+Frame No. 028:
+ * input:  d928a3e1d3b24036e37244abf02047634d371b74c047637148a29ac03b234e38e3
+ * output: d928a3e1d3b24036e37244abf02047634d371b74c047637148a29ac03b234e38e3
+Frame No. 029:
+ * input:  d9ab9b21d2e0c0471c693aec54e044dbae46dc7c20391badb724ee8038e469bb15
+ * output: d9ab9b21d2e0c0471c693aec54e044dbae46dc7c20391badb724ee8038e469bb15
+Frame No. 030:
+ * input:  d9a99361a276403b1a6ad6dcd40026e489c8e3bc40371c4dc564e2c036e28eb963
+ * output: d9a99361a276403b1a6ad6dcd40026e489c8e3bc40371c4dc564e2c036e28eb963
+Frame No. 031:
+ * input:  (bad)
+ * output: d9a99361a276403b1a6ad6dcd40026e489c8e3bc40371c4dc564e2c036e28eb963
+Frame No. 032:
+ * input:  (bad)
+ * output: d00000000000000000000000000000000000000000000000000000000000000000
+Frame No. 033:
+ * input:  (bad)
+ * output: d00000000000000000000000000000000000000000000000000000000000000000
+Frame No. 034:
+ * input:  (bad)
+ * output: d00000000000000000000000000000000000000000000000000000000000000000
+Frame No. 035:
+ * input:  (bad)
+ * output: d00000000000000000000000000000000000000000000000000000000000000000
+Frame No. 036:
+ * input:  (bad)
+ * output: d00000000000000000000000000000000000000000000000000000000000000000
+Frame No. 037:
+ * input:  d92c8b6d5aee4034ebb22724862047145634a5c0a038e371b8e4a880485c89dd25
+ * output: d92c8b6d5aee4034ebb22724862047145634a5c0a038e371b8e4a880485c89dd25
+Frame No. 038:
+ * input:  d8e78b29e3c6c038dba9d91beca04723ad491cda80471471b6ec7ae03b1396b91b
+ * output: d8e78b29e3c6c038dba9d91beca04723ad491cda80471471b6ec7ae03b1396b91b
+Frame No. 039:
+ * input:  d8a78b25e37a0022dd8a46dc68a0351bad391bde2046e56dd8dc96c038e396d89b
+ * output: d8a78b25e37a0022dd8a46dc68a0351bad391bde2046e56dd8dc96c038e396d89b
+Frame No. 040:
+ * input:  d8a88c255ab6e038e38e48dbde8038ad8dc8db8ec0376372b564b44038e49234dc
+ * output: d8a88c255ab6e038e38e48dbde8038ad8dc8db8ec0376372b564b44038e49234dc
+Frame No. 041:
+ * input:  d9708ce6a39ce049646646a2c1a0272496b29a66c037db562863ace0795b55b2e3
+ * output: d9708ce6a39ce049646646a2c1a0272496b29a66c037db562863ace0795b55b2e3
+Frame No. 042:
+ * input:  d8ee9bea5ae4003ae371b713eae05adc91995a5ea064dcc9571e786026ed51c52c
+ * output: d8ee9bea5ae4003ae371b713eae05adc91995a5ea064dcc9571e786026ed51c52c
+Frame No. 043:
+ * input:  d9299421d2944036ed69b8e572a048e36d551cd480571d4ec95be680356c69c763
+ * output: d9299421d2944036ed69b8e572a048e36d551cd480571d4ec95be680356c69c763
+Frame No. 044:
+ * input:  d92aab696190c046e26e392cae0026a376a8dc662048d291b75b54c04ad3ae3b1b
+ * output: d92aab696190c046e26e392cae0026a376a8dc662048d291b75b54c04ad3ae3b1b
+Frame No. 045:
+ * input:  d8e7a469627a6038e289cb1baca0569b8db6dddec026dc8e38e5dc803722722d23
+ * output: d8e7a469627a6038e289cb1baca0569b8db6dddec026dc8e38e5dc803722722d23
+Frame No. 046:
+ * input:  d8a88c299b64c03a548a58e37420272c6dd76b92c0471c9236dbc0e0551c71c713
+ * output: d8a88c299b64c03a548a58e37420272c6dd76b92c0471c9236dbc0e0551c71c713
+Frame No. 047:
+ * input:  (bad)
+ * output: d8a88c299b64c03a548a58e37420272c6dd76b92c0471c9236dbc0e0551c71c713
+Frame No. 048:
+ * input:  d7299c19a3be8024e58ea7a49f20a522963ad976e0a76ecd92b38500cb62aa4c94
+ * output: d7299c19a3be8024e58ea7a49f20a522963ad976e0a76ecd92b38500cb62aa4c94
+Frame No. 049:
+ * input:  d7eb6c6262eee02b2c42e79a60a0aa55aed68a7f00ad358e10fad960e55a39396d
+ * output: d7eb6c6262eee02b2c42e79a60a0aa55aed68a7f00ad358e10fad960e55a39396d
+Frame No. 050:
+ * input:  d970858dd2ab61d91355ebc15ca1a6a7ca48a05cc0dae66f2523c2a1bad3825daa
+ * output: d970858dd2ab61d91355ebc15ca1a6a7ca48a05cc0dae66f2523c2a1bad3825daa
+Frame No. 051:
+ * input:  d8f0844a23ad20da50d6de025e81c37392b9039cc0c764c1bd1e94c1b699736a98
+ * output: d8f0844a23ad20da50d6de025e81c37392b9039cc0c764c1bd1e94c1b699736a98
+Frame No. 052:
+ * input:  d9708ce6a39ce049646646a2c1a0272496b29a66c037db562863ace0795b55b2e3
+ * output: d9708ce6a39ce049646646a2c1a0272496b29a66c037db562863ace0795b55b2e3
+Frame No. 053:
+ * input:  d9299421d2944036ed69b8e572a048e36d551cd480571d4ec95be680356c69c763
+ * output: d9299421d2944036ed69b8e572a048e36d551cd480571d4ec95be680356c69c763
+Frame No. 054:
+ * input:  d9299421d2944036ed69b8e572a048e36d551cd480571d4ec95be680356c69c763
+ * output: d9299421d2944036ed69b8e572a048e36d551cd480571d4ec95be680356c69c763
+Frame No. 055:
+ * input:  d9299421d2944036ed69b8e572a048e36d551cd480571d4ec95be680356c69c763
+ * output: d9299421d2944036ed69b8e572a048e36d551cd480571d4ec95be680356c69c763
+Frame No. 056:
+ * input:  d2577a1cda50004924924924500049249249245000492492492450004923924924
+ * output: d2577a1cda50004924924924500049249249245000492492492450004923924924
+Success
+Talloc total blocks: 2
diff --git a/tests/testsuite.at b/tests/testsuite.at
index 2d1cefd..c6196a5 100644
--- a/tests/testsuite.at
+++ b/tests/testsuite.at
@@ -49,3 +49,9 @@
 cat $abs_srcdir/meas/meas_test.ok > expout
 AT_CHECK([$abs_top_builddir/tests/meas/meas_test], [], [expout], [ignore])
 AT_CLEANUP
+
+AT_SETUP([ecu_fr])
+AT_KEYWORDS([ecu_fr])
+cat $abs_srcdir/ecu_fr/ecu_fr_test.ok > expout
+AT_CHECK([$abs_top_builddir/tests/ecu_fr/ecu_fr_test], [], [expout], [ignore])
+AT_CLEANUP

-- 
To view, visit https://gerrit.osmocom.org/5214
To unsubscribe, visit https://gerrit.osmocom.org/settings

Gerrit-MessageType: newpatchset
Gerrit-Change-Id: Iae9e69a9578ae305bca42f834694af96a29084e6
Gerrit-PatchSet: 2
Gerrit-Project: osmo-bts
Gerrit-Branch: master
Gerrit-Owner: dexter <pmaier at sysmocom.de>
Gerrit-Reviewer: Jenkins Builder
Gerrit-Reviewer: Neels Hofmeyr <nhofmeyr at sysmocom.de>



More information about the gerrit-log mailing list