Path fixes
This commit is contained in:
parent
3603905b6f
commit
a7136447fc
4 changed files with 211 additions and 85 deletions
6
.gitignore
vendored
6
.gitignore
vendored
|
@ -22,3 +22,9 @@ _testmain.go
|
|||
*.exe
|
||||
*.test
|
||||
*.prof
|
||||
|
||||
# Editor remains
|
||||
*.swp
|
||||
|
||||
# Dump files
|
||||
*.dump
|
||||
|
|
|
@ -4,7 +4,8 @@ package bptc
|
|||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"pd0mz/go-dmr/bit"
|
||||
|
||||
"github.com/pd0mz/go-dmr/bit"
|
||||
)
|
||||
|
||||
type vector [4]bit.Bit
|
||||
|
|
86
dmr/message.go
Normal file
86
dmr/message.go
Normal file
|
@ -0,0 +1,86 @@
|
|||
// Package dmr contains generic message structures for the Digital Mobile Radio standard
|
||||
package dmr
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/pd0mz/go-dmr/bit"
|
||||
)
|
||||
|
||||
const (
|
||||
InfoPartBits = 98
|
||||
InfoBits = InfoPartBits * 2
|
||||
SlotPartBits = 10
|
||||
SlotBits = SlotPartBits * 2
|
||||
PayloadPartBits = InfoPartBits + SlotPartBits
|
||||
PayloadBits = PayloadPartBits * 2
|
||||
SignalBits = 48
|
||||
BurstBits = PayloadBits + SignalBits
|
||||
)
|
||||
|
||||
// Table 9.2: SYNC Patterns
|
||||
var SYNCPatterns = map[string]struct {
|
||||
ControlMode string
|
||||
PDU string
|
||||
}{
|
||||
"\x07\x05\x05\x0f\x0d\x07\x0d\x0f\x07\x05\x0f\x07": {"BS sourced", "voice"},
|
||||
"\x0d\x0f\x0f\x05\x07\x0d\x07\x05\x0d\x0f\x05\x0d": {"BS sourced", "data"},
|
||||
"\x07\x0f\x07\x0d\x05\x0d\x0d\x05\x07\x0d\x0f\x0d": {"MS sourced", "voice"},
|
||||
"\x0d\x05\x0d\x07\x0f\x07\x07\x0f\x0d\x07\x05\x07": {"MS sourced", "data"},
|
||||
"\x07\x07\x0d\x05\x05\x0f\x07\x0d\x0f\x0d\x07\x07": {"MS sourced", "rc sync"},
|
||||
"\x05\x0d\x05\x07\x07\x0f\x07\x07\x05\x07\x0f\x0f": {"TDMA direct mode time slot 1", "voice"},
|
||||
"\x0f\x07\x0f\x0d\x0d\x05\x0d\x0d\x0f\x0d\x05\x05": {"TDMA direct mode time slot 1", "data"},
|
||||
"\x07\x0d\x0f\x0f\x0d\x05\x0f\x05\x05\x0d\x05\x0f": {"TDMA direct mode time slot 2", "voice"},
|
||||
"\x0d\x07\x05\x05\x07\x0f\x05\x0f\x0f\x07\x0f\x05": {"TDMA direct mode time slot 2", "data"},
|
||||
"\x0d\x0d\x07\x0f\x0f\x05\x0d\x07\x05\x07\x0d\x0d": {"Reserved SYNC pattern", "reserved"},
|
||||
}
|
||||
|
||||
// Burst contains data from a single burst, see 4.2.2 Burst and frame structure
|
||||
type Burst struct {
|
||||
bits bit.Bits
|
||||
}
|
||||
|
||||
func NewBurst(raw []byte) (*Burst, error) {
|
||||
if len(raw)*8 != BurstBits {
|
||||
return nil, fmt.Errorf("dmr: expected %d bits, got %d", BurstBits, len(raw)*8)
|
||||
}
|
||||
b := &Burst{}
|
||||
b.bits = bit.NewBits(raw)
|
||||
return b, nil
|
||||
}
|
||||
|
||||
// Info returns the 196 bits of info in the burst. The data is usually BPTC(196, 96) encoded.
|
||||
func (b *Burst) Info() bit.Bits {
|
||||
// The info is contained in bits 0..98 and 166..216 for a total of 196 bits
|
||||
var n = make(bit.Bits, InfoBits)
|
||||
copy(n[0:InfoPartBits], b.bits[0:InfoPartBits])
|
||||
copy(n[InfoPartBits:InfoBits], b.bits[InfoPartBits+SignalBits+SlotBits:BurstBits])
|
||||
return n
|
||||
}
|
||||
|
||||
// Payload returns the 216 bits of payload in the burst.
|
||||
func (b *Burst) Payload() bit.Bits {
|
||||
// The payload is contained in bits 0..108 and 156..264 for a total of 216 bits
|
||||
var p = make(bit.Bits, PayloadBits)
|
||||
copy(p[0:PayloadPartBits], b.bits[0:PayloadPartBits])
|
||||
copy(p[PayloadPartBits:PayloadBits], b.bits[PayloadPartBits+SignalBits:BurstBits])
|
||||
return p
|
||||
}
|
||||
|
||||
// Signal returns the 48 bits of signal or SYNC information in the burst.
|
||||
func (b *Burst) Signal() bit.Bits {
|
||||
// The signal bits are contained in bits 108..156 for a total of 48 bits
|
||||
var s = make(bit.Bits, SignalBits)
|
||||
copy(s, b.bits[PayloadPartBits:PayloadPartBits+SignalBits])
|
||||
return s
|
||||
}
|
||||
|
||||
func (b *Burst) SlotType() uint32 {
|
||||
/* The slottype is 20 bits, starting after the payload info */
|
||||
var s uint32
|
||||
for i := InfoPartBits; i < InfoPartBits+SlotBits; i++ {
|
||||
var shift = uint32(20 - (i - InfoPartBits))
|
||||
s = s | uint32(b.bits[i]<<shift)
|
||||
}
|
||||
return s
|
||||
}
|
|
@ -119,6 +119,25 @@ type Frame struct {
|
|||
DMR [33]byte
|
||||
}
|
||||
|
||||
// Bytes packs the frame into bytes ready to send over the network.
|
||||
func (f *Frame) Bytes() []byte {
|
||||
var b = make([]byte, 53)
|
||||
copy(b[:4], f.Signature[:])
|
||||
b[4] = f.Sequence
|
||||
b[5] = byte(f.SrcID >> 16)
|
||||
b[6] = byte(f.SrcID >> 8)
|
||||
b[7] = byte(f.SrcID)
|
||||
b[8] = byte(f.DstID >> 16)
|
||||
b[9] = byte(f.DstID >> 8)
|
||||
b[10] = byte(f.DstID)
|
||||
binary.BigEndian.PutUint32(b[11:15], f.RepeaterID)
|
||||
b[15] = f.Flags
|
||||
binary.BigEndian.PutUint32(b[16:20], f.StreamID)
|
||||
copy(b[21:], f.DMR[:])
|
||||
|
||||
return b
|
||||
}
|
||||
|
||||
// CallType returns if the current frame is a group or unit call.
|
||||
func (f *Frame) CallType() CallType {
|
||||
return CallType((f.Flags >> 1) & 0x01)
|
||||
|
@ -149,8 +168,8 @@ func ParseFrame(data []byte) (*Frame, error) {
|
|||
f := &Frame{}
|
||||
copy(f.Signature[:], data[:4])
|
||||
f.Sequence = data[4]
|
||||
f.SrcID = binary.BigEndian.Uint32(append([]byte{0x00}, data[5:7]...))
|
||||
f.DstID = binary.BigEndian.Uint32(append([]byte{0x00}, data[8:10]...))
|
||||
f.SrcID = (uint32(data[5]) << 16) | (uint32(data[6]) << 8) | uint32(data[7])
|
||||
f.DstID = (uint32(data[8]) << 16) | (uint32(data[9]) << 8) | uint32(data[10])
|
||||
f.RepeaterID = binary.BigEndian.Uint32(data[11:15])
|
||||
f.Flags = data[15]
|
||||
f.StreamID = binary.BigEndian.Uint32(data[16:20])
|
||||
|
@ -179,6 +198,11 @@ type Network struct {
|
|||
MasterID uint32
|
||||
}
|
||||
|
||||
type packet struct {
|
||||
addr *net.UDPAddr
|
||||
data []byte
|
||||
}
|
||||
|
||||
type Link struct {
|
||||
Dump bool
|
||||
config ConfigFunc
|
||||
|
@ -250,7 +274,9 @@ func (l *Link) Run() error {
|
|||
return err
|
||||
}
|
||||
|
||||
queue := make(chan packet)
|
||||
go l.login()
|
||||
go l.parse(queue)
|
||||
|
||||
for {
|
||||
var (
|
||||
|
@ -263,7 +289,7 @@ func (l *Link) Run() error {
|
|||
continue
|
||||
}
|
||||
|
||||
go l.parse(peer, data[:n])
|
||||
queue <- packet{peer, data[:n]}
|
||||
}
|
||||
|
||||
return nil
|
||||
|
@ -304,7 +330,9 @@ func (l *Link) login() {
|
|||
|
||||
case authDone:
|
||||
config := l.config().Bytes()
|
||||
fmt.Printf(hex.Dump(config))
|
||||
if l.Dump {
|
||||
fmt.Printf(hex.Dump(config))
|
||||
}
|
||||
log.Printf("dmr/homebrew: logged in, sending %d bytes of repeater configuration\n", len(config))
|
||||
|
||||
if err := l.Send(l.master.addr, config); err != nil {
|
||||
|
@ -342,88 +370,93 @@ func (l *Link) keepAlive() {
|
|||
}
|
||||
}
|
||||
|
||||
func (l *Link) parse(addr *net.UDPAddr, data []byte) {
|
||||
size := len(data)
|
||||
|
||||
switch l.master.status {
|
||||
case authNone:
|
||||
if bytes.Equal(data, DMRData) {
|
||||
return
|
||||
}
|
||||
if size < 14 {
|
||||
return
|
||||
}
|
||||
packet := data[:6]
|
||||
repeater, err := hex.DecodeString(string(data[6:14]))
|
||||
if err != nil {
|
||||
log.Println("dmr/homebrew: unexpected login reply from master")
|
||||
l.master.status = authFail
|
||||
break
|
||||
}
|
||||
|
||||
switch {
|
||||
case bytes.Equal(packet, MasterNAK):
|
||||
log.Printf("dmr/homebrew: login refused by master %d\n", repeater)
|
||||
l.master.status = authFail
|
||||
break
|
||||
case bytes.Equal(packet, MasterACK):
|
||||
log.Printf("dmr/homebrew: login accepted by master %d\n", repeater)
|
||||
l.master.secret = data[14:]
|
||||
l.master.status = authBegin
|
||||
break
|
||||
default:
|
||||
log.Printf("dmr/homebrew: unexpected login reply from master %d\n", repeater)
|
||||
l.master.status = authFail
|
||||
break
|
||||
}
|
||||
|
||||
case authBegin:
|
||||
if bytes.Equal(data, DMRData) {
|
||||
return
|
||||
}
|
||||
if size < 14 {
|
||||
log.Println("dmr/homebrew: unexpected login reply from master")
|
||||
l.master.status = authFail
|
||||
break
|
||||
}
|
||||
packet := data[:6]
|
||||
repeater, err := hex.DecodeString(string(data[6:14]))
|
||||
if err != nil {
|
||||
log.Println("dmr/homebrew: unexpected login reply from master")
|
||||
l.master.status = authFail
|
||||
break
|
||||
}
|
||||
|
||||
switch {
|
||||
case bytes.Equal(packet, MasterNAK):
|
||||
log.Printf("dmr/homebrew: authentication refused by master %d\n", repeater)
|
||||
l.master.status = authFail
|
||||
break
|
||||
case bytes.Equal(packet, MasterACK):
|
||||
log.Printf("dmr/homebrew: authentication accepted by master %d\n", repeater)
|
||||
l.master.status = authDone
|
||||
break
|
||||
default:
|
||||
log.Printf("dmr/homebrew: unexpected authentication reply from master %d\n", repeater)
|
||||
l.master.status = authFail
|
||||
break
|
||||
}
|
||||
|
||||
case authDone:
|
||||
if len(data) < 4 {
|
||||
return
|
||||
}
|
||||
switch {
|
||||
case bytes.Equal(data[:4], DMRData):
|
||||
if l.stream == nil {
|
||||
return
|
||||
func (l *Link) parse(queue <-chan packet) {
|
||||
for {
|
||||
select {
|
||||
case p := <-queue:
|
||||
size := len(p.data)
|
||||
if size < 4 {
|
||||
continue
|
||||
}
|
||||
frame, err := ParseFrame(data)
|
||||
if err != nil {
|
||||
log.Printf("error parsing DMR data: %v\n", err)
|
||||
return
|
||||
|
||||
switch l.master.status {
|
||||
case authNone:
|
||||
if bytes.Equal(p.data[:4], DMRData) {
|
||||
return
|
||||
}
|
||||
if size < 14 {
|
||||
return
|
||||
}
|
||||
packet := p.data[:6]
|
||||
repeater, err := hex.DecodeString(string(p.data[6:14]))
|
||||
if err != nil {
|
||||
log.Println("dmr/homebrew: unexpected login reply from master")
|
||||
l.master.status = authFail
|
||||
break
|
||||
}
|
||||
|
||||
switch {
|
||||
case bytes.Equal(packet, MasterNAK):
|
||||
log.Printf("dmr/homebrew: login refused by master %d\n", repeater)
|
||||
l.master.status = authFail
|
||||
break
|
||||
case bytes.Equal(packet, MasterACK):
|
||||
log.Printf("dmr/homebrew: login accepted by master %d\n", repeater)
|
||||
l.master.secret = p.data[14:]
|
||||
l.master.status = authBegin
|
||||
break
|
||||
default:
|
||||
log.Printf("dmr/homebrew: unexpected login reply from master %d\n", repeater)
|
||||
l.master.status = authFail
|
||||
break
|
||||
}
|
||||
|
||||
case authBegin:
|
||||
if bytes.Equal(p.data[:4], DMRData) {
|
||||
return
|
||||
}
|
||||
if size < 14 {
|
||||
log.Println("dmr/homebrew: unexpected login reply from master")
|
||||
l.master.status = authFail
|
||||
break
|
||||
}
|
||||
packet := p.data[:6]
|
||||
repeater, err := hex.DecodeString(string(p.data[6:14]))
|
||||
if err != nil {
|
||||
log.Println("dmr/homebrew: unexpected login reply from master")
|
||||
l.master.status = authFail
|
||||
break
|
||||
}
|
||||
|
||||
switch {
|
||||
case bytes.Equal(packet, MasterNAK):
|
||||
log.Printf("dmr/homebrew: authentication refused by master %d\n", repeater)
|
||||
l.master.status = authFail
|
||||
break
|
||||
case bytes.Equal(packet, MasterACK):
|
||||
log.Printf("dmr/homebrew: authentication accepted by master %d\n", repeater)
|
||||
l.master.status = authDone
|
||||
break
|
||||
default:
|
||||
log.Printf("dmr/homebrew: unexpected authentication reply from master %d\n", repeater)
|
||||
l.master.status = authFail
|
||||
break
|
||||
}
|
||||
|
||||
case authDone:
|
||||
switch {
|
||||
case bytes.Equal(p.data[:4], DMRData):
|
||||
if l.stream == nil {
|
||||
return
|
||||
}
|
||||
frame, err := ParseFrame(p.data)
|
||||
if err != nil {
|
||||
log.Printf("error parsing DMR data: %v\n", err)
|
||||
return
|
||||
}
|
||||
l.stream(frame)
|
||||
}
|
||||
}
|
||||
l.stream(frame)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Add table
Reference in a new issue