mirror of
https://github.com/DSheirer/sdrtrunk.git
synced 2026-11-15 10:07:26 -05:00
Co-authored-by: denny <dennis.sheirer@gmail.com>
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* *****************************************************************************
|
||||
* Copyright (C) 2014-2025 Dennis Sheirer
|
||||
* Copyright (C) 2014-2026 Dennis Sheirer
|
||||
*
|
||||
* 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
|
||||
@@ -181,7 +181,10 @@ public class DMRMessageProcessor implements Listener<IMessage>
|
||||
//Now that the message has been (potentially) enriched, dispatch it to the modules
|
||||
dispatch(message);
|
||||
|
||||
//Extract the Full Link Control message fragment from the Voice with embedded signalling message
|
||||
//Extract the Full Link Control message fragment from the Voice with embedded signalling message. The LCSS is
|
||||
//used even when the EMB is flagged invalid: the soft decision best estimate is usually correct and the
|
||||
//assembled message is validated by its own BPTC and checksum, while discarding these fragments loses valid
|
||||
//FLC messages on weak signals.
|
||||
if(message instanceof VoiceEMBMessage voice)
|
||||
{
|
||||
if(message.getTimeslot() == 1)
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* *****************************************************************************
|
||||
* Copyright (C) 2014-2023 Dennis Sheirer
|
||||
* Copyright (C) 2014-2026 Dennis Sheirer
|
||||
*
|
||||
* 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
|
||||
@@ -22,33 +22,25 @@ package io.github.dsheirer.module.decode.dmr.message.voice;
|
||||
import io.github.dsheirer.bits.CorrectedBinaryMessage;
|
||||
import io.github.dsheirer.module.decode.dmr.message.type.LCSS;
|
||||
import java.util.HashSet;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
* DMR Voice Frame B-F Embedded Signalling Chunk
|
||||
*/
|
||||
public class EMB
|
||||
{
|
||||
private final static Logger mLog = LoggerFactory.getLogger(EMB.class);
|
||||
private static final int[] VALID_WORDS = new int[]{
|
||||
0x0000, 0x0273, 0x04E5, 0x0696, 0x09C9, 0x0BBA, 0x0D2C, 0x0F5F, 0x11E2, 0x1391, 0x1507, 0x1774, 0x182B, 0x1A58,
|
||||
0x1CCE, 0x1EBD, 0x21B7, 0x23C4, 0x2552, 0x2721, 0x287E, 0x2A0D, 0x2C9B, 0x2EE8, 0x3055, 0x3226, 0x34B0, 0x36C3,
|
||||
0x399C, 0x3BEF, 0x3D79, 0x3F0A, 0x411E, 0x436D, 0x45FB, 0x4788, 0x48D7, 0x4AA4, 0x4C32, 0x4E41, 0x50FC, 0x528F,
|
||||
0x5419, 0x566A, 0x5935, 0x5B46, 0x5DD0, 0x5FA3, 0x60A9, 0x62DA, 0x644C, 0x663F, 0x6960, 0x6B13, 0x6D85, 0x6FF6,
|
||||
0x714B, 0x7338, 0x75AE, 0x77DD, 0x7882, 0x7AF1, 0x7C67, 0x7E14, 0x804F, 0x823C, 0x84AA, 0x86D9, 0x8986, 0x8BF5,
|
||||
0x8D63, 0x8F10, 0x91AD, 0x93DE, 0x9548, 0x973B, 0x9864, 0x9A17, 0x9C81, 0x9EF2, 0xA1F8, 0xA38B, 0xA51D, 0xA76E,
|
||||
0xA831, 0xAA42, 0xACD4, 0xAEA7, 0xB01A, 0xB269, 0xB4FF, 0xB68C, 0xB9D3, 0xBBA0, 0xBD36, 0xBF45, 0xC151, 0xC322,
|
||||
0xC5B4, 0xC7C7, 0xC898, 0xCAEB, 0xCC7D, 0xCE0E, 0xD0B3, 0xD2C0, 0xD456, 0xD625, 0xD97A, 0xDB09, 0xDD9F, 0xDFEC,
|
||||
0xE0E6, 0xE295, 0xE403, 0xE670, 0xE92F, 0xEB5C, 0xEDCA, 0xEFB9, 0xF104, 0xF377, 0xF5E1, 0xF792, 0xF8CD, 0xFABE,
|
||||
0xFC28, 0xFE5B};
|
||||
//Quadratic Residue (17,9) generator polynomial g(x) = x^8 + x^5 + x^4 + x^3 + 1, shortened to (16,7) after
|
||||
//appending an even parity bit.
|
||||
private static final int GENERATOR = 0x139;
|
||||
private static final int INFORMATION_WORD_COUNT = 128;
|
||||
private static final int[] VALID_WORDS = new int[INFORMATION_WORD_COUNT];
|
||||
private static final HashSet<Integer> WORD_SET = new HashSet<>();
|
||||
|
||||
static
|
||||
{
|
||||
for(int word: VALID_WORDS)
|
||||
for(int information = 0; information < INFORMATION_WORD_COUNT; information++)
|
||||
{
|
||||
WORD_SET.add(word);
|
||||
VALID_WORDS[information] = encode(information);
|
||||
WORD_SET.add(VALID_WORDS[information]);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -56,8 +48,9 @@ public class EMB
|
||||
private static final int[] COLOR_CODE = new int[]{0, 1, 2, 3};
|
||||
private static final int ENCRYPTION_PI = 4;
|
||||
private static final int[] LINK_CONTROL_START_STOP = new int[]{5, 6};
|
||||
//Maximum bit errors between the hard decision and the soft decision (maximum likelihood) word for validity.
|
||||
private static final int SOFT_DECISION_MAX_BIT_ERRORS = 2;
|
||||
//Maximum correctable bit errors for a minimum Hamming distance 6 code: (6 - 1) / 2 = 2. Any decode that differs
|
||||
//from the received word by more than this is ambiguous and is flagged invalid by both the hard and soft decoders.
|
||||
private static final int MAX_CORRECTABLE_BIT_ERRORS = 2;
|
||||
|
||||
private CorrectedBinaryMessage mMessage;
|
||||
private boolean mValid = true;
|
||||
@@ -93,9 +86,43 @@ public class EMB
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Encodes the 7-bit information word (color code, privacy indicator and LCSS) into the 16-bit QR(16,7,6)
|
||||
* codeword: 7 information bits, followed by the 8 bit remainder of the information word times x^8 divided by the
|
||||
* generator polynomial, followed by an even parity bit over the preceding 15 bits.
|
||||
* @param information word in the range 0-127
|
||||
* @return codeword
|
||||
*/
|
||||
static int encode(int information)
|
||||
{
|
||||
int remainder = information << 8;
|
||||
|
||||
for(int bit = 14; bit >= 8; bit--)
|
||||
{
|
||||
if((remainder & (1 << bit)) != 0)
|
||||
{
|
||||
remainder ^= GENERATOR << (bit - 8);
|
||||
}
|
||||
}
|
||||
|
||||
int word = (information << 8) | remainder;
|
||||
return (word << 1) | (Integer.bitCount(word) & 1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Copy of the 128 valid codewords, indexed by information word.
|
||||
*/
|
||||
static int[] getValidWords()
|
||||
{
|
||||
return VALID_WORDS.clone();
|
||||
}
|
||||
|
||||
/**
|
||||
* Maximum likelihood soft decision decoding against all valid words. Selects the valid word with the lowest sum
|
||||
* of soft bit magnitudes for the bit positions where the valid word differs from the hard decision.
|
||||
* of soft bit magnitudes for the bit positions where the valid word differs from the hard decision. The message
|
||||
* is always set to the selected word, but it is only flagged valid when the selected word is within the
|
||||
* correctable bit error count. Beyond that count, the selected word is still the best estimate available and is
|
||||
* a better source for the LCSS used to assemble the embedded full link control fragments than the received bits.
|
||||
* @param softBits for the 16 transmitted bits.
|
||||
*/
|
||||
private void decodeSoft(float[] softBits)
|
||||
@@ -132,18 +159,20 @@ public class EMB
|
||||
getMessage().setCorrectedBitCount(bitErrors);
|
||||
}
|
||||
|
||||
mValid = bitErrors <= SOFT_DECISION_MAX_BIT_ERRORS;
|
||||
mValid = bitErrors <= MAX_CORRECTABLE_BIT_ERRORS;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks the EMB word using the Quadratic Residue(16,7,6) code as detailed in TS 102 361-1 B.3.2.
|
||||
*
|
||||
* Implementation Notes:
|
||||
* There are 7 information bits protected by 9 parity bits with a Hamming distance of 6 meaning that this code can
|
||||
* correct up to 3 bit errors. The VALID_WORDS constant is a list of all possible (128) valid words precalculated
|
||||
* with their corresponding QR checksum values. When performing a CRC check, if the complete 16-bit value matches
|
||||
* any of these valid words, then the value has no errors. Otherwise, XOR the received value against all valid
|
||||
* words and select any valid word that differs from the received word by no more than 3 bit position differences.
|
||||
* There are 7 information bits protected by 9 parity bits with a minimum Hamming distance of 6 meaning that this
|
||||
* code can correct up to 2 bit errors and detect 3 bit errors. The VALID_WORDS constant is a list of all
|
||||
* possible (128) valid words. If the complete 16-bit value matches any of these valid words, then the value has
|
||||
* no errors. Otherwise, select the valid word that differs from the received word by no more than 2 bit
|
||||
* positions. The distance 6 guarantees that there is at most one such word. When there is no such word, the
|
||||
* message is left unchanged and flagged invalid. Unlike the soft decoder, there is no unique best estimate at 3
|
||||
* or more bit errors, and the received bits are as likely to hold the correct LCSS as the nearest valid word.
|
||||
*/
|
||||
private void checkCRC()
|
||||
{
|
||||
@@ -155,17 +184,15 @@ public class EMB
|
||||
return;
|
||||
}
|
||||
|
||||
int bitErrors = 0;
|
||||
|
||||
for(int validWord: VALID_WORDS)
|
||||
{
|
||||
bitErrors = Integer.bitCount(word ^ validWord);
|
||||
int bitErrors = Integer.bitCount(word ^ validWord);
|
||||
|
||||
if(bitErrors <= 3)
|
||||
if(bitErrors <= MAX_CORRECTABLE_BIT_ERRORS)
|
||||
{
|
||||
getMessage().setInt(validWord, WORD);
|
||||
getMessage().setCorrectedBitCount(bitErrors);
|
||||
mValid = bitErrors <= 1; //Only accept up to 1 bit error for validity
|
||||
mValid = true;
|
||||
return;
|
||||
}
|
||||
}
|
||||
@@ -223,23 +250,4 @@ public class EMB
|
||||
sb.append("CC:").append(getColorCode());
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
public static void main(String[] args)
|
||||
{
|
||||
CorrectedBinaryMessage m = new CorrectedBinaryMessage(16);
|
||||
|
||||
for(int x = 0; x < Math.pow(2, 16); x++)
|
||||
{
|
||||
m.setInt(x, WORD);
|
||||
|
||||
EMB emb = new EMB(m);
|
||||
|
||||
if(!emb.isValid())
|
||||
{
|
||||
mLog.debug("Invalid: " + x);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -23,23 +23,20 @@ import io.github.dsheirer.bits.BinaryMessage;
|
||||
import io.github.dsheirer.bits.CorrectedBinaryMessage;
|
||||
import io.github.dsheirer.edac.Hamming16;
|
||||
import io.github.dsheirer.edac.soft.SoftBlockCode;
|
||||
import io.github.dsheirer.module.decode.dmr.message.voice.EMB;
|
||||
import java.util.HashSet;
|
||||
import java.util.Random;
|
||||
import java.util.Set;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertFalse;
|
||||
import static org.junit.jupiter.api.Assertions.assertNotNull;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
|
||||
/**
|
||||
* JUnit tests for DMR soft decision decoding of the embedded signalling BPTC(128,77) product code and the EMB.
|
||||
* JUnit tests for DMR soft decision decoding of the embedded signalling BPTC(128,77) product code.
|
||||
*/
|
||||
public class BPTC_128_77SoftTest
|
||||
{
|
||||
private static final int[] WORD = new int[]{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15};
|
||||
private static final SoftBlockCode ROW_CODE = SoftBlockCode.fromCodewordTest(16, word -> {
|
||||
BinaryMessage message = new BinaryMessage(16);
|
||||
|
||||
@@ -166,73 +163,4 @@ public class BPTC_128_77SoftTest
|
||||
//Sanity check that this scenario exceeds the hard decision decoder capability
|
||||
assertTrue(hardFailures > 0, "Expected hard decision failures");
|
||||
}
|
||||
|
||||
/**
|
||||
* Test: EMB soft decoding corrects 4 low reliability bit errors (beyond the hard decoder's 3-bit capability) and
|
||||
* flags the result invalid since it exceeds the soft decision validity threshold. Also verifies that a single
|
||||
* low reliability bit error is corrected and valid.
|
||||
*/
|
||||
@Test
|
||||
void testEMBSoftDecode()
|
||||
{
|
||||
Random random = new Random(3);
|
||||
CorrectedBinaryMessage reference = new CorrectedBinaryMessage(16);
|
||||
|
||||
for(int trial = 0; trial < 100; trial++)
|
||||
{
|
||||
//Find a valid word by hard decoding a random word with no more than 1 bit error after correction
|
||||
int word;
|
||||
EMB referenceEMB;
|
||||
|
||||
do
|
||||
{
|
||||
word = random.nextInt(1 << 16);
|
||||
reference.setInt(word, WORD);
|
||||
referenceEMB = new EMB(new CorrectedBinaryMessage(reference));
|
||||
}
|
||||
while(!referenceEMB.isValid());
|
||||
|
||||
//Recover the corrected valid word
|
||||
CorrectedBinaryMessage corrected = new CorrectedBinaryMessage(reference);
|
||||
new EMB(corrected);
|
||||
int validWord = corrected.getInt(0, 15);
|
||||
|
||||
for(int errorCount: new int[]{1, 4})
|
||||
{
|
||||
float[] softBits = new float[16];
|
||||
CorrectedBinaryMessage received = new CorrectedBinaryMessage(16);
|
||||
received.setInt(validWord, WORD);
|
||||
|
||||
for(int x = 0; x < 16; x++)
|
||||
{
|
||||
softBits[x] = received.get(x) ? 1.0f : -1.0f;
|
||||
}
|
||||
|
||||
Set<Integer> errors = new HashSet<>();
|
||||
|
||||
while(errors.size() < errorCount)
|
||||
{
|
||||
errors.add(random.nextInt(16));
|
||||
}
|
||||
|
||||
for(int error: errors)
|
||||
{
|
||||
received.flip(error);
|
||||
softBits[error] = -0.2f * softBits[error];
|
||||
}
|
||||
|
||||
EMB emb = new EMB(received, softBits);
|
||||
assertEquals(validWord, received.getInt(0, 15), "Trial " + trial + " errors " + errorCount);
|
||||
|
||||
if(errorCount == 1)
|
||||
{
|
||||
assertTrue(emb.isValid());
|
||||
}
|
||||
else
|
||||
{
|
||||
assertFalse(emb.isValid());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,225 @@
|
||||
/*
|
||||
* *****************************************************************************
|
||||
* Copyright (C) 2014-2026 Dennis Sheirer
|
||||
*
|
||||
* 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/>
|
||||
* ****************************************************************************
|
||||
*/
|
||||
|
||||
package io.github.dsheirer.module.decode.dmr.message.voice;
|
||||
|
||||
import io.github.dsheirer.bits.CorrectedBinaryMessage;
|
||||
import io.github.dsheirer.module.decode.dmr.message.type.LCSS;
|
||||
import java.util.HashSet;
|
||||
import java.util.Random;
|
||||
import java.util.Set;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertFalse;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
|
||||
/**
|
||||
* JUnit tests for the DMR EMB QR(16,7,6) codeword table and the hard and soft decision decoders.
|
||||
*/
|
||||
public class EMBTest
|
||||
{
|
||||
private static final int[] WORD = new int[]{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15};
|
||||
//Codewords for the 7 single-bit information words (1, 2, 4, ... 64) from the TS 102 361-1 B.3.2 codeword table.
|
||||
//Because the code is linear, these generator rows determine every other codeword.
|
||||
private static final int[] GENERATOR_ROWS = new int[]{0x0273, 0x04E5, 0x09C9, 0x11E2, 0x21B7, 0x411E, 0x804F};
|
||||
|
||||
/**
|
||||
* Creates an EMB message holding the 16-bit word.
|
||||
*/
|
||||
private static CorrectedBinaryMessage message(int word)
|
||||
{
|
||||
CorrectedBinaryMessage message = new CorrectedBinaryMessage(16);
|
||||
message.setInt(word, WORD);
|
||||
return message;
|
||||
}
|
||||
|
||||
/**
|
||||
* Asserts that the EMB fields decode to the information word (color code, privacy indicator, LCSS).
|
||||
*/
|
||||
private static void assertFields(int information, EMB emb, String context)
|
||||
{
|
||||
assertEquals(information >> 3, emb.getColorCode(), context);
|
||||
assertEquals(((information >> 2) & 1) == 1, emb.isEncrypted(), context);
|
||||
assertEquals(LCSS.fromValue(information & 3), emb.getLCSS(), context);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test: the codeword table is the systematic, linear QR(16,7,6) code from the specification with minimum distance
|
||||
* 6, so that every 1 and 2 bit error is uniquely correctable.
|
||||
*/
|
||||
@Test
|
||||
void testCodewordTable()
|
||||
{
|
||||
int[] words = EMB.getValidWords();
|
||||
assertEquals(128, words.length);
|
||||
|
||||
for(int bit = 0; bit < 7; bit++)
|
||||
{
|
||||
assertEquals(GENERATOR_ROWS[bit], words[1 << bit], "Generator row for information bit " + bit);
|
||||
}
|
||||
|
||||
int minimumDistance = Integer.MAX_VALUE;
|
||||
|
||||
for(int a = 0; a < 128; a++)
|
||||
{
|
||||
assertEquals(a, words[a] >> 9, "Information bits are the leading 7 bits");
|
||||
|
||||
for(int b = 0; b < 128; b++)
|
||||
{
|
||||
assertEquals(words[a ^ b], words[a] ^ words[b], "Linearity " + a + " ^ " + b);
|
||||
|
||||
if(a != b)
|
||||
{
|
||||
minimumDistance = Math.min(minimumDistance, Integer.bitCount(words[a] ^ words[b]));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
assertEquals(6, minimumDistance);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test: the hard decoder accepts every codeword and corrects every 1 and 2 bit error pattern on every codeword
|
||||
* back to that codeword, with the corrected fields (including color codes 8-15).
|
||||
*/
|
||||
@Test
|
||||
void testHardDecodeCorrectsUpToTwoBitErrors()
|
||||
{
|
||||
int[] words = EMB.getValidWords();
|
||||
|
||||
for(int information = 0; information < 128; information++)
|
||||
{
|
||||
int codeword = words[information];
|
||||
|
||||
EMB clean = new EMB(message(codeword));
|
||||
assertTrue(clean.isValid(), "Codeword " + information);
|
||||
assertFields(information, clean, "Codeword " + information);
|
||||
|
||||
for(int first = 0; first < 16; first++)
|
||||
{
|
||||
for(int second = first; second < 16; second++)
|
||||
{
|
||||
int errors = (1 << first) | (1 << second);
|
||||
String context = "Codeword " + information + " errors " + Integer.toHexString(errors);
|
||||
CorrectedBinaryMessage received = message(codeword ^ errors);
|
||||
EMB emb = new EMB(received);
|
||||
assertTrue(emb.isValid(), context);
|
||||
assertEquals(codeword, received.getInt(WORD), context);
|
||||
assertEquals(Integer.bitCount(errors), received.getCorrectedBitCount(), context);
|
||||
assertFields(information, emb, context);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Test: the hard decoder rejects every 3 bit error pattern on every codeword, which is beyond the correction
|
||||
* capability of a distance 6 code, and leaves the received bits unchanged rather than miscorrecting them.
|
||||
*/
|
||||
@Test
|
||||
void testHardDecodeRejectsThreeBitErrors()
|
||||
{
|
||||
int[] words = EMB.getValidWords();
|
||||
|
||||
for(int codeword: words)
|
||||
{
|
||||
for(int a = 0; a < 16; a++)
|
||||
{
|
||||
for(int b = a + 1; b < 16; b++)
|
||||
{
|
||||
for(int c = b + 1; c < 16; c++)
|
||||
{
|
||||
int receivedWord = codeword ^ (1 << a) ^ (1 << b) ^ (1 << c);
|
||||
CorrectedBinaryMessage received = message(receivedWord);
|
||||
EMB emb = new EMB(received);
|
||||
String context = "Received " + Integer.toHexString(receivedWord);
|
||||
assertFalse(emb.isValid(), context);
|
||||
assertEquals(receivedWord, received.getInt(WORD), context);
|
||||
assertEquals(0, received.getCorrectedBitCount(), context);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Test: soft decoding corrects 1 and 2 low reliability bit errors and flags the result valid. With 4 low
|
||||
* reliability bit errors, it still corrects to the transmitted word (best estimate, beyond the hard decoder's
|
||||
* capability) but flags the result invalid since it exceeds the correction capability.
|
||||
*/
|
||||
@Test
|
||||
void testSoftDecode()
|
||||
{
|
||||
int[] words = EMB.getValidWords();
|
||||
Random random = new Random(3);
|
||||
|
||||
for(int trial = 0; trial < 1000; trial++)
|
||||
{
|
||||
int information = random.nextInt(128);
|
||||
int codeword = words[information];
|
||||
|
||||
for(int errorCount: new int[]{1, 2, 4})
|
||||
{
|
||||
CorrectedBinaryMessage received = message(codeword);
|
||||
float[] softBits = new float[16];
|
||||
|
||||
for(int x = 0; x < 16; x++)
|
||||
{
|
||||
softBits[x] = received.get(x) ? 1.0f : -1.0f;
|
||||
}
|
||||
|
||||
Set<Integer> errors = new HashSet<>();
|
||||
|
||||
while(errors.size() < errorCount)
|
||||
{
|
||||
errors.add(random.nextInt(16));
|
||||
}
|
||||
|
||||
for(int error: errors)
|
||||
{
|
||||
received.flip(error);
|
||||
softBits[error] = -0.2f * softBits[error];
|
||||
}
|
||||
|
||||
EMB emb = new EMB(received, softBits);
|
||||
String context = "Trial " + trial + " errors " + errorCount;
|
||||
assertEquals(errorCount <= 2, emb.isValid(), context);
|
||||
assertEquals(codeword, received.getInt(WORD), context);
|
||||
assertEquals(errorCount, received.getCorrectedBitCount(), context);
|
||||
assertFields(information, emb, context);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Test: without soft bits, the soft decision constructor falls back to the hard decoder.
|
||||
*/
|
||||
@Test
|
||||
void testNullSoftBitsUsesHardDecoder()
|
||||
{
|
||||
int codeword = EMB.getValidWords()[93];
|
||||
CorrectedBinaryMessage received = message(codeword ^ 0x0101);
|
||||
EMB emb = new EMB(received, null);
|
||||
assertTrue(emb.isValid());
|
||||
assertEquals(codeword, received.getInt(WORD));
|
||||
assertEquals(2, received.getCorrectedBitCount());
|
||||
assertFields(93, emb, "Null soft bits");
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user