From b7c07ac7b36ffcf08c66341e11db187c453686e4 Mon Sep 17 00:00:00 2001 From: Wijnand Modderman-Lenstra Date: Fri, 11 Dec 2015 23:58:46 +0100 Subject: [PATCH] bptc: simplified --- bptc/bptc.go | 308 +++++++--------------------------------------- bptc/bptc19696.go | 133 ++++++++++++++++++++ bptc/bptc_test.go | 35 ------ 3 files changed, 175 insertions(+), 301 deletions(-) create mode 100644 bptc/bptc19696.go delete mode 100644 bptc/bptc_test.go diff --git a/bptc/bptc.go b/bptc/bptc.go index d5f3972..6db0192 100644 --- a/bptc/bptc.go +++ b/bptc/bptc.go @@ -1,296 +1,72 @@ -// Package bptc implements the BPTC(196, 96) Block Product Turbo Code package bptc import ( - "errors" "fmt" "github.com/pd0mz/go-dmr/bit" + "github.com/pd0mz/go-dmr/fec" ) -type vector [4]bit.Bit - -// hamming(15, 11, 3) checking of a matrix row (15 total bits, 11 data bits, -// min. distance: 3) See page 135 of the DMR Air Interface protocol -// specification for the generator matrix. A generator matrix looks like this: -// G = [Ik | P]. The parity check matrix is: H = [-P^T|In-k] In binary codes, -// then -P = P, so the negation is unnecessary. We can get the parity check -// matrix only by transposing the generator matrix. We then take a data row, -// and multiply it with each row of the parity check matrix, then xor each -// resulting row bits together with the corresponding parity check bit. The xor -// result (error vector) should be 0, if it's not, it can be used to determine -// the location of the erroneous bit using the generator matrix (P). -func hamming_15_11_3_parity(data bit.Bits, errorVector *vector) { - if data == nil || len(data) < 11 || errorVector == nil { - return +func Process(info bit.Bits, payload []byte) error { + if len(info) < 196 { + return fmt.Errorf("bptc: info size %d too small, need at least 196 bits", len(info)) } - - var e = *errorVector - e[0] = (data[0] ^ data[1] ^ data[2] ^ data[3] ^ data[5] ^ data[7] ^ data[8]) - e[1] = (data[1] ^ data[2] ^ data[3] ^ data[4] ^ data[6] ^ data[8] ^ data[9]) - e[2] = (data[2] ^ data[3] ^ data[4] ^ data[5] ^ data[7] ^ data[9] ^ data[10]) - e[3] = (data[0] ^ data[1] ^ data[2] ^ data[4] ^ data[6] ^ data[7] ^ data[10]) -} - -func hamming_15_11_3_check(data bit.Bits, errorVector *vector) bool { - if data == nil || len(data) < 15 || errorVector == nil { - return false + if len(payload) < 12 { + return fmt.Errorf("bptc: payload size %d too small, need at least 12 bytes", len(payload)) } - hamming_15_11_3_parity(data, errorVector) - - var e = *errorVector - e[0] ^= data[11] - e[1] ^= data[12] - e[2] ^= data[13] - e[3] ^= data[14] - - return e[0] == 0 && e[1] == 0 && e[2] == 0 && e[3] == 0 -} + var ( + i, j, k uint32 + datafr = make(bit.Bits, 196) + extracted = make(bit.Bits, 96) + ) -func hamming_13_9_3_parity(data bit.Bits, errorVector *vector) { - if data == nil || len(data) < 9 || errorVector == nil { - return + // Deinterleave bits + for i = 1; i < 197; i++ { + datafr[i-1] = info[((i * 181) % 196)] } - var e = *errorVector - e[0] = (data[0] ^ data[1] ^ data[3] ^ data[5] ^ data[6]) - e[1] = (data[0] ^ data[1] ^ data[2] ^ data[4] ^ data[6] ^ data[7]) - e[2] = (data[0] ^ data[1] ^ data[2] ^ data[3] ^ data[5] ^ data[7] ^ data[8]) - e[3] = (data[0] ^ data[2] ^ data[4] ^ data[5] ^ data[8]) -} - -// hamming(13, 9, 3) checking of a matrix column (13 total bits, 9 data bits, -// min. distance: 3) -func hamming_13_9_3_check(data bit.Bits, errorVector *vector) bool { - if data == nil || len(data) < 13 || errorVector == nil { - return false + // Zero reserved bits + for i = 0; i < 3; i++ { + datafr[0*15+i] = 0 } - hamming_13_9_3_parity(data, errorVector) - - var e = *errorVector - e[0] ^= data[9] - e[1] ^= data[10] - e[2] ^= data[11] - e[3] ^= data[12] - - return e[0] == 0 && e[1] == 0 && e[2] == 0 && e[3] == 0 -} - -var hamming_15_11_generator_matrix = bit.Bits{ - 1, 0, 0, 1, - 1, 1, 0, 1, - 1, 1, 1, 1, - 1, 1, 1, 0, - 0, 1, 1, 1, - 1, 0, 1, 0, - 0, 1, 0, 1, - 1, 0, 1, 1, - 1, 1, 0, 0, - 0, 1, 1, 0, - 0, 0, 1, 1, - - 1, 0, 0, 0, // These are used to determine errors in the hamming checksum bits. - 0, 1, 0, 0, - 0, 0, 1, 0, - 0, 0, 0, 1, -} - -func hamming_15_11_3_error_position(errorVector *vector) int { - if errorVector == nil { - return -1 - } - var e = *errorVector - for row := 0; row < 15; row++ { - if hamming_15_11_generator_matrix[row*4] == e[0] && - hamming_15_11_generator_matrix[row*4+1] == e[1] && - hamming_15_11_generator_matrix[row*4+2] == e[2] && - hamming_15_11_generator_matrix[row*4+3] == e[3] { - return row + for i = 0; i < 15; i++ { + var codeword uint32 + for j = 0; j < 13; j++ { + codeword <<= 1 + codeword |= uint32(datafr[j*15+i]) } - } - return -1 -} - -var hamming_13_9_generator_matrix = bit.Bits{ - 1, 1, 1, 1, - 1, 1, 1, 0, - 0, 1, 1, 1, - 0, 1, 1, 1, - 0, 1, 0, 1, - 1, 0, 1, 1, - 1, 1, 0, 0, - 0, 1, 1, 0, - 0, 0, 1, 1, - 1, 0, 0, 0, // These are used to determine errors in the hamming checksum bits. - 0, 1, 0, 0, - 0, 0, 1, 0, - 0, 0, 0, 1, -} - -func hamming_13_9_3_error_position(errorVector *vector) int { - if errorVector == nil { - return -1 - } - var e = *errorVector - for row := 0; row < 13; row++ { - if hamming_13_9_generator_matrix[row*4] == e[0] && - hamming_13_9_generator_matrix[row*4+1] == e[1] && - hamming_13_9_generator_matrix[row*4+2] == e[2] && - hamming_13_9_generator_matrix[row*4+3] == e[3] { - return row + fec.Hamming15_11_3_Correct(&codeword) + codeword &= 0x01ff + for j = 0; j < 9; j++ { + datafr[j*15+i] = bit.Bit((codeword >> (8 - j)) & 1) } } - return -1 -} - -func Dump(bits bit.Bits) { - if len(bits) != 196 { - return - } - - var row, col int - - fmt.Println("BPTC(196, 96) matrix:") - fmt.Print(" |") - for col = 0; col < 15; col++ { - fmt.Printf("%x", col) - if col == 10 { - fmt.Print(" ") - } - } - fmt.Println("\n --+----------- ----") - for row = 0; row < 13; row++ { - fmt.Printf(" %.2d|", row) - for col = 0; col < 11; col++ { - // +1 because the first bit is R(3) and it's not used - // so we can ignore that. - fmt.Printf("%d", bits[col+row*15+1]) - } - fmt.Print(" ") - for ; col < 15; col++ { - // +1 because the first bit is R(3) and it's not used - // so we can ignore that. - fmt.Printf("%d", bits[col+row*15+1]) - } - fmt.Println("") - if row == 8 { - fmt.Println(" :") - } - } -} - -func CheckAndRepair(bits bit.Bits) (bool, error) { - if bits == nil || len(bits) != 196 { - return false, errors.New("expected 196 input bits") - } - - var ( - cb = make([]bit.Bit, 13) - errorVector = vector{} - ) - for col := 0; col < 15; col++ { - for row := 0; row < 13; row++ { - // +1 because the first bit is R(3) and it's not used so we can ignore that. - cb[row] = bits[col+row*15+1] + for j = 0; j < 9; j++ { + var codeword uint32 + for i = 0; i < 15; i++ { + codeword <<= 1 + codeword |= uint32(datafr[j*15+i]) } - - if !hamming_13_9_3_check(cb, &errorVector) { - wrong := hamming_13_9_3_error_position(&errorVector) - if wrong < 0 { - return false, fmt.Errorf("dmr/bptc(196, 96): hamming(13, 9) check error in column #%d, can't repair", col) - } - - // Fix bit error - bits[col+wrong*15+1] ^= 1 - for row := 0; row < 13; row++ { - // +1 because the first bit is R(3) and it's not used so we can ignore that. - cb[row] = bits[col+row*15+1] - } - - if !hamming_13_9_3_check(cb, &errorVector) { - return false, fmt.Errorf("dmr/bptc(196, 96): hamming(13, 9) check error in column #%d, couldn't repair", col) - } + fec.Hamming15_11_3_Correct(&codeword) + for i = 0; i < 11; i++ { + datafr[j*15+10-i] = bit.Bit((codeword >> i) & 1) } } - for row := 0; row < 9; row++ { - // +1 because the first bit is R(3) and it's not used so we can ignore that. - if !hamming_15_11_3_check(bits[row*15+1:], &errorVector) { - wrong := hamming_15_11_3_error_position(&errorVector) - if wrong < 0 { - return false, fmt.Errorf("dmr/bptc(196, 96): hamming(15, 11) check error in row #%d, can't repair", row) - } - - // Fix bit error - bits[row*15+wrong+1] ^= 1 - if !hamming_15_11_3_check(bits[row*15+1:], &errorVector) { - return false, fmt.Errorf("dmr/bptc (196,96): hamming(15,11) check error, couldn't repair row #%d", row) - } - } + // Extract data bits + for i, k = 3, 0; i < 11; i, k = i+1, k+1 { + extracted[k] = datafr[0*15+i] } - - return true, nil -} - -// Extract the data bits from the given deinterleaved info bits array (discards BPTC bits). -func Extract(bits bit.Bits) bit.Bits { - var e = make([]bit.Bit, 96) - copy(e[0:8], bits[4:12]) - copy(e[8:19], bits[16:27]) - copy(e[19:30], bits[31:42]) - copy(e[30:41], bits[46:57]) - copy(e[41:52], bits[61:72]) - copy(e[52:63], bits[76:87]) - copy(e[63:74], bits[91:102]) - copy(e[74:85], bits[106:117]) - copy(e[85:96], bits[121:132]) - return e -} - -// New BPTC(196, 96) payload from 96 data bits. -func New(bits bit.Bits) bit.Bits { - var ( - dbp int - errorVector = vector{} - p = make([]bit.Bit, 196) - ) - - for row := 0; row < 9; row++ { - if row == 0 { - for col := 3; col < 11; col++ { - // +1 because the first bit is R(3) and it's not used so we can ignore that. - p[col+1] = bits[dbp] - dbp++ - } - } else { - for col := 0; col < 11; col++ { - // +1 because the first bit is R(3) and it's not used so we can ignore that. - p[col+row*15+1] = bits[dbp] - dbp++ - } + for j = 1; j < 9; j++ { + for i = 0; i < 11; i, k = i+1, k+1 { + extracted[k] = datafr[j*15+i] } - // +1 because the first bit is R(3) and it's not used so we can ignore that. - hamming_15_11_3_parity(p[row*15+1:], &errorVector) - p[row*15+11+1] = errorVector[0] - p[row*15+12+1] = errorVector[1] - p[row*15+13+1] = errorVector[2] - p[row*15+14+1] = errorVector[3] } - for col := 0; col < 15; col++ { - var cb = make([]bit.Bit, 9) - for row := 0; row < 9; row++ { - cb[row] = p[col+row*15+1] - } - hamming_13_9_3_parity(cb, &errorVector) - p[col+135+1] = errorVector[0] - p[col+135+15+1] = errorVector[1] - p[col+135+30+1] = errorVector[2] - p[col+135+45+1] = errorVector[3] - } + copy(payload, extracted.Bytes()) - return p + return nil } diff --git a/bptc/bptc19696.go b/bptc/bptc19696.go new file mode 100644 index 0000000..61e8697 --- /dev/null +++ b/bptc/bptc19696.go @@ -0,0 +1,133 @@ +package bptc + +import ( + "fmt" + + "github.com/pd0mz/go-dmr/bit" +) + +func Decode(bits bit.Bits, deinterleave bool) (bit.Bits, error) { + var debits bit.Bits + if deinterleave { + debits = Deinterleave(bits) + } else { + debits = bits + } + if err := Check(bits); err != nil { + return nil, err + } + + return Extract(debits), nil +} + +// Deinterleave raw bits +func Deinterleave(r bit.Bits) bit.Bits { + // The first bit is R(3) which is not used so can be ignored + var d = make(bit.Bits, 196) + var i int + for a := 0; a < 196; a++ { + i = (a * 181) % 196 + d[a] = r[i] + } + return d +} + +// Hamming(13, 9, 3) check +func Hamming1393(debits bit.Bits) (bool, bit.Bits) { + var err = make(bit.Bits, 4) + err[0] = debits[0] ^ debits[1] ^ debits[3] ^ debits[5] ^ debits[6] + err[1] = debits[0] ^ debits[1] ^ debits[2] ^ debits[4] ^ debits[6] ^ debits[7] + err[2] = debits[0] ^ debits[1] ^ debits[2] ^ debits[3] ^ debits[5] ^ debits[7] ^ debits[8] + err[3] = debits[0] ^ debits[2] ^ debits[4] ^ debits[5] ^ debits[8] + return (err[0] == debits[9]) && (err[1] == debits[10]) && (err[2] == debits[11]) && (err[3] == debits[12]), err +} + +// Hamming(15, 11, 3) check +func Hamming15113(debits bit.Bits) (bool, bit.Bits) { + var err = make(bit.Bits, 4) + err[0] = debits[0] ^ debits[1] ^ debits[2] ^ debits[3] ^ debits[5] ^ debits[7] ^ debits[8] + err[1] = debits[1] ^ debits[2] ^ debits[3] ^ debits[4] ^ debits[6] ^ debits[8] ^ debits[9] + err[2] = debits[2] ^ debits[3] ^ debits[4] ^ debits[5] ^ debits[7] ^ debits[9] ^ debits[10] + err[3] = debits[0] ^ debits[1] ^ debits[2] ^ debits[4] ^ debits[6] ^ debits[7] ^ debits[10] + return (err[0] == debits[11]) && (err[1] == debits[12]) && (err[2] == debits[13]) && (err[3] == debits[14]), err +} + +// Check each row with a Hamming (15,11,3) code +func Check(debits bit.Bits) error { + var ( + row = make(bit.Bits, 15) + col = make(bit.Bits, 13) + ) + + // Run through each of the 9 rows containing data + for r := 0; r < 9; r++ { + p := (r * 15) + 1 + for a := 0; a < 15; a++ { + row[a] = debits[p] + } + if ok, _ := Hamming15113(row); !ok { + return fmt.Errorf("hamming(15, 11, 3) check failed on row #%d", r) + } + } + + // Run through each of the 15 columns + for c := 0; c < 15; c++ { + p := c + 1 + for a := 0; a < 13; a++ { + col[a] = debits[p] + p += 15 + } + if ok, _ := Hamming1393(col); !ok { + return fmt.Errorf("hamming(13, 9, 3) check failed on col #%d", c) + } + } + + return nil +} + +// Extract the 96 bits of data +func Extract(debits bit.Bits) bit.Bits { + var ( + out = make(bit.Bits, 96) + a, pos int + ) + + for a = 4; a <= 11; a++ { + out[pos] = debits[a] + pos++ + } + for a = 16; a <= 26; a++ { + out[pos] = debits[a] + pos++ + } + for a = 31; a <= 41; a++ { + out[pos] = debits[a] + pos++ + } + for a = 46; a <= 56; a++ { + out[pos] = debits[a] + pos++ + } + for a = 61; a <= 71; a++ { + out[pos] = debits[a] + pos++ + } + for a = 76; a <= 86; a++ { + out[pos] = debits[a] + pos++ + } + for a = 91; a <= 101; a++ { + out[pos] = debits[a] + pos++ + } + for a = 106; a <= 116; a++ { + out[pos] = debits[a] + pos++ + } + for a = 121; a <= 131; a++ { + out[pos] = debits[a] + pos++ + } + + return out +} diff --git a/bptc/bptc_test.go b/bptc/bptc_test.go deleted file mode 100644 index 99878ba..0000000 --- a/bptc/bptc_test.go +++ /dev/null @@ -1,35 +0,0 @@ -package bptc - -import ( - "math/rand" - "pd0mz/go-dmr/bit" -) -import "testing" - -func Test(t *testing.T) { - for i := 0; i < 100; i++ { - test := make(bit.Bits, 96) - for b := 0; b < 96; b++ { - if rand.Uint32() > 0x7fffffff { - test[b].Flip() - } - } - bptc := New(test) - if len(bptc) != 196 { - t.Fatalf("expected 196 bits, got %d", len(bptc)) - } - if testing.Verbose() { - Dump(bptc) - } - corrupt := rand.Intn(10) - bptc[corrupt].Flip() - if ok, err := CheckAndRepair(bptc); !ok { - t.Fatalf("check and repair failed: %v", err) - } - bptc[corrupt].Flip() - back := Extract(bptc) - if len(test) != len(back) { - t.Fatalf("expected %d bits, got %d", len(test), len(back)) - } - } -}