Skip to content
This repository has been archived by the owner on Dec 14, 2021. It is now read-only.

Commit

Permalink
Merge pull request #758 from TheThingsNetwork/feature/adr
Browse files Browse the repository at this point in the history
Refactor ADR handling
  • Loading branch information
htdvisser authored Mar 7, 2019
2 parents fd911b0 + f57e965 commit 7d6d975
Show file tree
Hide file tree
Showing 5 changed files with 240 additions and 474 deletions.
1 change: 1 addition & 0 deletions cmd/docs/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -147,6 +147,7 @@ ttn gen-keypair generates a public/private keypair
**Options**

```
--force-adr-optimize Force ADR optimization
--net-id int LoRaWAN NetID (default 19)
--redis-address string Redis server and port (default "localhost:6379")
--redis-db int Redis database
Expand Down
3 changes: 3 additions & 0 deletions cmd/networkserver.go
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,9 @@ func init() {
"26000000/20": "otaa,abp,world,local,private,testing",
})

networkserverCmd.Flags().Bool("force-adr-optimize", false, "Force ADR optimization")
viper.BindPFlag("networkserver.force-adr-optimize", networkserverCmd.Flags().Lookup("force-adr-optimize"))

networkserverCmd.Flags().String("server-address", "0.0.0.0", "The IP address to listen for communication")
networkserverCmd.Flags().String("server-address-announce", "localhost", "The public IP address to announce")
networkserverCmd.Flags().Int("server-port", 1903, "The port for communication")
Expand Down
177 changes: 57 additions & 120 deletions core/networkserver/adr.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@
package networkserver

import (
"math"
"sort"

pb_broker "github.com/TheThingsNetwork/api/broker"
Expand All @@ -15,6 +14,7 @@ import (
"github.com/TheThingsNetwork/ttn/core/networkserver/device"
"github.com/TheThingsNetwork/ttn/utils/errors"
"github.com/brocaar/lorawan"
"github.com/spf13/viper"
)

// DefaultADRMargin is the default SNR margin for ADR
Expand All @@ -33,15 +33,6 @@ func maxSNR(frames []*device.Frame) float32 {
return max
}

func lossPercentage(frames []*device.Frame) int {
if len(frames) == 0 {
return 0
}
sentPackets := frames[0].FCnt - frames[len(frames)-1].FCnt + 1
loss := sentPackets - uint32(len(frames))
return int(math.Floor((float64(loss) / float64(sentPackets) * 100) + .5))
}

const ScheduleMACEvent = "schedule mac command"

func (n *networkServer) handleUplinkADR(message *pb_broker.DeduplicatedUplinkMessage, dev *device.Device) error {
Expand Down Expand Up @@ -75,57 +66,74 @@ func (n *networkServer) handleUplinkADR(message *pb_broker.DeduplicatedUplinkMes
ctx.WithError(err).Error("Could not push frame for device")
}

frames, _ := history.Get()

if dev.ADR.Failed <= maxADRFails && len(frames) >= device.FramesHistorySize {
lorawanDownlinkMAC.ADR = true
}

md := message.GetProtocolMetadata()
if dev.ADR.Band == "" {
dev.ADR.Band = md.GetLoRaWAN().GetFrequencyPlan().String()
}
if dev.ADR.Margin == 0 {
dev.ADR.Margin = DefaultADRMargin
}

var scheduleADR, forceADR bool
fp, err := band.Get(dev.ADR.Band)
if err != nil {
return err
}
dev.ADR.DataRate = md.GetLoRaWAN().GetDataRate()
if dev.ADR.TxPower == 0 {
dev.ADR.TxPower = fp.DefaultTXPower
}
if dev.ADR.NbTrans == 0 {
dev.ADR.NbTrans = 1
}
dev.ADR.SendReq = false

switch dev.ADR.Band {
case pb_lorawan.FrequencyPlan_US_902_928.String(), pb_lorawan.FrequencyPlan_AU_915_928.String():
if !dev.ADR.SentInitial {
scheduleADR = true
forceADR = true
message.Trace = message.Trace.WithEvent(ScheduleMACEvent, macCMD, "link-adr", "reason", "initial")
ctx.Debug("Schedule ADR [initial]")
}
adrMargin := float32(dev.ADR.Margin)
frames, _ := history.Get()
if len(frames) >= device.FramesHistorySize {
frames = frames[:device.FramesHistorySize]
} else {
adrMargin += 2.5
}

dataRate := md.GetLoRaWAN().GetDataRate()
if dev.ADR.DataRate != dataRate {
dev.ADR.DataRate = dataRate
scheduleADR = true
message.Trace = message.Trace.WithEvent(ScheduleMACEvent, macCMD, "link-adr", "reason", "optimize")
ctx.Debug("Schedule ADR [optimize]")
desiredDataRate, desiredTxPower, err := fp.ADRSettings(dev.ADR.DataRate, dev.ADR.TxPower, maxSNR(frames), adrMargin)
if err == band.ErrADRUnavailable {
ctx.Debugf("ADR not available in %s", dev.ADR.Band)
return nil
}
if err != nil {
return err
}

if lorawanUplinkMAC.ADRAckReq {
scheduleADR = true
var forceADR bool

if !dev.ADR.SentInitial && (dev.ADR.Band == pb_lorawan.FrequencyPlan_US_902_928.String() || dev.ADR.Band == pb_lorawan.FrequencyPlan_AU_915_928.String()) {
dev.ADR.SendReq = true
forceADR = true
message.Trace = message.Trace.WithEvent(ScheduleMACEvent, macCMD, "link-adr", "reason", "initial")
ctx.Debug("Schedule ADR [initial]")
} else if lorawanUplinkMAC.ADRAckReq {
dev.ADR.SendReq = true
forceADR = true
message.Trace = message.Trace.WithEvent(ScheduleMACEvent, macCMD, "link-adr", "reason", "adr-ack-req")
lorawanDownlinkMAC.Ack = true
message.Trace = message.Trace.WithEvent("set ack", "reason", "adr-ack-req")
ctx.Debug("Schedule ADR [adr-ack-req]")
} else if dev.ADR.DataRate != desiredDataRate || dev.ADR.TxPower != desiredTxPower {
dev.ADR.SendReq = true
if drIdx, err := fp.GetDataRateIndexFor(dev.ADR.DataRate); err == nil && drIdx == 0 {
forceADR = true
} else {
forceADR = viper.GetBool("networkserver.force-adr-optimize")
}
message.Trace = message.Trace.WithEvent(ScheduleMACEvent, macCMD, "link-adr", "reason", "optimize")
ctx.Debugf("Schedule ADR [optimize] %s->%s", dev.ADR.DataRate, desiredDataRate)
}

if scheduleADR {
if fp, err := band.Get(dev.ADR.Band); err == nil {
if drIdx, err := fp.GetDataRateIndexFor(dataRate); err == nil && drIdx == 0 {
if len(frames) >= device.FramesHistorySize {
forceADR = true
message.Trace = message.Trace.WithEvent(ScheduleMACEvent, macCMD, "link-adr", "reason", "avoid high sf")
ctx.Debug("Schedule ADR [avoid high sf]")
}
}
}
if !dev.ADR.SendReq {
return nil
}

dev.ADR.SendReq = scheduleADR
dev.ADR.DataRate, dev.ADR.TxPower, dev.ADR.NbTrans = desiredDataRate, desiredTxPower, 1

if forceADR {
err := n.setADR(lorawanDownlinkMAC, dev)
if err != nil {
Expand All @@ -138,7 +146,7 @@ func (n *networkServer) handleUplinkADR(message *pb_broker.DeduplicatedUplinkMes
return nil
}

const maxADRFails = 3
const maxADRFails = 10

func (n *networkServer) setADR(mac *pb_lorawan.MACPayload, dev *device.Device) error {
if !dev.ADR.SendReq {
Expand Down Expand Up @@ -166,90 +174,19 @@ func (n *networkServer) setADR(mac *pb_lorawan.MACPayload, dev *device.Device) e
ctx.Debug("Empty ADR DataRate")
return nil
}
if dev.ADR.Margin == 0 {
dev.ADR.Margin = DefaultADRMargin
}
if dev.ADR.Band == "" {
ctx.Debug("Empty ADR Band")
return nil
}
fp, err := band.Get(dev.ADR.Band)
if err != nil {
return err
}
if dev.ADR.TxPower == 0 {
dev.ADR.TxPower = fp.DefaultTXPower
}
if dev.ADR.NbTrans == 0 {
dev.ADR.NbTrans = 1
}

// Get history
history, err := n.devices.Frames(dev.AppEUI, dev.DevEUI)
if err != nil {
return err
}
frames, err := history.Get()
if err != nil {
return err
}
switch {
case len(frames) == 0:
ctx.Debug("No historical data for ADR")
return nil
case len(frames) >= device.FramesHistorySize:
frames = frames[:device.FramesHistorySize]
}

// Add extra margin if we don't have enough data yet
adrMargin := float32(dev.ADR.Margin)
if len(frames) < device.FramesHistorySize {
adrMargin += 2.5
}

// Calculate desired ADR settings
dataRate, txPower, err := fp.ADRSettings(dev.ADR.DataRate, dev.ADR.TxPower, maxSNR(frames), adrMargin)
if err == band.ErrADRUnavailable {
ctx.Debugf("ADR not available in %s", dev.ADR.Band)
return nil
}
fp, err := band.Get(dev.ADR.Band)
if err != nil {
return err
}
drIdx, err := fp.GetDataRateIndexFor(dataRate)
drIdx, err := fp.GetDataRateIndexFor(dev.ADR.DataRate)
if err != nil {
return err
}
powerIdx, err := fp.GetTxPowerIndexFor(txPower)
powerIdx, err := fp.GetTxPowerIndexFor(dev.ADR.TxPower)
if err != nil {
powerIdx, _ = fp.GetTxPowerIndexFor(fp.DefaultTXPower)
}
var nbTrans = dev.ADR.NbTrans
if dev.ADR.DataRate == dataRate && dev.ADR.TxPower == txPower && !dev.Options.DisableFCntCheck {
lossPercentage := lossPercentage(frames)
switch {
case lossPercentage <= 5:
nbTrans--
case lossPercentage <= 10:
// don't change
case lossPercentage <= 30:
nbTrans++
default:
nbTrans += 2
}
if nbTrans < 1 {
nbTrans = 1
}
if nbTrans > 3 {
nbTrans = 3
}
}

if dev.ADR.SentInitial && dev.ADR.DataRate == dataRate && dev.ADR.TxPower == txPower && dev.ADR.NbTrans == nbTrans {
ctx.Debug("No ADR needed")
return nil // Nothing to do
}
dev.ADR.DataRate, dev.ADR.TxPower, dev.ADR.NbTrans = dataRate, txPower, nbTrans

payloads := getAdrReqPayloads(dev, &fp, drIdx, powerIdx)
if len(payloads) == 0 {
Expand Down
Loading

0 comments on commit 7d6d975

Please sign in to comment.