pull/1/head
Wijnand Modderman-Lenstra 9 years ago
parent b7c07ac7b3
commit f9646e9667

@ -0,0 +1,65 @@
package homebrew
import (
"fmt"
"github.com/pd0mz/go-dmr/bit"
"github.com/pd0mz/go-dmr/ipsc"
)
// ParseData reads a raw DMR data frame from the homebrew protocol and parses it as it were an IPSC packet.
func ParseData(data []byte) (*ipsc.Packet, error) {
if len(data) != 53 {
return nil, fmt.Errorf("invalid packet length %d, expected 53 bytes", len(data))
}
var (
slotType uint16
dataType = (data[15] & bit.B11110000) >> 4
frameType = (data[15] & bit.B00001100) >> 2
)
switch frameType {
case bit.B00000000, bit.B00000001: // voice/voice sync
switch dataType {
case bit.B00000000:
slotType = ipsc.VoiceDataA
case bit.B00000001:
slotType = ipsc.VoiceDataB
case bit.B00000010:
slotType = ipsc.VoiceDataC
case bit.B00000011:
slotType = ipsc.VoiceDataD
case bit.B00000100:
slotType = ipsc.VoiceDataE
case bit.B00000101:
slotType = ipsc.VoiceDataF
}
case bit.B00000010: // data sync
switch dataType {
case bit.B00000001:
slotType = ipsc.VoiceLCHeader
case bit.B00000010:
slotType = ipsc.TerminatorWithLC
case bit.B00000011:
slotType = ipsc.CSBK
case bit.B00000110:
slotType = ipsc.DataHeader
case bit.B00000111:
slotType = ipsc.Rate12Data
case bit.B00001000:
slotType = ipsc.Rate34Data
}
}
return &ipsc.Packet{
Timeslot: (data[15] & bit.B00000001),
CallType: (data[15] & bit.B00000010) >> 1,
FrameType: (data[15] & bit.B00001100) >> 2,
SlotType: slotType,
SrcID: uint32(data[5])<<16 | uint32(data[6])<<8 | uint32(data[7]),
DstID: uint32(data[8])<<16 | uint32(data[9])<<8 | uint32(data[10]),
Payload: data[20:],
PayloadBits: bit.NewBits(data[20:]),
Sequence: data[4],
}, nil
}

@ -4,7 +4,6 @@ package homebrew
import ( import (
"bytes" "bytes"
"crypto/sha256" "crypto/sha256"
"encoding/binary"
"encoding/hex" "encoding/hex"
"errors" "errors"
"fmt" "fmt"
@ -14,6 +13,8 @@ import (
"strings" "strings"
"sync/atomic" "sync/atomic"
"time" "time"
"github.com/pd0mz/go-dmr/ipsc"
) )
var ( var (
@ -97,89 +98,8 @@ const (
UnitCall UnitCall
) )
// FrameType reflects the DMR data frame type.
type FrameType byte
const (
Voice FrameType = iota
VoiceSync
DataSync
UnusedFrameType
)
// Frame is a frame of DMR data.
type Frame struct {
Signature [4]byte
Sequence byte
SrcID uint32
DstID uint32
RepeaterID uint32
Flags byte
StreamID uint32
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)
}
// DataType is the slot type when the FrameType is a data sync frame. For voice
// frames it's the voice sequence number in DMR numbering, where 0=A, 1=B, etc.
func (f *Frame) DataType() byte {
return f.Flags >> 4
}
// FrameType returns if the current frame is a voice, voice sync or data sync frame.
func (f *Frame) FrameType() FrameType {
return FrameType((f.Flags >> 2) & 0x03)
}
// Slot is the time slot number.
func (f *Frame) Slot() int {
return int(f.Flags&0x01) + 1
}
// ParseFrame parses a raw DMR data frame.
func ParseFrame(data []byte) (*Frame, error) {
if len(data) != 53 {
return nil, errors.New("invalid packet length")
}
f := &Frame{}
copy(f.Signature[:], data[:4])
f.Sequence = data[4]
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])
copy(f.DMR[:], data[20:])
return f, nil
}
// StreamFunc is called by the DMR repeater when a DMR data frame is received. // StreamFunc is called by the DMR repeater when a DMR data frame is received.
type StreamFunc func(*Frame) type StreamFunc func(*ipsc.Packet)
type authStatus byte type authStatus byte
@ -444,19 +364,32 @@ func (l *Link) parse(queue <-chan packet) {
} }
case authDone: case authDone:
if l.Dump {
fmt.Printf("received from %s:\n", p.addr)
fmt.Print(hex.Dump(p.data))
}
switch { switch {
case bytes.Equal(p.data[:4], DMRData): case bytes.Equal(p.data[:4], DMRData):
if l.stream == nil { l.parseDMR(p.data)
return
}
frame, err := ParseFrame(p.data)
if err != nil {
log.Printf("error parsing DMR data: %v\n", err)
return
}
l.stream(frame)
} }
} }
} }
} }
} }
func (l *Link) parseDMR(d []byte) {
// If we have no stream callback, don't even bother to decode the DMR data frame.
if l.stream == nil {
return
}
var (
p *ipsc.Packet
err error
)
if p, err = ParseData(d); err != nil {
log.Printf("dmr/homebrew: error parsing DMRD frame: %v\n", err)
return
}
l.stream(p)
}

@ -0,0 +1,105 @@
package ipsc
import (
"encoding/hex"
"fmt"
"github.com/pd0mz/go-dmr/bit"
"github.com/pd0mz/go-dmr/dmr"
)
const (
VoiceLCHeader uint16 = 0x1111
TerminatorWithLC uint16 = 0x2222
CSBK uint16 = 0x3333
DataHeader uint16 = 0x4444
Rate12Data uint16 = 0x5555
Rate34Data uint16 = 0x6666
VoiceDataA uint16 = 0xbbbb
VoiceDataB uint16 = 0xcccc
VoiceDataC uint16 = 0x7777
VoiceDataD uint16 = 0x8888
VoiceDataE uint16 = 0x9999
VoiceDataF uint16 = 0xaaaa
IPSCSync uint16 = 0xeeee
UnknownSlotType uint16 = 0x0000
)
const (
CallTypePrivate uint8 = iota
CallTypeGroup
)
var (
TimeslotName = map[uint8]string{
0x00: "TS1",
0x01: "TS2",
}
FrameTypeName = map[uint8]string{
0x00: "voice",
0x01: "voice sync",
0x02: "data sync",
0x03: "unused",
}
SlotTypeName = map[uint16]string{
0x0000: "unknown",
0x1111: "voice LC header",
0x2222: "terminator with LC",
0x3333: "CBSK",
0x4444: "data header",
0x5555: "rate 1/2 data",
0x6666: "rate 3/4 data",
0x7777: "voice data C",
0x8888: "voice data D",
0x9999: "voice data E",
0xaaaa: "voice data F",
0xbbbb: "voice data A",
0xcccc: "voice data B",
0xeeee: "IPSC sync",
}
CallTypeName = map[uint8]string{
0x00: "private",
0x01: "group",
}
)
type Packet struct {
Timeslot uint8 // 0=ts1, 1=ts2
FrameType uint8
SlotType uint16
CallType uint8 // 0=private, 1=group
SrcID uint32
DstID uint32
Payload []byte // 34 bytes
PayloadBits bit.Bits // 264 bits
Sequence uint8
}
func (p *Packet) Dump() string {
var s string
s += fmt.Sprintf("timeslot..: 0b%02b (%s)\n", p.Timeslot, TimeslotName[p.Timeslot])
s += fmt.Sprintf("frame type: 0b%02b (%s)\n", p.FrameType, FrameTypeName[p.FrameType])
s += fmt.Sprintf("slot type.: 0x%04x (%s)\n", p.SlotType, SlotTypeName[p.SlotType])
s += fmt.Sprintf("call type.: 0x%02x (%s)\n", p.CallType, CallTypeName[p.CallType])
s += fmt.Sprintf("source....: %d\n", p.SrcID)
s += fmt.Sprintf("target....: %d\n", p.DstID)
s += fmt.Sprintf("payload...: %d bytes (swapped):\n", len(p.Payload))
s += hex.Dump(p.Payload)
s += fmt.Sprintf("payload...: %d bits:\n", len(p.PayloadBits))
s += p.PayloadBits.Dump()
return s
}
func (p *Packet) InfoBits() bit.Bits {
var b = make(bit.Bits, dmr.InfoBits)
copy(b[0:dmr.InfoHalfBits], p.PayloadBits[0:dmr.InfoHalfBits])
copy(b[dmr.InfoHalfBits:], p.PayloadBits[dmr.InfoHalfBits+dmr.SlotTypeBits+dmr.SignalBits:])
return b
}
func (p *Packet) VoiceBits() bit.Bits {
var b = make(bit.Bits, dmr.VoiceBits)
copy(b[:dmr.VoiceHalfBits], p.PayloadBits[:dmr.VoiceHalfBits])
copy(b[dmr.VoiceHalfBits:], p.PayloadBits[dmr.VoiceHalfBits+dmr.SignalBits:])
return b
}

@ -0,0 +1,7 @@
package ipsc
func SwapPayloadBytes(payload []byte) {
for i := 0; i < len(payload)-1; i += 2 {
payload[i], payload[i+1] = payload[i+1], payload[i]
}
}
Loading…
Cancel
Save