You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
609 lines
15 KiB
609 lines
15 KiB
// SPDX-License-Identifier: GPL-2.0-only
|
|
/*
|
|
* Digital Voice Modem - Common Library
|
|
* GPLv2 Open Source. Use is subject to license terms.
|
|
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
|
*
|
|
* Copyright (C) 2010,2014,2016,2021 Jonathan Naylor, G4KLX
|
|
* Copyright (C) 2016 Mathias Weyland, HB9FRV
|
|
* Copyright (C) 2018-2022 Bryan Biedenkapp, N2PLL
|
|
*
|
|
*/
|
|
#include "Defines.h"
|
|
#include "edac/AMBEFEC.h"
|
|
#include "edac/Golay24128.h"
|
|
#include "edac/Hamming.h"
|
|
#include "Log.h"
|
|
#include "Utils.h"
|
|
|
|
using namespace edac;
|
|
|
|
#include <cstdio>
|
|
#include <cassert>
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// Public Class Members
|
|
// ---------------------------------------------------------------------------
|
|
|
|
/* Initializes a new instance of the AMBEFEC class. */
|
|
|
|
AMBEFEC::AMBEFEC() = default;
|
|
|
|
/* Finalizes a instance of the AMBEFEC class. */
|
|
|
|
AMBEFEC::~AMBEFEC() = default;
|
|
|
|
/* Regenerates the DMR AMBE FEC for the input bytes. */
|
|
|
|
uint32_t AMBEFEC::regenerateDMR(uint8_t* bytes) const
|
|
{
|
|
assert(bytes != nullptr);
|
|
|
|
uint32_t a1 = 0U, a2 = 0U, a3 = 0U;
|
|
uint32_t b1 = 0U, b2 = 0U, b3 = 0U;
|
|
uint32_t c1 = 0U, c2 = 0U, c3 = 0U;
|
|
|
|
uint32_t MASK = 0x800000U;
|
|
for (uint32_t i = 0U; i < 24U; i++, MASK >>= 1) {
|
|
uint32_t a1Pos = AMBE_A_TABLE[i];
|
|
uint32_t a2Pos = a1Pos + 72U;
|
|
if (a2Pos >= 108U)
|
|
a2Pos += 48U;
|
|
uint32_t a3Pos = a1Pos + 192U;
|
|
|
|
if (READ_BIT(bytes, a1Pos))
|
|
a1 |= MASK;
|
|
if (READ_BIT(bytes, a2Pos))
|
|
a2 |= MASK;
|
|
if (READ_BIT(bytes, a3Pos))
|
|
a3 |= MASK;
|
|
}
|
|
|
|
MASK = 0x400000U;
|
|
for (uint32_t i = 0U; i < 23U; i++, MASK >>= 1) {
|
|
uint32_t b1Pos = AMBE_B_TABLE[i];
|
|
uint32_t b2Pos = b1Pos + 72U;
|
|
if (b2Pos >= 108U)
|
|
b2Pos += 48U;
|
|
uint32_t b3Pos = b1Pos + 192U;
|
|
|
|
if (READ_BIT(bytes, b1Pos))
|
|
b1 |= MASK;
|
|
if (READ_BIT(bytes, b2Pos))
|
|
b2 |= MASK;
|
|
if (READ_BIT(bytes, b3Pos))
|
|
b3 |= MASK;
|
|
}
|
|
|
|
MASK = 0x1000000U;
|
|
for (uint32_t i = 0U; i < 25U; i++, MASK >>= 1) {
|
|
uint32_t c1Pos = AMBE_C_TABLE[i];
|
|
uint32_t c2Pos = c1Pos + 72U;
|
|
if (c2Pos >= 108U)
|
|
c2Pos += 48U;
|
|
uint32_t c3Pos = c1Pos + 192U;
|
|
|
|
if (READ_BIT(bytes, c1Pos))
|
|
c1 |= MASK;
|
|
if (READ_BIT(bytes, c2Pos))
|
|
c2 |= MASK;
|
|
if (READ_BIT(bytes, c3Pos))
|
|
c3 |= MASK;
|
|
}
|
|
|
|
uint32_t errors = regenerate(a1, b1, c1);
|
|
errors += regenerate(a2, b2, c2);
|
|
errors += regenerate(a3, b3, c3);
|
|
|
|
MASK = 0x800000U;
|
|
for (uint32_t i = 0U; i < 24U; i++, MASK >>= 1) {
|
|
uint32_t a1Pos = AMBE_A_TABLE[i];
|
|
uint32_t a2Pos = a1Pos + 72U;
|
|
if (a2Pos >= 108U)
|
|
a2Pos += 48U;
|
|
uint32_t a3Pos = a1Pos + 192U;
|
|
|
|
WRITE_BIT(bytes, a1Pos, a1 & MASK);
|
|
WRITE_BIT(bytes, a2Pos, a2 & MASK);
|
|
WRITE_BIT(bytes, a3Pos, a3 & MASK);
|
|
}
|
|
|
|
MASK = 0x400000U;
|
|
for (uint32_t i = 0U; i < 23U; i++, MASK >>= 1) {
|
|
uint32_t b1Pos = AMBE_B_TABLE[i];
|
|
uint32_t b2Pos = b1Pos + 72U;
|
|
if (b2Pos >= 108U)
|
|
b2Pos += 48U;
|
|
uint32_t b3Pos = b1Pos + 192U;
|
|
|
|
WRITE_BIT(bytes, b1Pos, b1 & MASK);
|
|
WRITE_BIT(bytes, b2Pos, b2 & MASK);
|
|
WRITE_BIT(bytes, b3Pos, b3 & MASK);
|
|
}
|
|
|
|
MASK = 0x1000000U;
|
|
for (uint32_t i = 0U; i < 25U; i++, MASK >>= 1) {
|
|
uint32_t c1Pos = AMBE_C_TABLE[i];
|
|
uint32_t c2Pos = c1Pos + 72U;
|
|
if (c2Pos >= 108U)
|
|
c2Pos += 48U;
|
|
uint32_t c3Pos = c1Pos + 192U;
|
|
|
|
WRITE_BIT(bytes, c1Pos, c1 & MASK);
|
|
WRITE_BIT(bytes, c2Pos, c2 & MASK);
|
|
WRITE_BIT(bytes, c3Pos, c3 & MASK);
|
|
}
|
|
|
|
return errors;
|
|
}
|
|
|
|
/* Returns the number of errors on the DMR BER input bytes. */
|
|
|
|
uint32_t AMBEFEC::measureDMRBER(const uint8_t* bytes) const
|
|
{
|
|
assert(bytes != nullptr);
|
|
|
|
uint32_t a1 = 0U, a2 = 0U, a3 = 0U;
|
|
uint32_t b1 = 0U, b2 = 0U, b3 = 0U;
|
|
uint32_t c1 = 0U, c2 = 0U, c3 = 0U;
|
|
|
|
uint32_t MASK = 0x800000U;
|
|
for (uint32_t i = 0U; i < 24U; i++, MASK >>= 1) {
|
|
uint32_t a1Pos = AMBE_A_TABLE[i];
|
|
uint32_t a2Pos = a1Pos + 72U;
|
|
if (a2Pos >= 108U)
|
|
a2Pos += 48U;
|
|
uint32_t a3Pos = a1Pos + 192U;
|
|
|
|
if (READ_BIT(bytes, a1Pos))
|
|
a1 |= MASK;
|
|
if (READ_BIT(bytes, a2Pos))
|
|
a2 |= MASK;
|
|
if (READ_BIT(bytes, a3Pos))
|
|
a3 |= MASK;
|
|
}
|
|
|
|
MASK = 0x400000U;
|
|
for (uint32_t i = 0U; i < 23U; i++, MASK >>= 1) {
|
|
uint32_t b1Pos = AMBE_B_TABLE[i];
|
|
uint32_t b2Pos = b1Pos + 72U;
|
|
if (b2Pos >= 108U)
|
|
b2Pos += 48U;
|
|
uint32_t b3Pos = b1Pos + 192U;
|
|
|
|
if (READ_BIT(bytes, b1Pos))
|
|
b1 |= MASK;
|
|
if (READ_BIT(bytes, b2Pos))
|
|
b2 |= MASK;
|
|
if (READ_BIT(bytes, b3Pos))
|
|
b3 |= MASK;
|
|
}
|
|
|
|
MASK = 0x1000000U;
|
|
for (uint32_t i = 0U; i < 25U; i++, MASK >>= 1) {
|
|
uint32_t c1Pos = AMBE_C_TABLE[i];
|
|
uint32_t c2Pos = c1Pos + 72U;
|
|
if (c2Pos >= 108U)
|
|
c2Pos += 48U;
|
|
uint32_t c3Pos = c1Pos + 192U;
|
|
|
|
if (READ_BIT(bytes, c1Pos))
|
|
c1 |= MASK;
|
|
if (READ_BIT(bytes, c2Pos))
|
|
c2 |= MASK;
|
|
if (READ_BIT(bytes, c3Pos))
|
|
c3 |= MASK;
|
|
}
|
|
|
|
uint32_t errors = regenerate(a1, b1, c1);
|
|
errors += regenerate(a2, b2, c2);
|
|
errors += regenerate(a3, b3, c3);
|
|
|
|
return errors;
|
|
}
|
|
|
|
/* Regenerates the P25 IMBE FEC for the input bytes. */
|
|
|
|
uint32_t AMBEFEC::regenerateIMBE(uint8_t* bytes) const
|
|
{
|
|
assert(bytes != nullptr);
|
|
|
|
bool orig[144U];
|
|
bool temp[144U];
|
|
|
|
// De-interleave
|
|
for (uint32_t i = 0U; i < 144U; i++) {
|
|
uint32_t n = IMBE_INTERLEAVE[i];
|
|
orig[i] = temp[i] = READ_BIT(bytes, n);
|
|
}
|
|
|
|
// now ..
|
|
|
|
// 12 voice bits 0
|
|
// 11 golay bits 12
|
|
//
|
|
// 12 voice bits 23
|
|
// 11 golay bits 35
|
|
//
|
|
// 12 voice bits 46
|
|
// 11 golay bits 58
|
|
//
|
|
// 12 voice bits 69
|
|
// 11 golay bits 81
|
|
//
|
|
// 11 voice bits 92
|
|
// 4 hamming bits 103
|
|
//
|
|
// 11 voice bits 107
|
|
// 4 hamming bits 118
|
|
//
|
|
// 11 voice bits 122
|
|
// 4 hamming bits 133
|
|
//
|
|
// 7 voice bits 137
|
|
|
|
// Process the c0 section first to allow the de-whitening to be accurate
|
|
|
|
// Check/Fix FEC
|
|
bool* bit = temp;
|
|
|
|
// c0
|
|
uint32_t g1 = 0U;
|
|
for (uint32_t i = 0U; i < 23U; i++)
|
|
g1 = (g1 << 1) | (bit[i] ? 0x01U : 0x00U);
|
|
uint32_t c0data = Golay24128::decode23127(g1);
|
|
uint32_t g2 = Golay24128::encode23127(c0data);
|
|
for (int i = 23; i >= 0; i--) {
|
|
bit[i] = (g2 & 0x01U) == 0x01U;
|
|
g2 >>= 1;
|
|
}
|
|
bit += 23U;
|
|
|
|
bool prn[114U];
|
|
|
|
// Create the whitening vector and save it for future use
|
|
uint32_t p = 16U * c0data;
|
|
for (uint32_t i = 0U; i < 114U; i++) {
|
|
p = (173U * p + 13849U) % 65536U;
|
|
prn[i] = p >= 32768U;
|
|
}
|
|
|
|
// De-whiten some bits
|
|
for (uint32_t i = 0U; i < 114U; i++)
|
|
temp[i + 23U] ^= prn[i];
|
|
|
|
// c1
|
|
g1 = 0U;
|
|
for (uint32_t i = 0U; i < 23U; i++)
|
|
g1 = (g1 << 1) | (bit[i] ? 0x01U : 0x00U);
|
|
uint32_t c1data = Golay24128::decode23127(g1);
|
|
g2 = Golay24128::encode23127(c1data);
|
|
for (int i = 23; i >= 0; i--) {
|
|
bit[i] = (g2 & 0x01U) == 0x01U;
|
|
g2 >>= 1;
|
|
}
|
|
bit += 23U;
|
|
|
|
// c2
|
|
g1 = 0;
|
|
for (uint32_t i = 0U; i < 23U; i++)
|
|
g1 = (g1 << 1) | (bit[i] ? 0x01U : 0x00U);
|
|
uint32_t c2data = Golay24128::decode23127(g1);
|
|
g2 = Golay24128::encode23127(c2data);
|
|
for (int i = 23; i >= 0; i--) {
|
|
bit[i] = (g2 & 0x01U) == 0x01U;
|
|
g2 >>= 1;
|
|
}
|
|
bit += 23U;
|
|
|
|
// c3
|
|
g1 = 0U;
|
|
for (uint32_t i = 0U; i < 23U; i++)
|
|
g1 = (g1 << 1) | (bit[i] ? 0x01U : 0x00U);
|
|
uint32_t c3data = Golay24128::decode23127(g1);
|
|
g2 = Golay24128::encode23127(c3data);
|
|
for (int i = 23; i >= 0; i--) {
|
|
bit[i] = (g2 & 0x01U) == 0x01U;
|
|
g2 >>= 1;
|
|
}
|
|
bit += 23U;
|
|
|
|
// c4
|
|
Hamming::decode15113_1(bit);
|
|
bit += 15U;
|
|
|
|
// c5
|
|
Hamming::decode15113_1(bit);
|
|
bit += 15U;
|
|
|
|
// c6
|
|
Hamming::decode15113_1(bit);
|
|
|
|
// Whiten some bits
|
|
for (uint32_t i = 0U; i < 114U; i++)
|
|
temp[i + 23U] ^= prn[i];
|
|
|
|
uint32_t errors = 0U;
|
|
for (uint32_t i = 0U; i < 144U; i++) {
|
|
if (orig[i] != temp[i])
|
|
errors++;
|
|
}
|
|
|
|
// Interleave
|
|
for (uint32_t i = 0U; i < 144U; i++) {
|
|
uint32_t n = IMBE_INTERLEAVE[i];
|
|
WRITE_BIT(bytes, n, temp[i]);
|
|
}
|
|
|
|
return errors;
|
|
}
|
|
|
|
/* Returns the number of errors on the P25 BER input bytes. */
|
|
|
|
uint32_t AMBEFEC::measureP25BER(const uint8_t* bytes) const
|
|
{
|
|
assert(bytes != nullptr);
|
|
|
|
bool orig[144U];
|
|
bool temp[144U];
|
|
|
|
// De-interleave
|
|
for (uint32_t i = 0U; i < 144U; i++) {
|
|
uint32_t n = IMBE_INTERLEAVE[i];
|
|
orig[i] = temp[i] = READ_BIT(bytes, n);
|
|
}
|
|
|
|
// now ..
|
|
|
|
// 12 voice bits 0
|
|
// 11 golay bits 12
|
|
//
|
|
// 12 voice bits 23
|
|
// 11 golay bits 35
|
|
//
|
|
// 12 voice bits 46
|
|
// 11 golay bits 58
|
|
//
|
|
// 12 voice bits 69
|
|
// 11 golay bits 81
|
|
//
|
|
// 11 voice bits 92
|
|
// 4 hamming bits 103
|
|
//
|
|
// 11 voice bits 107
|
|
// 4 hamming bits 118
|
|
//
|
|
// 11 voice bits 122
|
|
// 4 hamming bits 133
|
|
//
|
|
// 7 voice bits 137
|
|
|
|
// Process the c0 section first to allow the de-whitening to be accurate
|
|
|
|
// Check/Fix FEC
|
|
bool* bit = temp;
|
|
|
|
// c0
|
|
uint32_t g1 = 0U;
|
|
for (uint32_t i = 0U; i < 23U; i++)
|
|
g1 = (g1 << 1) | (bit[i] ? 0x01U : 0x00U);
|
|
uint32_t c0data = Golay24128::decode23127(g1);
|
|
uint32_t g2 = Golay24128::encode23127(c0data);
|
|
for (int i = 23; i >= 0; i--) {
|
|
bit[i] = (g2 & 0x01U) == 0x01U;
|
|
g2 >>= 1;
|
|
}
|
|
bit += 23U;
|
|
|
|
bool prn[114U];
|
|
|
|
// Create the whitening vector and save it for future use
|
|
uint32_t p = 16U * c0data;
|
|
for (uint32_t i = 0U; i < 114U; i++) {
|
|
p = (173U * p + 13849U) % 65536U;
|
|
prn[i] = p >= 32768U;
|
|
}
|
|
|
|
// De-whiten some bits
|
|
for (uint32_t i = 0U; i < 114U; i++)
|
|
temp[i + 23U] ^= prn[i];
|
|
|
|
// c1
|
|
g1 = 0U;
|
|
for (uint32_t i = 0U; i < 23U; i++)
|
|
g1 = (g1 << 1) | (bit[i] ? 0x01U : 0x00U);
|
|
uint32_t c1data = Golay24128::decode23127(g1);
|
|
g2 = Golay24128::encode23127(c1data);
|
|
for (int i = 23; i >= 0; i--) {
|
|
bit[i] = (g2 & 0x01U) == 0x01U;
|
|
g2 >>= 1;
|
|
}
|
|
bit += 23U;
|
|
|
|
// c2
|
|
g1 = 0;
|
|
for (uint32_t i = 0U; i < 23U; i++)
|
|
g1 = (g1 << 1) | (bit[i] ? 0x01U : 0x00U);
|
|
uint32_t c2data = Golay24128::decode23127(g1);
|
|
g2 = Golay24128::encode23127(c2data);
|
|
for (int i = 23; i >= 0; i--) {
|
|
bit[i] = (g2 & 0x01U) == 0x01U;
|
|
g2 >>= 1;
|
|
}
|
|
bit += 23U;
|
|
|
|
// c3
|
|
g1 = 0U;
|
|
for (uint32_t i = 0U; i < 23U; i++)
|
|
g1 = (g1 << 1) | (bit[i] ? 0x01U : 0x00U);
|
|
uint32_t c3data = Golay24128::decode23127(g1);
|
|
g2 = Golay24128::encode23127(c3data);
|
|
for (int i = 23; i >= 0; i--) {
|
|
bit[i] = (g2 & 0x01U) == 0x01U;
|
|
g2 >>= 1;
|
|
}
|
|
bit += 23U;
|
|
|
|
// c4
|
|
Hamming::decode15113_1(bit);
|
|
bit += 15U;
|
|
|
|
// c5
|
|
Hamming::decode15113_1(bit);
|
|
bit += 15U;
|
|
|
|
// c6
|
|
Hamming::decode15113_1(bit);
|
|
|
|
// Whiten some bits
|
|
for (uint32_t i = 0U; i < 114U; i++)
|
|
temp[i + 23U] ^= prn[i];
|
|
|
|
uint32_t errors = 0U;
|
|
for (uint32_t i = 0U; i < 144U; i++) {
|
|
if (orig[i] != temp[i])
|
|
errors++;
|
|
}
|
|
|
|
return errors;
|
|
}
|
|
|
|
/* Regenerates the NXDN AMBE FEC for the input bytes. */
|
|
|
|
uint32_t AMBEFEC::regenerateNXDN(uint8_t* bytes) const
|
|
{
|
|
assert(bytes != nullptr);
|
|
|
|
uint32_t a = 0U;
|
|
uint32_t MASK = 0x800000U;
|
|
for (uint32_t i = 0U; i < 24U; i++, MASK >>= 1) {
|
|
uint32_t aPos = AMBE_A_TABLE[i];
|
|
if (READ_BIT(bytes, aPos))
|
|
a |= MASK;
|
|
}
|
|
|
|
uint32_t b = 0U;
|
|
MASK = 0x400000U;
|
|
for (uint32_t i = 0U; i < 23U; i++, MASK >>= 1) {
|
|
uint32_t bPos = AMBE_B_TABLE[i];
|
|
if (READ_BIT(bytes, bPos))
|
|
b |= MASK;
|
|
}
|
|
|
|
uint32_t c = 0U;
|
|
MASK = 0x1000000U;
|
|
for (uint32_t i = 0U; i < 25U; i++, MASK >>= 1) {
|
|
uint32_t cPos = AMBE_C_TABLE[i];
|
|
if (READ_BIT(bytes, cPos))
|
|
c |= MASK;
|
|
}
|
|
|
|
uint32_t errors = regenerate(a, b, c);
|
|
|
|
MASK = 0x800000U;
|
|
for (uint32_t i = 0U; i < 24U; i++, MASK >>= 1) {
|
|
uint32_t aPos = AMBE_A_TABLE[i];
|
|
WRITE_BIT(bytes, aPos, a & MASK);
|
|
}
|
|
|
|
MASK = 0x400000U;
|
|
for (uint32_t i = 0U; i < 23U; i++, MASK >>= 1) {
|
|
uint32_t bPos = AMBE_B_TABLE[i];
|
|
WRITE_BIT(bytes, bPos, b & MASK);
|
|
}
|
|
|
|
MASK = 0x1000000U;
|
|
for (uint32_t i = 0U; i < 25U; i++, MASK >>= 1) {
|
|
uint32_t cPos = AMBE_C_TABLE[i];
|
|
WRITE_BIT(bytes, cPos, c & MASK);
|
|
}
|
|
|
|
return errors;
|
|
}
|
|
|
|
/* Returns the number of errors on the NXDN BER input bytes. */
|
|
|
|
uint32_t AMBEFEC::measureNXDNBER(uint8_t* bytes) const
|
|
{
|
|
assert(bytes != nullptr);
|
|
|
|
uint32_t a = 0U;
|
|
uint32_t MASK = 0x800000U;
|
|
for (uint32_t i = 0U; i < 24U; i++, MASK >>= 1) {
|
|
uint32_t aPos = AMBE_A_TABLE[i];
|
|
if (READ_BIT(bytes, aPos))
|
|
a |= MASK;
|
|
}
|
|
|
|
uint32_t b = 0U;
|
|
MASK = 0x400000U;
|
|
for (uint32_t i = 0U; i < 23U; i++, MASK >>= 1) {
|
|
uint32_t bPos = AMBE_B_TABLE[i];
|
|
if (READ_BIT(bytes, bPos))
|
|
b |= MASK;
|
|
}
|
|
|
|
uint32_t c = 0U;
|
|
MASK = 0x1000000U;
|
|
for (uint32_t i = 0U; i < 25U; i++, MASK >>= 1) {
|
|
uint32_t cPos = AMBE_C_TABLE[i];
|
|
if (READ_BIT(bytes, cPos))
|
|
c |= MASK;
|
|
}
|
|
|
|
uint32_t errors = regenerate(a, b, c);
|
|
return errors;
|
|
}
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// Private Class Members
|
|
// ---------------------------------------------------------------------------
|
|
|
|
/* */
|
|
|
|
uint32_t AMBEFEC::regenerate(uint32_t& a, uint32_t& b, uint32_t& c) const
|
|
{
|
|
uint32_t old_a = a;
|
|
uint32_t old_b = b;
|
|
|
|
uint32_t data;
|
|
bool valid = Golay24128::decode24128(a, data);
|
|
if (!valid) {
|
|
uint32_t errsA = Utils::countBits32(data ^ a);
|
|
#if DEBUG_AMBEFEC
|
|
LogDebug(LOG_HOST, "AMBEFEC::regnerate() invalid A block, errsA = %u, a = %6X, b = %6X, c = %6X", errsA, a, b, c);
|
|
#endif
|
|
a = 0xF00292U;
|
|
b = 0x0E0B20U;
|
|
c = 0x000000U;
|
|
return errsA;
|
|
}
|
|
|
|
a = Golay24128::encode24128(data);
|
|
|
|
// PRNG
|
|
uint32_t p = PRNG_TABLE[data] >> 1;
|
|
b ^= p;
|
|
|
|
uint32_t datb = Golay24128::decode23127(b);
|
|
b = Golay24128::encode23127(datb) >> 1;
|
|
|
|
b ^= p;
|
|
|
|
uint32_t v = a ^ old_a;
|
|
uint32_t errsA = Utils::countBits32(v);
|
|
|
|
v = b ^ old_b;
|
|
uint32_t errsB = Utils::countBits32(v);
|
|
#if DEBUG_AMBEFEC
|
|
LogDebug(LOG_HOST, "AMBEFEC::regnerate() errsA = %u, a = %6X, errsB = %u, b = %6X, c = %6X", errsA, a, errsB, b, c);
|
|
#endif
|
|
if (errsA >= 4U || ((errsA + errsB) >= 6U && errsA >= 2U)) {
|
|
a = 0xF00292U;
|
|
b = 0x0E0B20U;
|
|
c = 0x000000U;
|
|
}
|
|
|
|
return errsA + errsB;
|
|
}
|