Added the BPTC(196, 96) algo
parent
c67f84086b
commit
f4c8a1570d
@ -0,0 +1,287 @@
|
||||
// Package bptc implements the BPTC(196, 96) Block Product Turbo Code
|
||||
package bptc
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
type Bit byte
|
||||
|
||||
type vector [4]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, 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, 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, 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, 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{
|
||||
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{
|
||||
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) {
|
||||
if len(bits) != 196 {
|
||||
return
|
||||
}
|
||||
|
||||
var row, col int
|
||||
|
||||
fmt.Println(" BPTC(196, 96) matrix:")
|
||||
for row = 0; row < 13; 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(" #%.2u ", 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("%u", bits[col+row*15+1])
|
||||
}
|
||||
fmt.Println("")
|
||||
if row == 8 {
|
||||
fmt.Println("")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func CheckAndRepair(bits []Bit) (bool, error) {
|
||||
if bits == nil || len(bits) != 196 {
|
||||
return false, errors.New("expected 196 input bits")
|
||||
}
|
||||
|
||||
var (
|
||||
cb = make([]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]
|
||||
}
|
||||
|
||||
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 #%u, 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 #%u, couldn't repair", col)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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 #%u, 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 #%u", row)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true, nil
|
||||
}
|
||||
|
||||
// Extract the data bits from the given deinterleaved info bits array (discards BPTC bits).
|
||||
func Extract(bits []Bit) []Bit {
|
||||
var e = make([]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) []Bit {
|
||||
var (
|
||||
dbp int
|
||||
errorVector = vector{}
|
||||
p = make([]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++
|
||||
}
|
||||
}
|
||||
// +1 because the first bit is R(3) and it's not used so we can ignore that.
|
||||
hamming_15_11_3_parity(bits[row*15+1:], &errorVector)
|
||||
bits[row*15+11+1] = errorVector[0]
|
||||
bits[row*15+12+1] = errorVector[1]
|
||||
bits[row*15+13+1] = errorVector[2]
|
||||
bits[row*15+14+1] = errorVector[3]
|
||||
}
|
||||
|
||||
for col := 0; col < 15; col++ {
|
||||
var cb = make([]Bit, 9)
|
||||
for row := 0; row < 9; row++ {
|
||||
cb[row] = bits[col+row*15+1]
|
||||
}
|
||||
hamming_13_9_3_parity(cb, &errorVector)
|
||||
bits[col+135+1] = errorVector[0]
|
||||
bits[col+135+15+1] = errorVector[1]
|
||||
bits[col+135+30+1] = errorVector[2]
|
||||
bits[col+135+45+1] = errorVector[3]
|
||||
}
|
||||
|
||||
return p
|
||||
}
|
Loading…
Reference in New Issue