bptc: rewrite; fixed encoder; added tests
This commit is contained in:
parent
500a97e3f3
commit
599d42dca9
3 changed files with 390 additions and 38 deletions
207
bptc/bptc.go
207
bptc/bptc.go
|
@ -2,71 +2,202 @@ package bptc
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"os"
|
||||||
|
|
||||||
"github.com/pd0mz/go-dmr"
|
"github.com/pd0mz/go-dmr"
|
||||||
"github.com/pd0mz/go-dmr/fec"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func Decode(info []byte, payload []byte) error {
|
var (
|
||||||
|
debug bool
|
||||||
|
|
||||||
|
// deinterleave matrix
|
||||||
|
dm = [256]uint8{}
|
||||||
|
)
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
debug = os.Getenv("DEBUG_DMR_BPTC") != ""
|
||||||
|
|
||||||
|
var i uint32
|
||||||
|
for i = 0; i < 0x100; i++ {
|
||||||
|
dm[i] = uint8((i * 181) % 196)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func dump(bits []byte) {
|
||||||
|
for row := 0; row < 13; row++ {
|
||||||
|
if row == 0 {
|
||||||
|
fmt.Printf("col # ")
|
||||||
|
for col := 0; col < 15; col++ {
|
||||||
|
fmt.Printf("%02d ", col+1)
|
||||||
|
if col == 10 {
|
||||||
|
fmt.Print("| ")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fmt.Println("")
|
||||||
|
}
|
||||||
|
if row == 9 {
|
||||||
|
fmt.Println(" ------------------------------- ------------")
|
||||||
|
}
|
||||||
|
for col := 0; col < 15; col++ {
|
||||||
|
if col == 0 {
|
||||||
|
fmt.Printf("row #%02d: ", row+1)
|
||||||
|
}
|
||||||
|
fmt.Printf(" %d ", bits[col+row*15+1])
|
||||||
|
if col == 10 {
|
||||||
|
fmt.Print("| ")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fmt.Println("")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func Decode(info, data []byte) error {
|
||||||
if len(info) < 196 {
|
if len(info) < 196 {
|
||||||
return fmt.Errorf("bptc: info size %d too small, need at least 196 bits", len(info))
|
return fmt.Errorf("bptc: info size %d too small, need at least 196 bits", len(info))
|
||||||
}
|
}
|
||||||
if len(payload) < 12 {
|
if len(data) < 12 {
|
||||||
return fmt.Errorf("bptc: payload size %d too small, need at least 12 bytes", len(payload))
|
return fmt.Errorf("bptc: data size %d too small, need at least 12 bytes", len(data))
|
||||||
}
|
}
|
||||||
|
|
||||||
var (
|
var (
|
||||||
i, j, k uint32
|
i, j, k uint32
|
||||||
datafr = make([]byte, 196)
|
bits = make([]byte, 196)
|
||||||
extracted = make([]byte, 96)
|
temp = make([]byte, 196)
|
||||||
)
|
)
|
||||||
|
|
||||||
// Deinterleave bits
|
// Deinterleave
|
||||||
for i = 1; i < 197; i++ {
|
for i = 1; i < 197; i++ {
|
||||||
datafr[i-1] = info[((i * 181) % 196)]
|
bits[i-1] = info[dm[i]]
|
||||||
}
|
}
|
||||||
|
|
||||||
// Zero reserved bits
|
if debug {
|
||||||
for i = 0; i < 3; i++ {
|
dump(bits)
|
||||||
datafr[0*15+i] = 0
|
|
||||||
}
|
}
|
||||||
|
|
||||||
for i = 0; i < 15; i++ {
|
// Hamming checks
|
||||||
var codeword uint32
|
if err := hamming_check(bits); err != nil {
|
||||||
for j = 0; j < 13; j++ {
|
return err
|
||||||
codeword <<= 1
|
|
||||||
codeword |= uint32(datafr[j*15+i])
|
|
||||||
}
|
|
||||||
|
|
||||||
fec.Hamming15_11_3_Correct(&codeword)
|
|
||||||
codeword &= 0x01ff
|
|
||||||
for j = 0; j < 9; j++ {
|
|
||||||
datafr[j*15+i] = byte((codeword >> (8 - j)) & 1)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
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] = byte((codeword >> i) & 1)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Extract data bits
|
// Extract data bits
|
||||||
for i, k = 3, 0; i < 11; i, k = i+1, k+1 {
|
for i, k = 3, 0; i < 11; i, k = i+1, k+1 {
|
||||||
extracted[k] = datafr[0*15+i]
|
temp[k] = bits[0*15+i]
|
||||||
}
|
}
|
||||||
for j = 1; j < 9; j++ {
|
for j = 1; j < 9; j++ {
|
||||||
for i = 0; i < 11; i, k = i+1, k+1 {
|
for i = 0; i < 11; i, k = i+1, k+1 {
|
||||||
extracted[k] = datafr[j*15+i]
|
temp[k] = bits[j*15+i]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
copy(payload, dmr.BitsToBytes(extracted))
|
copy(data, dmr.BitsToBytes(temp))
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func Encode(data, info []byte) error {
|
||||||
|
if len(data) < 12 {
|
||||||
|
return fmt.Errorf("bptc: data size %d too small, need at least 12 bytes", len(data))
|
||||||
|
}
|
||||||
|
if len(info) < 196 {
|
||||||
|
return fmt.Errorf("bptc: info size %d too small, need at least 196 bits", len(info))
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
bits = dmr.BytesToBits(data)
|
||||||
|
temp = make([]byte, 196)
|
||||||
|
errs = make([]byte, 4)
|
||||||
|
cols = make([]byte, 13)
|
||||||
|
)
|
||||||
|
|
||||||
|
var c, r, k uint32
|
||||||
|
for r = 0; r < 9; r++ {
|
||||||
|
if r == 0 {
|
||||||
|
for c = 3; c < 11; c, k = c+1, k+1 {
|
||||||
|
temp[c] = bits[k]
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
for c = 0; c < 11; c, k = c+1, k+1 {
|
||||||
|
temp[c+r*15] = bits[k]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
hamming_15_11_3_parity(temp[r*15:], errs)
|
||||||
|
temp[r*15+11] = errs[0]
|
||||||
|
temp[r*15+12] = errs[1]
|
||||||
|
temp[r*15+13] = errs[2]
|
||||||
|
temp[r*15+14] = errs[3]
|
||||||
|
}
|
||||||
|
for c = 0; c < 15; c++ {
|
||||||
|
for r = 0; r < 9; r++ {
|
||||||
|
cols[r] = temp[c+r*15+1]
|
||||||
|
}
|
||||||
|
|
||||||
|
hamming_13_9_3_parity(cols, errs)
|
||||||
|
temp[c+135+1] = errs[0]
|
||||||
|
temp[c+135+15+1] = errs[1]
|
||||||
|
temp[c+135+30+1] = errs[2]
|
||||||
|
temp[c+135+45+1] = errs[3]
|
||||||
|
}
|
||||||
|
|
||||||
|
if debug {
|
||||||
|
dump(temp)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Interleave
|
||||||
|
for k = 1; k < 197; k++ {
|
||||||
|
info[dm[k]] = temp[k-1]
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Hamming(13, 9, 3) check
|
||||||
|
func hamming_13_9_3_parity(bits, errs []byte) bool {
|
||||||
|
errs[0] = bits[0] ^ bits[1] ^ bits[3] ^ bits[5] ^ bits[6]
|
||||||
|
errs[1] = bits[0] ^ bits[1] ^ bits[2] ^ bits[4] ^ bits[6] ^ bits[7]
|
||||||
|
errs[2] = bits[0] ^ bits[1] ^ bits[2] ^ bits[3] ^ bits[5] ^ bits[7] ^ bits[8]
|
||||||
|
errs[3] = bits[0] ^ bits[2] ^ bits[4] ^ bits[5] ^ bits[8]
|
||||||
|
return (errs[0] == bits[9]) && (errs[1] == bits[10]) && (errs[2] == bits[11]) && (errs[3] == bits[12])
|
||||||
|
}
|
||||||
|
|
||||||
|
// Hamming(15, 11, 3) check
|
||||||
|
func hamming_15_11_3_parity(bits, errs []byte) bool {
|
||||||
|
errs[0] = bits[0] ^ bits[1] ^ bits[2] ^ bits[3] ^ bits[5] ^ bits[7] ^ bits[8]
|
||||||
|
errs[1] = bits[1] ^ bits[2] ^ bits[3] ^ bits[4] ^ bits[6] ^ bits[8] ^ bits[9]
|
||||||
|
errs[2] = bits[2] ^ bits[3] ^ bits[4] ^ bits[5] ^ bits[7] ^ bits[9] ^ bits[10]
|
||||||
|
errs[3] = bits[0] ^ bits[1] ^ bits[2] ^ bits[4] ^ bits[6] ^ bits[7] ^ bits[10]
|
||||||
|
return (errs[0] == bits[11]) && (errs[1] == bits[12]) && (errs[2] == bits[13]) && (errs[3] == bits[14])
|
||||||
|
}
|
||||||
|
|
||||||
|
// hamming_check checks each row with a Hamming(15,11,3) code and each column with Hamming(13, 9, 3)
|
||||||
|
func hamming_check(bits []byte) error {
|
||||||
|
var (
|
||||||
|
c, r, k uint32
|
||||||
|
row = make([]byte, 15)
|
||||||
|
col = make([]byte, 13)
|
||||||
|
errs = make([]byte, 4)
|
||||||
|
)
|
||||||
|
|
||||||
|
// Run through each of the 9 rows containing data
|
||||||
|
for r = 0; r < 9; r++ {
|
||||||
|
k = r*15 + 1
|
||||||
|
for a := 0; a < 15; a++ {
|
||||||
|
row[a] = bits[k]
|
||||||
|
}
|
||||||
|
if !hamming_15_11_3_parity(row, errs) {
|
||||||
|
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++ {
|
||||||
|
k = c + 1
|
||||||
|
for a := 0; a < 13; a, k = a+1, k+15 {
|
||||||
|
col[a] = bits[k]
|
||||||
|
}
|
||||||
|
if !hamming_13_9_3_parity(col, errs) {
|
||||||
|
return fmt.Errorf("hamming(13, 9, 3) check failed on col #%d", c)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
56
bptc/bptc_test.go
Normal file
56
bptc/bptc_test.go
Normal file
|
@ -0,0 +1,56 @@
|
||||||
|
package bptc
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"encoding/hex"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/pd0mz/go-dmr"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
encoded = []byte{
|
||||||
|
0x4b, 0xb2, 0x1d, 0x6d, 0x82, 0xd4,
|
||||||
|
0x23, 0x34, 0x0e, 0xe9, 0x66, 0xf3,
|
||||||
|
0xc2, 0x20, 0xc3, 0x87, 0xfd, 0x84,
|
||||||
|
0x54, 0x12, 0x4d, 0xb2, 0xd1, 0x40,
|
||||||
|
0x70,
|
||||||
|
}
|
||||||
|
decoded = []byte{
|
||||||
|
0xbd, 0x00, 0x80, 0x03, 0x1f, 0x29,
|
||||||
|
0x66, 0x1f, 0x2c, 0xa4, 0x66, 0x7e,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestDecode(t *testing.T) {
|
||||||
|
var want = dmr.BytesToBits(encoded)
|
||||||
|
var test = make([]byte, 12)
|
||||||
|
|
||||||
|
if err := Decode(want, test); err != nil {
|
||||||
|
t.Fatalf("decode failed: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if !bytes.Equal(test, decoded) {
|
||||||
|
t.Fatalf("decode failed: not equal")
|
||||||
|
}
|
||||||
|
t.Logf("input:\n%s", hex.Dump(encoded))
|
||||||
|
t.Logf("decoded:\n%s", hex.Dump(test))
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestEncode(t *testing.T) {
|
||||||
|
var want = make([]byte, 12)
|
||||||
|
var bits = make([]byte, 196)
|
||||||
|
copy(want, decoded)
|
||||||
|
|
||||||
|
if err := Encode(want, bits); err != nil {
|
||||||
|
t.Fatalf("encode failed: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
test := dmr.BitsToBytes(bits)
|
||||||
|
if !bytes.Equal(test, encoded) {
|
||||||
|
t.Fatalf("encode failed: not equal")
|
||||||
|
}
|
||||||
|
|
||||||
|
t.Logf("input:\n%s", hex.Dump(decoded))
|
||||||
|
t.Logf("encoded:\n%s", hex.Dump(test))
|
||||||
|
}
|
165
bptc/testdata/bptc.go
vendored
Normal file
165
bptc/testdata/bptc.go
vendored
Normal file
|
@ -0,0 +1,165 @@
|
||||||
|
package bptc
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
|
||||||
|
"github.com/pd0mz/go-dmr"
|
||||||
|
"github.com/pd0mz/go-dmr/fec"
|
||||||
|
)
|
||||||
|
|
||||||
|
// deinterleave matrix
|
||||||
|
var dm = [256]uint8{}
|
||||||
|
var debug bool
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
var i uint32
|
||||||
|
for i = 0; i < 0x100; i++ {
|
||||||
|
dm[i] = uint8((i * 181) % 196)
|
||||||
|
}
|
||||||
|
|
||||||
|
debug = os.Getenv("DEBUG_DMR_BPTC") != ""
|
||||||
|
}
|
||||||
|
|
||||||
|
func dump(bits []byte) {
|
||||||
|
for row := 0; row < 13; row++ {
|
||||||
|
if row == 0 {
|
||||||
|
fmt.Printf("col # ")
|
||||||
|
for col := 0; col < 15; col++ {
|
||||||
|
fmt.Printf("%02d ", col+1)
|
||||||
|
if col == 10 {
|
||||||
|
fmt.Print("| ")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fmt.Println("")
|
||||||
|
}
|
||||||
|
if row == 9 {
|
||||||
|
fmt.Println(" ------------------------------- ------------")
|
||||||
|
}
|
||||||
|
for col := 0; col < 15; col++ {
|
||||||
|
if col == 0 {
|
||||||
|
fmt.Printf("row #%02d: ", row+1)
|
||||||
|
}
|
||||||
|
fmt.Printf(" %d ", bits[col+row*15+1])
|
||||||
|
if col == 10 {
|
||||||
|
fmt.Print("| ")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fmt.Println("")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func Decode(info, data []byte) error {
|
||||||
|
if len(info) < 196 {
|
||||||
|
return fmt.Errorf("bptc: info size %d too small, need at least 196 bits", len(info))
|
||||||
|
}
|
||||||
|
if len(data) < 12 {
|
||||||
|
return fmt.Errorf("bptc: data size %d too small, need at least 12 bytes", len(data))
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
i, j, k uint32
|
||||||
|
datafr = make([]byte, 196)
|
||||||
|
extracted = make([]byte, 96)
|
||||||
|
)
|
||||||
|
|
||||||
|
// Deinterleave bits
|
||||||
|
for i = 1; i < 197; i++ {
|
||||||
|
datafr[i-1] = info[dm[i]]
|
||||||
|
}
|
||||||
|
|
||||||
|
if debug {
|
||||||
|
dump(datafr)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Zero reserved bits
|
||||||
|
for i = 0; i < 3; i++ {
|
||||||
|
datafr[0*15+i] = 0
|
||||||
|
}
|
||||||
|
|
||||||
|
for i = 0; i < 15; i++ {
|
||||||
|
var codeword uint32
|
||||||
|
for j = 0; j < 13; j++ {
|
||||||
|
codeword <<= 1
|
||||||
|
codeword |= uint32(datafr[j*15+i])
|
||||||
|
}
|
||||||
|
|
||||||
|
fec.Hamming15_11_3_Correct(&codeword)
|
||||||
|
codeword &= 0x01ff
|
||||||
|
for j = 0; j < 9; j++ {
|
||||||
|
datafr[j*15+i] = byte((codeword >> (8 - j)) & 1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
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] = byte((codeword >> i) & 1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Extract data bits
|
||||||
|
for i, k = 3, 0; i < 11; i, k = i+1, k+1 {
|
||||||
|
extracted[k] = datafr[0*15+i]
|
||||||
|
}
|
||||||
|
for j = 1; j < 9; j++ {
|
||||||
|
for i = 0; i < 11; i, k = i+1, k+1 {
|
||||||
|
extracted[k] = datafr[j*15+i]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
copy(data, dmr.BitsToBytes(extracted))
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func Encode(data, info []byte) error {
|
||||||
|
if len(data) < 12 {
|
||||||
|
return fmt.Errorf("bptc: data size %d too small, need at least 12 bytes", len(data))
|
||||||
|
}
|
||||||
|
if len(info) < 196 {
|
||||||
|
return fmt.Errorf("bptc: info size %d too small, need at least 196 bits", len(info))
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
i, j, k uint32
|
||||||
|
datafr = make([]byte, 196)
|
||||||
|
extracted = make([]byte, 96)
|
||||||
|
)
|
||||||
|
|
||||||
|
copy(extracted, dmr.BytesToBits(data))
|
||||||
|
|
||||||
|
for i = 0; i < 9; i++ {
|
||||||
|
if i == 0 {
|
||||||
|
for j = 3; j < 11; j++ {
|
||||||
|
datafr[j+1] = extracted[k]
|
||||||
|
k++
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
for j = 0; j < 11; j++ {
|
||||||
|
datafr[j+i*15+1] = extracted[k]
|
||||||
|
k++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
datafr[i*15+11+1] = 8
|
||||||
|
datafr[i*15+12+1] = 8
|
||||||
|
datafr[i*15+13+1] = 8
|
||||||
|
datafr[i*15+14+1] = 8
|
||||||
|
}
|
||||||
|
|
||||||
|
// Interleave bits
|
||||||
|
for i = 1; i < 197; i++ {
|
||||||
|
info[dm[i]] = datafr[i-1]
|
||||||
|
}
|
||||||
|
|
||||||
|
if debug {
|
||||||
|
dump(info)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
Loading…
Add table
Reference in a new issue