You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
215 lines
4.2 KiB
Go
215 lines
4.2 KiB
Go
package terminal
|
|
|
|
import (
|
|
"encoding/hex"
|
|
"fmt"
|
|
"time"
|
|
|
|
"github.com/op/go-logging"
|
|
"github.com/pd0mz/go-dmr"
|
|
"github.com/pd0mz/go-dmr/bptc"
|
|
"github.com/pd0mz/go-dmr/trellis"
|
|
)
|
|
|
|
var log = logging.MustGetLogger("dmr/terminal")
|
|
|
|
const (
|
|
idle uint8 = iota
|
|
dataCallActive
|
|
voideCallActive
|
|
)
|
|
|
|
type Slot struct {
|
|
call struct {
|
|
start time.Time
|
|
end time.Time
|
|
}
|
|
dstID, srcID uint32
|
|
dataType uint8
|
|
data struct {
|
|
packetHeaderValid bool
|
|
blocks [64]dmr.DataBlock
|
|
blocksExpected uint8
|
|
blocksReceived uint8
|
|
}
|
|
selectiveAckRequestsSent int
|
|
rxSequence int
|
|
fullMessageBlocks uint8
|
|
last struct {
|
|
packetReceived time.Time
|
|
}
|
|
}
|
|
|
|
func NewSlot() Slot {
|
|
return Slot{}
|
|
}
|
|
|
|
type Terminal struct {
|
|
ID uint32
|
|
Call string
|
|
Repeater dmr.Repeater
|
|
slot []Slot
|
|
state uint8
|
|
}
|
|
|
|
func New(id uint32, call string, r dmr.Repeater) *Terminal {
|
|
t := &Terminal{
|
|
ID: id,
|
|
Call: call,
|
|
Repeater: r,
|
|
slot: []Slot{NewSlot(), NewSlot()},
|
|
}
|
|
r.SetPacketFunc(t.handlePacket)
|
|
return t
|
|
}
|
|
|
|
func (t *Terminal) dataCallEnd(p *dmr.Packet) error {
|
|
slot := t.slot[p.Timeslot]
|
|
|
|
if t.state != dataCallActive {
|
|
return nil
|
|
}
|
|
|
|
log.Debugf("[%d->%d] data call ended", slot.srcID, slot.dstID)
|
|
|
|
return nil
|
|
}
|
|
|
|
func (t *Terminal) dataCallStart(p *dmr.Packet) error {
|
|
slot := t.slot[p.Timeslot]
|
|
|
|
if slot.dstID != p.DstID || slot.srcID != p.SrcID || slot.dataType != p.DataType {
|
|
if err := t.dataCallEnd(p); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
|
|
slot.data.packetHeaderValid = false
|
|
slot.call.start = time.Now()
|
|
slot.call.end = time.Time{}
|
|
slot.dstID = p.DstID
|
|
slot.srcID = p.SrcID
|
|
t.state = dataCallActive
|
|
|
|
log.Debugf("[%d->%d] data call started", slot.srcID, slot.dstID)
|
|
|
|
return nil
|
|
}
|
|
|
|
func (t *Terminal) handlePacket(r dmr.Repeater, p *dmr.Packet) error {
|
|
var err error
|
|
if p.DstID != t.ID {
|
|
//log.Debugf("[%d->%d] (%s, %#04b): ignored, not sent to me", p.SrcID, p.DstID, dmr.DataTypeName[p.DataType], p.DataType)
|
|
return nil
|
|
}
|
|
|
|
switch p.DataType {
|
|
case dmr.VoiceBurstA, dmr.VoiceBurstB, dmr.VoiceBurstC, dmr.VoiceBurstD, dmr.VoiceBurstE, dmr.VoiceBurstF:
|
|
return nil
|
|
case dmr.CBSK:
|
|
return nil
|
|
}
|
|
|
|
log.Debugf("[%d->%d] (%s, %#04b): sent to me", p.SrcID, p.DstID, dmr.DataTypeName[p.DataType], p.DataType)
|
|
|
|
switch p.DataType {
|
|
case dmr.CBSK:
|
|
err = t.handleControlBlock(p)
|
|
break
|
|
case dmr.Data:
|
|
err = t.handleData(p)
|
|
break
|
|
case dmr.Rate34Data:
|
|
err = t.handleRate34Data(p)
|
|
default:
|
|
log.Debug(hex.Dump(p.Data))
|
|
}
|
|
|
|
if err != nil {
|
|
log.Errorf("handle packet error: %v", err)
|
|
}
|
|
|
|
return err
|
|
}
|
|
|
|
func (t *Terminal) handleControlBlock(p *dmr.Packet) error {
|
|
slot := t.slot[p.Timeslot]
|
|
slot.last.packetReceived = time.Now()
|
|
|
|
var (
|
|
bits = p.InfoBits()
|
|
data = make([]byte, 12)
|
|
)
|
|
|
|
if err := bptc.Decode(bits, data); err != nil {
|
|
return err
|
|
}
|
|
cb, err := dmr.ParseControlBlock(data)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
log.Debugf("[%d->%d] control block %T", cb.SrcID, cb.DstID, cb.Data)
|
|
|
|
return nil
|
|
}
|
|
|
|
func (t *Terminal) handleData(p *dmr.Packet) error {
|
|
slot := t.slot[p.Timeslot]
|
|
slot.last.packetReceived = time.Now()
|
|
|
|
var (
|
|
bits = p.InfoBits()
|
|
data = make([]byte, 12)
|
|
)
|
|
|
|
if err := bptc.Decode(bits, data); err != nil {
|
|
return err
|
|
}
|
|
|
|
h, err := dmr.ParseDataHeader(data, false)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
slot.data.packetHeaderValid = false
|
|
slot.data.blocksReceived = 0
|
|
slot.selectiveAckRequestsSent = 0
|
|
slot.rxSequence = 0
|
|
|
|
c := h.CommonHeader()
|
|
log.Debugf("[%d->%d] data header %T", c.SrcID, c.DstID, h)
|
|
|
|
switch ht := h.(type) {
|
|
case dmr.ShortDataDefinedDataHeader:
|
|
if ht.FullMessage {
|
|
slot.data.blocks = [64]dmr.DataBlock{}
|
|
slot.fullMessageBlocks = ht.AppendedBlocks
|
|
log.Debugf("[%d->%d] expecting %d data blocks", c.SrcID, c.DstID, slot.fullMessageBlocks)
|
|
}
|
|
slot.data.blocksExpected = ht.AppendedBlocks
|
|
return t.dataCallStart(p)
|
|
default:
|
|
log.Warningf("unhandled data header %T", h)
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func (t *Terminal) handleRate34Data(p *dmr.Packet) error {
|
|
slot := t.slot[p.Timeslot]
|
|
slot.last.packetReceived = time.Now()
|
|
|
|
var (
|
|
bits = p.InfoBits()
|
|
data = make([]byte, 18)
|
|
)
|
|
|
|
if err := trellis.Decode(bits, data); err != nil {
|
|
return err
|
|
}
|
|
fmt.Print(hex.Dump(data))
|
|
|
|
return nil
|
|
}
|