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.

145 lines
3.0 KiB
Go

package repeater
import (
"fmt"
"log"
"time"
"github.com/tehmaze/go-dmr/bit"
"github.com/tehmaze/go-dmr/ipsc"
)
const (
Idle uint8 = iota
VoiceCallRunning
DataCallRunning
)
type SlotData struct {
BlocksReceived uint8
BlocksExpected uint8
PacketHeaderValid bool
}
type SlotVoice struct {
// Last frame number
Frame uint8
}
type Slot struct {
State uint8
LastCallReceived time.Time
CallStart time.Time
CallEnd time.Time
CallType uint8
SrcID, DstID uint32
Data SlotData
Voice SlotVoice
LastSequence uint8
LastSlotType uint16
}
type Repeater struct {
Slot []*Slot
DataFrameFunc func(*ipsc.Packet, bit.Bits)
VoiceFrameFunc func(*ipsc.Packet, bit.Bits)
}
func New() *Repeater {
r := &Repeater{
Slot: make([]*Slot, 2),
}
r.Slot[0] = &Slot{}
r.Slot[1] = &Slot{}
return r
}
func (r *Repeater) DataCallEnd(p *ipsc.Packet) {
if p.Timeslot > 1 {
return
}
slot := r.Slot[p.Timeslot]
if slot.State != DataCallRunning {
return
}
log.Printf("dmr/repeater: data call ended on TS%d, %d -> %d\n", p.Timeslot+1, slot.SrcID, slot.DstID)
slot.State = Idle
slot.CallEnd = time.Now()
slot.Data.PacketHeaderValid = false
}
func (r *Repeater) VoiceCallStart(p *ipsc.Packet) {
if p.Timeslot > 1 {
return
}
slot := r.Slot[p.Timeslot]
if slot.State == VoiceCallRunning {
r.VoiceCallEnd(p)
}
log.Printf("dmr/repeater: voice call started on TS%d, %d -> %d\n", p.Timeslot+1, slot.SrcID, slot.DstID)
slot.CallStart = time.Now()
slot.CallType = p.CallType
slot.SrcID = p.SrcID
slot.DstID = p.DstID
slot.State = VoiceCallRunning
}
func (r *Repeater) VoiceCallEnd(p *ipsc.Packet) {
if p.Timeslot > 1 {
return
}
slot := r.Slot[p.Timeslot]
if slot.State != VoiceCallRunning {
return
}
log.Printf("dmr/repeater: voice call ended on TS%d, %d -> %d\n", p.Timeslot+1, slot.SrcID, slot.DstID)
slot.State = Idle
slot.CallEnd = time.Now()
}
func (r *Repeater) Stream(p *ipsc.Packet) {
// Kill errneous timeslots here
if p.Timeslot > 1 {
log.Printf("killed packet with timeslot %d\n", p.Timeslot)
return
}
if p.Sequence == r.Slot[p.Timeslot].LastSequence {
return
}
r.Slot[p.Timeslot].LastSequence = p.Sequence
var err error
fmt.Printf("ts%d/dmr[%03d] [%d->%d]: %s: ", p.Timeslot+1, p.Sequence, p.SrcID, p.DstID, ipsc.SlotTypeName[p.SlotType])
switch p.SlotType {
case ipsc.VoiceLCHeader:
err = r.HandleVoiceLCHeader(p)
case ipsc.TerminatorWithLC:
err = r.HandleTerminatorWithLC(p)
case ipsc.DataHeader:
err = r.HandleDataHeader(p)
case ipsc.VoiceDataA, ipsc.VoiceDataB, ipsc.VoiceDataC, ipsc.VoiceDataD, ipsc.VoiceDataE, ipsc.VoiceDataF:
// De-duplicate packets, since we could run in merged TS1/2 mode
if r.Slot[p.Timeslot].LastSlotType == p.SlotType {
fmt.Println("(ignored)")
} else {
err = r.HandleVoiceData(p)
}
r.Slot[p.Timeslot].LastSlotType = p.SlotType
default:
fmt.Println("unhandled")
}
if err != nil {
fmt.Printf("error: %v\n", err)
}
}