codec: replace GSM-FR ECU with new implementation

The original GSM-FR ECU implementation from 2017 exhibits a lot of
defects, as detailed in OS#6027.  Replace it with a new implementation
based on Themyscira libgsmfrp (a complete Rx DTX handler for GSM-FR),
but reduced to just the ECU function, without the comfort noise
generator function.  (These two functions are coupled together in the
classic GSM architecture, but not in libosmocodec ECU model.)

Related: OS#6027
Change-Id: I0200e423ca6165c1313ec9a4effc3f3047f5f032
This commit is contained in:
Mychaela N. Falconia 2023-05-12 17:21:25 +00:00 committed by falconia
parent 8fa13c66ac
commit 5eb356be99
4 changed files with 309 additions and 53 deletions

View File

@ -6,7 +6,7 @@
#include <osmocom/core/defs.h>
#include <osmocom/codec/codec.h>
/* ECU state for GSM-FR */
/* ECU state for GSM-FR - deprecated version only! */
struct osmo_ecu_fr_state {
bool subsequent_lost_frame;
uint8_t frame_backup[GSM_FR_BYTES];

View File

@ -1,9 +1,15 @@
/*
* (C) 2017 by sysmocom - s.f.m.c. GmbH
* (C) 2017 by Philipp Maier <pmaier@sysmocom.de>
*
* All Rights Reserved
*
* Significantly reworked in 2023 by Mother
* Mychaela N. Falconia <falcon@freecalypso.org> - however,
* Mother Mychaela's contributions are NOT subject to copyright.
* No rights reserved, all rights relinquished.
* Portions of this code are based on Themyscira libgsmfrp,
* a public domain library by the same author.
*
* 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
@ -14,6 +20,46 @@
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
*
* The present ECU implementation for GSM-FR is closely based on the
* TS 46.011 spec from 3GPP; more specifically, it is based on the
* Example solution presented in Chapter 6 of that spec, adapted for
* libosmocodec ECU architecture, and comes as close to fulfilling
* the spec's officially stated requirements (Chapter 5) as is
* possible within this Osmocom-imposed architecture. Please note
* the following areas where the present implementation fails to
* fulfill the original intent of GSM spec authors:
*
* - The "lost SID" criterion, defined in GSM 06.31, is based on the
* TAF bit from the Radio Subsystem. However, libosmocodec ECU API
* does not include this flag, thus spec requirements related to
* lost SID conditions cannot be implemented in a strictly compliant
* manner. The present implementation improvises its own "lost SID"
* detector (not strictly spec-compliant) by counting frame_out()
* calls in between good traffic frame inputs via frame_in().
*
* - In the architecture envisioned and assumed in the GSM specs,
* the ECU function of GSM 06.11 was never intended to be a fully
* modular component with its own bona fide I/O interfaces - this
* approach appears to be an Osmocom invention - instead this ECU
* function was intended to be subsumed in the Rx DTX handler
* component of GSM 06.31, also incorporating the comfort noise
* generator of GSM 06.12 - and unlike the narrower-scope ECU,
* this slightly-larger-scope Rx DTX handler is a modular component
* with well-defined I/O interfaces. In the case of BFI conditions
* following a SID, GSM 06.11 spec was written with the assumption
* that the ECU controls the comfort noise generator via internal
* signals, as opposed to emitting "corrected" SID frames on a
* modular interface going to a CN generator located somewhere else.
* Thus the "correct" behavior for a fully modularized ECU is unclear,
* and an argument can be made that the very existence of such a
* fully modularized ECU is incorrect in itself. The present
* implementation re-emits a "rejuvenated" form of the last saved
* SID frame during BFI conditions following a SID within the
* permitted window of 48 frames, then starts emitting muted SIDs
* with Xmaxc decreasing by 4 on each frame, and finally switches
* to emitting non-SID silence frames (Table 1 of TS 46.011)
* once Xmaxc reaches 0.
*/
#include <stdbool.h>
@ -21,9 +67,212 @@
#include <stdint.h>
#include <errno.h>
#include <osmocom/core/prbs.h>
#include <osmocom/codec/codec.h>
#include <osmocom/codec/ecu.h>
/* See TS 46.011, Chapter 6 Example solution */
#define GSM611_XMAXC_REDUCE 4
/* The first 5 bytes of RTP encoding neatly contain the magic nibble
* and LARc parameters, which also happens to be the part of SID frames
* that needs to be passed through as-is. */
#define SID_PREFIX_LEN 5
enum ecu_principal_state {
STATE_NO_DATA,
STATE_SPEECH,
STATE_SP_MUTING,
STATE_SID,
STATE_SID_MUTING,
};
struct fr_ecu_state {
enum ecu_principal_state pr_state;
uint8_t speech_frame[GSM_FR_BYTES];
uint8_t sid_prefix[SID_PREFIX_LEN];
uint8_t sid_xmaxc;
uint8_t sid_reemit_count;
struct osmo_prbs_state prng;
};
/* This function is the frame input to the ECU - all inputs to this
* function have been received by the Radio Subsystem as good traffic
* frames in the GSM 06.31 definition.
*/
static void fr_ecu_input(struct fr_ecu_state *fr, const uint8_t *frame)
{
enum osmo_gsm631_sid_class sidc;
sidc = osmo_fr_sid_classify(frame);
switch (sidc) {
case OSMO_GSM631_SID_CLASS_SPEECH:
memcpy(fr->speech_frame, frame, GSM_FR_BYTES);
fr->pr_state = STATE_SPEECH;
return;
case OSMO_GSM631_SID_CLASS_INVALID:
/* GSM 06.31 section 6.1.2 says: "an invalid SID frame
* shall be substituted by the last valid SID frame
* and the procedure for valid SID frames be applied."
* However, libosmocodec ECU architecture prevents us
* from doing what the spec says: the frame_in() method
* gets a const frame that can't be modified, and
* frame_out() will never get called when BFI=0, even
* when the "good traffic frame" (in the BFI=0 sense)
* is an invalid SID by the bit-counting rule.
* Thus there is no place where we can re-emit a cached
* copy of the last valid SID upon receiving an invalid SID.
*
* In the standard GSM architecture this problem never
* arises because the ECU is not a separate component
* but is coupled with the CN generator, thus the output
* from the Rx DTX handler block will be a CN frame,
* for both valid-SID and invalid-SID inputs to the block.
* But what can we do within the constraints of libosmocodec
* ECU framework? We treat the invalid SID almost like a
* BFI, doing almost nothing in the frame_in() method,
* but we reset sid_reemit_count because by the rules of
* GSM 06.31 an invalid SID is still an accepted SID frame
* for the purpose of "lost SID" logic. */
fr->sid_reemit_count = 0;
return;
case OSMO_GSM631_SID_CLASS_VALID:
/* save LARc part */
memcpy(fr->sid_prefix, frame, SID_PREFIX_LEN);
/* save Xmaxc from the last subframe */
fr->sid_xmaxc = ((frame[27] & 0x1F) << 1) | (frame[28] >> 7);
fr->pr_state = STATE_SID;
fr->sid_reemit_count = 0;
return;
default:
/* There are only 3 possible SID classifications per GSM 06.31
* section 6.1.1, thus any other return value is a grave error
* in the code. */
OSMO_ASSERT(0);
}
}
/* Reduce all 4 Xmaxc fields in the frame. When all 4 Xmaxc fields
* reach 0, the function will return true for "mute".
*/
static bool reduce_xmaxc(uint8_t *frame)
{
bool mute_flag = true;
uint8_t sub, xmaxc;
for (sub = 0; sub < 4; sub++) {
xmaxc = ((frame[sub*7+6] & 0x1F) << 1) | (frame[sub*7+7] >> 7);
if (xmaxc > GSM611_XMAXC_REDUCE) {
xmaxc -= GSM611_XMAXC_REDUCE;
mute_flag = false;
} else
xmaxc = 0;
frame[sub*7+6] &= 0xE0;
frame[sub*7+6] |= xmaxc >> 1;
frame[sub*7+7] &= 0x7F;
frame[sub*7+7] |= (xmaxc & 1) << 7;
}
return mute_flag;
}
/* TS 46.011 chapter 6, paragraph 4, last sentence: "The grid position
* parameters are chosen randomly between 0 and 3 during this time."
* (The "during this time" qualifier refers to the speech muting state.)
* This sentence in the spec must have been overlooked by previous ECU
* implementors, as this aspect of the muting logic was missing.
*/
static void random_grid_pos(struct fr_ecu_state *fr, uint8_t *frame)
{
uint8_t sub;
for (sub = 0; sub < 4; sub++) {
frame[sub*7+6] &= 0x9F;
frame[sub*7+6] |= osmo_prbs_get_ubit(&fr->prng) << 6;
frame[sub*7+6] |= osmo_prbs_get_ubit(&fr->prng) << 5;
}
}
/* Like reduce_xmaxc() above, but for comfort noise rather than speech. */
static bool reduce_xmaxc_sid(struct fr_ecu_state *fr)
{
if (fr->sid_xmaxc > GSM611_XMAXC_REDUCE) {
fr->sid_xmaxc -= GSM611_XMAXC_REDUCE;
return false;
}
fr->sid_xmaxc = 0;
return true;
}
/* This function implements the part which is peculiar to the present
* "standalone" packaging of GSM-FR ECU, without a directly coupled
* comfort noise generator - it re-emits synthetic SID frames during
* DTX pauses, initially unchanged from the saved SID and later muted.
*/
static void reemit_sid(struct fr_ecu_state *fr, uint8_t *frame)
{
uint8_t *p, sub;
memcpy(frame, fr->sid_prefix, SID_PREFIX_LEN);
p = frame + SID_PREFIX_LEN;
for (sub = 0; sub < 4; sub++) {
*p++ = 0;
*p++ = fr->sid_xmaxc >> 1;
*p++ = (fr->sid_xmaxc & 1) << 7;
*p++ = 0;
*p++ = 0;
*p++ = 0;
*p++ = 0;
}
}
/* This function is responsible for generating the ECU's output
* in the event that the Radio Subsystem does not have a good
* traffic frame - conditions corresponding to BFI=1 in the specs.
*/
static void fr_ecu_output(struct fr_ecu_state *fr, uint8_t *frame)
{
bool mute;
switch (fr->pr_state) {
case STATE_NO_DATA:
memcpy(frame, osmo_gsm611_silence_frame, GSM_FR_BYTES);
return;
case STATE_SPEECH:
/* TS 46.011 chapter 6: "The first lost speech frame is
* replaced at the speech decoder input by the previous
* good speech frame." */
memcpy(frame, fr->speech_frame, GSM_FR_BYTES);
fr->pr_state = STATE_SP_MUTING;
return;
case STATE_SP_MUTING:
mute = reduce_xmaxc(fr->speech_frame);
memcpy(frame, fr->speech_frame, GSM_FR_BYTES);
random_grid_pos(fr, frame);
if (mute)
fr->pr_state = STATE_NO_DATA;
return;
case STATE_SID:
fr->sid_reemit_count++;
if (fr->sid_reemit_count >= 48) {
fr->pr_state = STATE_SID_MUTING;
reduce_xmaxc_sid(fr);
}
reemit_sid(fr, frame);
return;
case STATE_SID_MUTING:
if (reduce_xmaxc_sid(fr)) {
fr->pr_state = STATE_NO_DATA;
memcpy(frame, osmo_gsm611_silence_frame, GSM_FR_BYTES);
} else
reemit_sid(fr, frame);
return;
default:
/* a severe bug in the state machine! */
OSMO_ASSERT(0);
}
}
/***********************************************************************
* Integration with ECU core
***********************************************************************/
@ -31,7 +280,8 @@
static struct osmo_ecu_state *ecu_fr_init(void *ctx, enum osmo_ecu_codec codec)
{
struct osmo_ecu_state *st;
size_t size = sizeof(*st) + sizeof(struct osmo_ecu_fr_state);
struct fr_ecu_state *fr;
size_t size = sizeof(*st) + sizeof(*fr);
st = talloc_named_const(ctx, size, "ecu_state_FR");
if (!st)
@ -39,6 +289,9 @@ static struct osmo_ecu_state *ecu_fr_init(void *ctx, enum osmo_ecu_codec codec)
memset(st, 0, size);
st->codec = codec;
fr = (struct fr_ecu_state *) &st->data;
fr->pr_state = STATE_NO_DATA;
osmo_prbs_state_init(&fr->prng, &osmo_prbs15);
return st;
}
@ -46,22 +299,25 @@ static struct osmo_ecu_state *ecu_fr_init(void *ctx, enum osmo_ecu_codec codec)
static int ecu_fr_frame_in(struct osmo_ecu_state *st, bool bfi, const uint8_t *frame,
unsigned int frame_bytes)
{
struct osmo_ecu_fr_state *fr = (struct osmo_ecu_fr_state *) &st->data;
struct fr_ecu_state *fr = (struct fr_ecu_state *) &st->data;
if (bfi)
return 0;
if (frame_bytes != GSM_FR_BYTES)
return 0;
if ((frame[0] & 0xF0) != 0xD0)
return 0;
osmo_ecu_fr_reset(fr, frame);
fr_ecu_input(fr, frame);
return 0;
}
static int ecu_fr_frame_out(struct osmo_ecu_state *st, uint8_t *frame_out)
{
struct osmo_ecu_fr_state *fr = (struct osmo_ecu_fr_state *) &st->data;
struct fr_ecu_state *fr = (struct fr_ecu_state *) &st->data;
if (osmo_ecu_fr_conceal(fr, frame_out) == 0)
return GSM_FR_BYTES;
else
return -1;
fr_ecu_output(fr, frame_out);
return GSM_FR_BYTES;
}
static const struct osmo_ecu_ops osmo_ecu_ops_fr = {

View File

@ -151,7 +151,7 @@ void test_fr_concealment_core(void)
int i, rc;
int j = 0;
printf("=> Testing FR concealment (simple, consecutive bad frames)\n");
printf("=> Testing FR concealment (simple, using ECU abstraction)\n");
while (sample_frame_hex[j] != NULL) {
/* Parse frame from string to hex */

View File

@ -41,49 +41,49 @@ conceal: 16, result: d0000000000000000000000000000000000000000000000000000000000
conceal: 17, result: d00000000000000000000000000000000000000000000000000000000000000000 XMAXC: [0, 0, 0, 0]
conceal: 18, result: d00000000000000000000000000000000000000000000000000000000000000000 XMAXC: [0, 0, 0, 0]
conceal: 19, result: d00000000000000000000000000000000000000000000000000000000000000000 XMAXC: [0, 0, 0, 0]
=> Testing FR concealment (simple, consecutive bad frames)
=> Testing FR concealment (simple, using ECU abstraction)
Start with: d9ec9be212901f802335598c501f805bad3d4ba01f809b69df5a501f809cd1b4da, XMAXC: [3f, 3f, 3f, 3f]
conceal: 00, result: d9ec9be212901f802335598c501f805bad3d4ba01f809b69df5a501f809cd1b4da XMAXC: [3f, 3f, 3f, 3f]
conceal: 01, result: d9ec9be212901d802335598c501d805bad3d4ba01d809b69df5a501d809cd1b4da XMAXC: [3b, 3b, 3b, 3b]
conceal: 02, result: d9ec9be212901b802335598c501b805bad3d4ba01b809b69df5a501b809cd1b4da XMAXC: [37, 37, 37, 37]
conceal: 01, result: d9ec9be212905d802335598c501d805bad3d4ba01d809b69df5a501d809cd1b4da XMAXC: [3b, 3b, 3b, 3b]
conceal: 02, result: d9ec9be212901b802335598c501b805bad3d4ba01b809b69df5a507b809cd1b4da XMAXC: [37, 37, 37, 37]
conceal: 03, result: d9ec9be2129019802335598c5019805bad3d4ba019809b69df5a5019809cd1b4da XMAXC: [33, 33, 33, 33]
conceal: 04, result: d9ec9be2129017802335598c5017805bad3d4ba017809b69df5a5017809cd1b4da XMAXC: [2f, 2f, 2f, 2f]
conceal: 04, result: d9ec9be2129017802335598c5017805bad3d4ba057809b69df5a5057809cd1b4da XMAXC: [2f, 2f, 2f, 2f]
conceal: 05, result: d9ec9be2129015802335598c5015805bad3d4ba015809b69df5a5015809cd1b4da XMAXC: [2b, 2b, 2b, 2b]
conceal: 06, result: d9ec9be2129013802335598c5013805bad3d4ba013809b69df5a5013809cd1b4da XMAXC: [27, 27, 27, 27]
conceal: 06, result: d9ec9be2129013802335598c5073805bad3d4ba073809b69df5a5013809cd1b4da XMAXC: [27, 27, 27, 27]
conceal: 07, result: d9ec9be2129011802335598c5011805bad3d4ba011809b69df5a5011809cd1b4da XMAXC: [23, 23, 23, 23]
conceal: 08, result: d9ec9be212900f802335598c500f805bad3d4ba00f809b69df5a500f809cd1b4da XMAXC: [1f, 1f, 1f, 1f]
conceal: 09, result: d9ec9be212900d802335598c500d805bad3d4ba00d809b69df5a500d809cd1b4da XMAXC: [1b, 1b, 1b, 1b]
conceal: 10, result: d9ec9be212900b802335598c500b805bad3d4ba00b809b69df5a500b809cd1b4da XMAXC: [17, 17, 17, 17]
conceal: 11, result: d9ec9be2129009802335598c5009805bad3d4ba009809b69df5a5009809cd1b4da XMAXC: [13, 13, 13, 13]
conceal: 12, result: d9ec9be2129007802335598c5007805bad3d4ba007809b69df5a5007809cd1b4da XMAXC: [f, f, f, f]
conceal: 13, result: d9ec9be2129005802335598c5005805bad3d4ba005809b69df5a5005809cd1b4da XMAXC: [b, b, b, b]
conceal: 14, result: d9ec9be2129003802335598c5003805bad3d4ba003809b69df5a5003809cd1b4da XMAXC: [7, 7, 7, 7]
conceal: 15, result: d9ec9be2129001802335598c5001805bad3d4ba001809b69df5a5001809cd1b4da XMAXC: [3, 3, 3, 3]
conceal: 16, result: d00000000000000000000000000000000000000000000000000000000000000000 XMAXC: [0, 0, 0, 0]
conceal: 17, result: d00000000000000000000000000000000000000000000000000000000000000000 XMAXC: [0, 0, 0, 0]
conceal: 18, result: d00000000000000000000000000000000000000000000000000000000000000000 XMAXC: [0, 0, 0, 0]
conceal: 19, result: d00000000000000000000000000000000000000000000000000000000000000000 XMAXC: [0, 0, 0, 0]
conceal: 08, result: d9ec9be212904f802335598c500f805bad3d4ba04f809b69df5a500f809cd1b4da XMAXC: [1f, 1f, 1f, 1f]
conceal: 09, result: d9ec9be212900d802335598c500d805bad3d4ba00d809b69df5a506d809cd1b4da XMAXC: [1b, 1b, 1b, 1b]
conceal: 10, result: d9ec9be212900b802335598c506b805bad3d4ba00b809b69df5a500b809cd1b4da XMAXC: [17, 17, 17, 17]
conceal: 11, result: d9ec9be2129009802335598c5009805bad3d4ba049809b69df5a5049809cd1b4da XMAXC: [13, 13, 13, 13]
conceal: 12, result: d9ec9be2129047802335598c5047805bad3d4ba007809b69df5a5007809cd1b4da XMAXC: [f, f, f, f]
conceal: 13, result: d9ec9be2129005802335598c5065805bad3d4ba065809b69df5a5065809cd1b4da XMAXC: [b, b, b, b]
conceal: 14, result: d9ec9be2129063802335598c5003805bad3d4ba003809b69df5a5003809cd1b4da XMAXC: [7, 7, 7, 7]
conceal: 15, result: d9ec9be2129041802335598c5001805bad3d4ba001809b69df5a5001809cd1b4da XMAXC: [3, 3, 3, 3]
conceal: 16, result: d9ec9be2129040002335598c5000005bad3d4ba000009b69df5a5060009cd1b4da XMAXC: [0, 0, 0, 0]
conceal: 17, result: daa7aaa51a502038e46db91b502038e46db91b502038e46db91b502038e46db91b XMAXC: [0, 0, 0, 0]
conceal: 18, result: daa7aaa51a502038e46db91b502038e46db91b502038e46db91b502038e46db91b XMAXC: [0, 0, 0, 0]
conceal: 19, result: daa7aaa51a502038e46db91b502038e46db91b502038e46db91b502038e46db91b XMAXC: [0, 0, 0, 0]
Start with: d9ec9be212901d802335598c5013805bad3d4ba01f809b69df5a5019809cd1b4da, XMAXC: [3b, 27, 3f, 33]
conceal: 00, result: d9ec9be212901d802335598c5013805bad3d4ba01f809b69df5a5019809cd1b4da XMAXC: [3b, 27, 3f, 33]
conceal: 01, result: d9ec9be212901b802335598c5011805bad3d4ba01d809b69df5a5017809cd1b4da XMAXC: [37, 23, 3b, 2f]
conceal: 02, result: d9ec9be2129019802335598c500f805bad3d4ba01b809b69df5a5015809cd1b4da XMAXC: [33, 1f, 37, 2b]
conceal: 03, result: d9ec9be2129017802335598c500d805bad3d4ba019809b69df5a5013809cd1b4da XMAXC: [2f, 1b, 33, 27]
conceal: 04, result: d9ec9be2129015802335598c500b805bad3d4ba017809b69df5a5011809cd1b4da XMAXC: [2b, 17, 2f, 23]
conceal: 05, result: d9ec9be2129013802335598c5009805bad3d4ba015809b69df5a500f809cd1b4da XMAXC: [27, 13, 2b, 1f]
conceal: 06, result: d9ec9be2129011802335598c5007805bad3d4ba013809b69df5a500d809cd1b4da XMAXC: [23, f, 27, 1b]
conceal: 07, result: d9ec9be212900f802335598c5005805bad3d4ba011809b69df5a500b809cd1b4da XMAXC: [1f, b, 23, 17]
conceal: 08, result: d9ec9be212900d802335598c5003805bad3d4ba00f809b69df5a5009809cd1b4da XMAXC: [1b, 7, 1f, 13]
conceal: 09, result: d9ec9be212900b802335598c5001805bad3d4ba00d809b69df5a5007809cd1b4da XMAXC: [17, 3, 1b, f]
conceal: 10, result: d9ec9be2129009802335598c5000005bad3d4ba00b809b69df5a5005809cd1b4da XMAXC: [13, 0, 17, b]
conceal: 11, result: d9ec9be2129007802335598c5000005bad3d4ba009809b69df5a5003809cd1b4da XMAXC: [f, 0, 13, 7]
conceal: 12, result: d9ec9be2129005802335598c5000005bad3d4ba007809b69df5a5001809cd1b4da XMAXC: [b, 0, f, 3]
conceal: 13, result: d9ec9be2129003802335598c5000005bad3d4ba005809b69df5a5000009cd1b4da XMAXC: [7, 0, b, 0]
conceal: 14, result: d9ec9be2129001802335598c5000005bad3d4ba003809b69df5a5000009cd1b4da XMAXC: [3, 0, 7, 0]
conceal: 15, result: d9ec9be2129000002335598c5000005bad3d4ba001809b69df5a5000009cd1b4da XMAXC: [0, 0, 3, 0]
conceal: 16, result: d00000000000000000000000000000000000000000000000000000000000000000 XMAXC: [0, 0, 0, 0]
conceal: 17, result: d00000000000000000000000000000000000000000000000000000000000000000 XMAXC: [0, 0, 0, 0]
conceal: 18, result: d00000000000000000000000000000000000000000000000000000000000000000 XMAXC: [0, 0, 0, 0]
conceal: 19, result: d00000000000000000000000000000000000000000000000000000000000000000 XMAXC: [0, 0, 0, 0]
conceal: 01, result: d9ec9be212901b802335598c5011805bad3d4ba01d809b69df5a5077809cd1b4da XMAXC: [37, 23, 3b, 2f]
conceal: 02, result: d9ec9be2129019802335598c500f805bad3d4ba05b809b69df5a5055809cd1b4da XMAXC: [33, 1f, 37, 2b]
conceal: 03, result: d9ec9be2129017802335598c500d805bad3d4ba059809b69df5a5053809cd1b4da XMAXC: [2f, 1b, 33, 27]
conceal: 04, result: d9ec9be2129015802335598c506b805bad3d4ba077809b69df5a5011809cd1b4da XMAXC: [2b, 17, 2f, 23]
conceal: 05, result: d9ec9be2129013802335598c5069805bad3d4ba075809b69df5a500f809cd1b4da XMAXC: [27, 13, 2b, 1f]
conceal: 06, result: d9ec9be2129051802335598c5007805bad3d4ba053809b69df5a500d809cd1b4da XMAXC: [23, f, 27, 1b]
conceal: 07, result: d9ec9be212904f802335598c5005805bad3d4ba051809b69df5a506b809cd1b4da XMAXC: [1f, b, 23, 17]
conceal: 08, result: d9ec9be212900d802335598c5063805bad3d4ba00f809b69df5a5069809cd1b4da XMAXC: [1b, 7, 1f, 13]
conceal: 09, result: d9ec9be212900b802335598c5061805bad3d4ba04d809b69df5a5047809cd1b4da XMAXC: [17, 3, 1b, f]
conceal: 10, result: d9ec9be2129049802335598c5040005bad3d4ba04b809b69df5a5045809cd1b4da XMAXC: [13, 0, 17, b]
conceal: 11, result: d9ec9be2129047802335598c5020005bad3d4ba069809b69df5a5063809cd1b4da XMAXC: [f, 0, 13, 7]
conceal: 12, result: d9ec9be2129065802335598c5060005bad3d4ba067809b69df5a5061809cd1b4da XMAXC: [b, 0, f, 3]
conceal: 13, result: d9ec9be2129023802335598c5000005bad3d4ba005809b69df5a5000009cd1b4da XMAXC: [7, 0, b, 0]
conceal: 14, result: d9ec9be2129001802335598c5000005bad3d4ba003809b69df5a5060009cd1b4da XMAXC: [3, 0, 7, 0]
conceal: 15, result: d9ec9be2129040002335598c5000005bad3d4ba001809b69df5a5000009cd1b4da XMAXC: [0, 0, 3, 0]
conceal: 16, result: d9ec9be2129000002335598c5000005bad3d4ba040009b69df5a5020009cd1b4da XMAXC: [0, 0, 0, 0]
conceal: 17, result: daa7aaa51a502038e46db91b502038e46db91b502038e46db91b502038e46db91b XMAXC: [0, 0, 0, 0]
conceal: 18, result: daa7aaa51a502038e46db91b502038e46db91b502038e46db91b502038e46db91b XMAXC: [0, 0, 0, 0]
conceal: 19, result: daa7aaa51a502038e46db91b502038e46db91b502038e46db91b502038e46db91b XMAXC: [0, 0, 0, 0]
=> Testing FR concealment (realistic, various bad frames)
Frame No. 000:
@ -318,7 +318,7 @@ Frame No. 018:
* output: d9689ba5e3d260491b516adb5e4027256e27227ee0351c8e549a5c60492471971b
Frame No. 019:
* input: (bad)
* output: d00000000000000000000000000000000000000000000000000000000000000000
* output: d9689ba5e3d240491b516adb5e0027256e27227e80351c8e549a5c00492471971b
Frame No. 020:
* input: d8e6a2e1d3d2605b1376c8d35280392451391cbc80392a71b6db8aa049238dc8ab
* output: d8e6a2e1d3d2605b1376c8d35280392451391cbc80392a71b6db8aa049238dc8ab
@ -357,19 +357,19 @@ Frame No. 031:
* output: d9a99361a276403b1a6ad6dcd40026e489c8e3bc40371c4dc564e2c036e28eb963
Frame No. 032:
* input: (bad)
* output: d00000000000000000000000000000000000000000000000000000000000000000
* output: d9a99361a276003b1a6ad6dcd40026e489c8e3bc00371c4dc564e2e036e28eb963
Frame No. 033:
* input: (bad)
* output: d00000000000000000000000000000000000000000000000000000000000000000
* output: daa7aaa51a502038e46db91b502038e46db91b502038e46db91b502038e46db91b
Frame No. 034:
* input: (bad)
* output: d00000000000000000000000000000000000000000000000000000000000000000
* output: daa7aaa51a502038e46db91b502038e46db91b502038e46db91b502038e46db91b
Frame No. 035:
* input: (bad)
* output: d00000000000000000000000000000000000000000000000000000000000000000
* output: daa7aaa51a502038e46db91b502038e46db91b502038e46db91b502038e46db91b
Frame No. 036:
* input: (bad)
* output: d00000000000000000000000000000000000000000000000000000000000000000
* output: daa7aaa51a502038e46db91b502038e46db91b502038e46db91b502038e46db91b
Frame No. 037:
* input: d92c8b6d5aee4034ebb22724862047145634a5c0a038e371b8e4a880485c89dd25
* output: d92c8b6d5aee4034ebb22724862047145634a5c0a038e371b8e4a880485c89dd25