bptc: simplified

pull/1/head
Wijnand Modderman-Lenstra 9 years ago
parent 1f073cf902
commit b7c07ac7b3

@ -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
}
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
}
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
}
func hamming_13_9_3_parity(data bit.Bits, errorVector *vector) {
if data == nil || len(data) < 9 || errorVector == nil {
return
}
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
}
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
}
}
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
}
}
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 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))
}
}
func CheckAndRepair(bits bit.Bits) (bool, error) {
if bits == nil || len(bits) != 196 {
return false, errors.New("expected 196 input bits")
if len(payload) < 12 {
return fmt.Errorf("bptc: payload size %d too small, need at least 12 bytes", len(payload))
}
var (
cb = make([]bit.Bit, 13)
errorVector = vector{}
i, j, k uint32
datafr = make(bit.Bits, 196)
extracted = make(bit.Bits, 96)
)
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]
}
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)
// Deinterleave bits
for i = 1; i < 197; i++ {
datafr[i-1] = info[((i * 181) % 196)]
}
// 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]
// Zero reserved bits
for i = 0; i < 3; i++ {
datafr[0*15+i] = 0
}
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)
}
}
for i = 0; i < 15; i++ {
var codeword uint32
for j = 0; j < 13; j++ {
codeword <<= 1
codeword |= uint32(datafr[j*15+i])
}
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)
fec.Hamming15_11_3_Correct(&codeword)
codeword &= 0x01ff
for j = 0; j < 9; j++ {
datafr[j*15+i] = bit.Bit((codeword >> (8 - j)) & 1)
}
// 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)
}
for j = 0; j < 9; j++ {
var codeword uint32
for i = 0; i < 15; i++ {
codeword <<= 1
codeword |= uint32(datafr[j*15+i])
}
fec.Hamming15_11_3_Correct(&codeword)
for i = 0; i < 11; i++ {
datafr[j*15+10-i] = bit.Bit((codeword >> i) & 1)
}
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
// Extract data bits
for i, k = 3, 0; i < 11; i, k = i+1, k+1 {
extracted[k] = datafr[0*15+i]
}
// 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
}

@ -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
}

@ -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))
}
}
}
Loading…
Cancel
Save