// Package bptc implements the BPTC(196, 96) Block Product Turbo Code package bptc import ( "errors" "fmt" "github.com/pd0mz/go-dmr/bit" ) 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 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] } 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) } } } 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) } } } 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++ } } // +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] } return p }