From 599d42dca94412a156c14f66d4a56df842876bd9 Mon Sep 17 00:00:00 2001 From: Wijnand Modderman-Lenstra Date: Fri, 18 Dec 2015 22:16:21 +0100 Subject: [PATCH] bptc: rewrite; fixed encoder; added tests --- bptc/bptc.go | 205 ++++++++++++++++++++++++++++++++++-------- bptc/bptc_test.go | 56 ++++++++++++ bptc/testdata/bptc.go | 165 ++++++++++++++++++++++++++++++++++ 3 files changed, 389 insertions(+), 37 deletions(-) create mode 100644 bptc/bptc_test.go create mode 100644 bptc/testdata/bptc.go diff --git a/bptc/bptc.go b/bptc/bptc.go index 4d5d32e..77a47be 100644 --- a/bptc/bptc.go +++ b/bptc/bptc.go @@ -2,71 +2,202 @@ package bptc import ( "fmt" + "os" "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 { return fmt.Errorf("bptc: info size %d too small, need at least 196 bits", len(info)) } - if len(payload) < 12 { - return fmt.Errorf("bptc: payload size %d too small, need at least 12 bytes", len(payload)) + 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) + i, j, k uint32 + bits = make([]byte, 196) + temp = make([]byte, 196) ) - // Deinterleave bits + // Deinterleave for i = 1; i < 197; i++ { - datafr[i-1] = info[((i * 181) % 196)] + bits[i-1] = info[dm[i]] } - // Zero reserved bits - for i = 0; i < 3; i++ { - datafr[0*15+i] = 0 + if debug { + dump(bits) } - for i = 0; i < 15; i++ { - var codeword uint32 - for j = 0; j < 13; j++ { - codeword <<= 1 - codeword |= uint32(datafr[j*15+i]) - } + // Hamming checks + if err := hamming_check(bits); err != nil { + return err + } - fec.Hamming15_11_3_Correct(&codeword) - codeword &= 0x01ff - for j = 0; j < 9; j++ { - datafr[j*15+i] = byte((codeword >> (8 - j)) & 1) + // Extract data bits + for i, k = 3, 0; i < 11; i, k = i+1, k+1 { + temp[k] = bits[0*15+i] + } + for j = 1; j < 9; j++ { + for i = 0; i < 11; i, k = i+1, k+1 { + temp[k] = bits[j*15+i] } } - for j = 0; j < 9; j++ { - var codeword uint32 - for i = 0; i < 15; i++ { - codeword <<= 1 - codeword |= uint32(datafr[j*15+i]) + + 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] + } } - fec.Hamming15_11_3_Correct(&codeword) - for i = 0; i < 11; i++ { - datafr[j*15+10-i] = byte((codeword >> i) & 1) + + 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] } - // Extract data bits - for i, k = 3, 0; i < 11; i, k = i+1, k+1 { - extracted[k] = datafr[0*15+i] + if debug { + dump(temp) } - for j = 1; j < 9; j++ { - for i = 0; i < 11; i, k = i+1, k+1 { - extracted[k] = datafr[j*15+i] + + // 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) } } - copy(payload, dmr.BitsToBytes(extracted)) + // 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 } diff --git a/bptc/bptc_test.go b/bptc/bptc_test.go new file mode 100644 index 0000000..88be3ae --- /dev/null +++ b/bptc/bptc_test.go @@ -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)) +} diff --git a/bptc/testdata/bptc.go b/bptc/testdata/bptc.go new file mode 100644 index 0000000..ed2d460 --- /dev/null +++ b/bptc/testdata/bptc.go @@ -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 +}