From e00b87c08e504551c59de9edb32a0de946aba7fe Mon Sep 17 00:00:00 2001 From: belovictor Date: Thu, 18 Oct 2018 15:41:18 +0300 Subject: [PATCH 01/14] Add support for RU_864_870 to ADR processing --- core/networkserver/adr.go | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/core/networkserver/adr.go b/core/networkserver/adr.go index 4f7680630..3f6d8bcca 100644 --- a/core/networkserver/adr.go +++ b/core/networkserver/adr.go @@ -332,6 +332,34 @@ func getAdrReqPayloads(dev *device.Device, frequencyPlan *band.FrequencyPlan, dr } } } + case pb_lorawan.FrequencyPlan_RU_864_870.String(): + if dev.ADR.Failed > 0 && powerIdx > 5 { + // fall back to txPower 5 for LoRaWAN 1.0 + powerIdx = 5 + } + payloads = []lorawan.LinkADRReqPayload{ + { + DataRate: uint8(drIdx), + TXPower: uint8(powerIdx), + Redundancy: lorawan.Redundancy{ + ChMaskCntl: 0, + NbRep: uint8(dev.ADR.NbTrans), + }, + }, + } + if dev.ADR.Failed > 0 { + // Fall back to the mandatory RU_864_870 LoRaWAN channels + payloads[0].ChMask[0] = true + payloads[0].ChMask[1] = true + } else { + for i, ch := range frequencyPlan.UplinkChannels { + for _, dr := range ch.DataRates { + if dr == drIdx { + payloads[0].ChMask[i] = true + } + } + } + } case pb_lorawan.FrequencyPlan_US_902_928.String(), pb_lorawan.FrequencyPlan_AU_915_928.String(): var dr500 uint8 switch dev.ADR.Band { From 02eded92ffbbefd23020434afbf8b05f19c75b40 Mon Sep 17 00:00:00 2001 From: belovictor Date: Thu, 18 Oct 2018 16:31:21 +0300 Subject: [PATCH 02/14] Remove power index check and add RU_864_870 to ADR tests --- core/networkserver/adr.go | 4 ---- core/networkserver/adr_test.go | 21 +++++++++++++++++++++ 2 files changed, 21 insertions(+), 4 deletions(-) diff --git a/core/networkserver/adr.go b/core/networkserver/adr.go index 3f6d8bcca..a6e8f4c07 100644 --- a/core/networkserver/adr.go +++ b/core/networkserver/adr.go @@ -333,10 +333,6 @@ func getAdrReqPayloads(dev *device.Device, frequencyPlan *band.FrequencyPlan, dr } } case pb_lorawan.FrequencyPlan_RU_864_870.String(): - if dev.ADR.Failed > 0 && powerIdx > 5 { - // fall back to txPower 5 for LoRaWAN 1.0 - powerIdx = 5 - } payloads = []lorawan.LinkADRReqPayload{ { DataRate: uint8(drIdx), diff --git a/core/networkserver/adr_test.go b/core/networkserver/adr_test.go index 4b137b8e5..905774ade 100644 --- a/core/networkserver/adr_test.go +++ b/core/networkserver/adr_test.go @@ -298,6 +298,27 @@ func TestHandleDownlinkADR(t *testing.T) { a.So(payload.ChMask[8], ShouldBeFalse) // 9th channel (FSK) disabled } + dev.ADR.DataRate = "SF10BW125" + dev.ADR.TxPower = 20 + + { + dev.ADR.Band = "RU_864_870" + message := adrInitDownlinkMessage() + err := ns.handleDownlinkADR(message, dev) + a.So(err, ShouldBeNil) + fOpts := message.Message.GetLoRaWAN().GetMACPayload().FOpts + a.So(fOpts, ShouldHaveLength, 2) + a.So(fOpts[1].CID, ShouldEqual, lorawan.LinkADRReq) + payload := new(lorawan.LinkADRReqPayload) + payload.UnmarshalBinary(fOpts[1].Payload) + a.So(payload.DataRate, ShouldEqual, 5) // SF7BW125 + a.So(payload.TXPower, ShouldEqual, 1) // 14 + for i := 0; i < 7; i++ { // First 7 channels enabled + a.So(payload.ChMask[i], ShouldBeTrue) + } + a.So(payload.ChMask[7], ShouldBeFalse) // 8th channel disabled + } + shouldHaveNbTrans := func(nbTrans int) { a := New(t) message := adrInitDownlinkMessage() From b86efc410138b06ca8eb8273e822f52d99c2cb86 Mon Sep 17 00:00:00 2001 From: D0nShim0da Date: Tue, 23 Oct 2018 21:17:20 +0100 Subject: [PATCH 03/14] https://github.com/golang/lint/issues/415 --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 5baf6fd30..dfd6fbdf6 100644 --- a/Makefile +++ b/Makefile @@ -29,7 +29,7 @@ dev-deps: deps @command -v protoc-gen-gogottn > /dev/null || go install github.com/TheThingsNetwork/ttn/utils/protoc-gen-gogottn @command -v protoc-gen-ttndoc > /dev/null || go install github.com/TheThingsNetwork/ttn/utils/protoc-gen-ttndoc @command -v mockgen > /dev/null || go get github.com/golang/mock/mockgen - @command -v golint > /dev/null || go get github.com/golang/lint/golint + @command -v golint > /dev/null || go get golang.org/x/lint/golint @command -v forego > /dev/null || go get github.com/ddollar/forego # Protobuf From 32848c7b71d3cad423e59135edfd83e151b3ea12 Mon Sep 17 00:00:00 2001 From: Hylke Visser Date: Wed, 21 Nov 2018 13:23:44 +0100 Subject: [PATCH 04/14] Slow down invinite loop when router-broker conn errors and log the error --- core/router/router.go | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/core/router/router.go b/core/router/router.go index 38c042498..767dde7be 100644 --- a/core/router/router.go +++ b/core/router/router.go @@ -184,13 +184,20 @@ func (r *router) getBroker(brokerAnnouncement *pb_discovery.Announcement) (*brok brk.association = cli.NewRouterStreams(r.Identity.ID, "") go func() { + downlinkStream := brk.association.Downlink() + refreshDownlinkStream := time.NewTicker(time.Second) for { select { case message := <-brk.uplink: brk.association.Uplink(message) - case message, ok := <-brk.association.Downlink(): + case <-refreshDownlinkStream.C: + downlinkStream = brk.association.Downlink() + case message, ok := <-downlinkStream: if ok { go r.HandleDownlink(message) + } else { + r.Ctx.WithField("broker", brokerAnnouncement.ID).Error("Downlink Stream errored") + downlinkStream = nil } } } From b44392c192a26074f4d46f5e715ed8729cd5d4b6 Mon Sep 17 00:00:00 2001 From: Robert Bricheno Date: Wed, 5 Dec 2018 10:55:17 +0000 Subject: [PATCH 05/14] Update otto hash in vendor.json, closes #750 * Update from nonexistent version to latest version at github.com/TheThingsIndustries/otto * Reverted revision times to original format --- vendor/vendor.json | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/vendor/vendor.json b/vendor/vendor.json index 49a5aab74..f1e48e25f 100644 --- a/vendor/vendor.json +++ b/vendor/vendor.json @@ -968,49 +968,49 @@ "checksumSHA1": "o0+FxYUsE4W0478XA9AnSIB5wfs=", "origin": "github.com/TheThingsIndustries/otto", "path": "github.com/robertkrimen/otto", - "revision": "571d0fa31a277d8a63de1fd0afee431d627fbbaf", + "revision": "6ddbbb60554aaa88b1c38a9f5518165bc1160bd7", "revisionTime": "2017-11-07T14:36:49Z" }, { "checksumSHA1": "q1HKpLIWi3tTT5W5LqMlwPcGmyQ=", "origin": "github.com/TheThingsIndustries/otto/ast", "path": "github.com/robertkrimen/otto/ast", - "revision": "571d0fa31a277d8a63de1fd0afee431d627fbbaf", + "revision": "6ddbbb60554aaa88b1c38a9f5518165bc1160bd7", "revisionTime": "2017-11-07T14:36:49Z" }, { "checksumSHA1": "L0KsB2EzTlPgv0iae3q3SukNW7U=", "origin": "github.com/TheThingsIndustries/otto/dbg", "path": "github.com/robertkrimen/otto/dbg", - "revision": "571d0fa31a277d8a63de1fd0afee431d627fbbaf", + "revision": "6ddbbb60554aaa88b1c38a9f5518165bc1160bd7", "revisionTime": "2017-11-07T14:36:49Z" }, { "checksumSHA1": "euDLJKhw4doeTSxjEoezjxYXLzs=", "origin": "github.com/TheThingsIndustries/otto/file", "path": "github.com/robertkrimen/otto/file", - "revision": "571d0fa31a277d8a63de1fd0afee431d627fbbaf", + "revision": "6ddbbb60554aaa88b1c38a9f5518165bc1160bd7", "revisionTime": "2017-11-07T14:36:49Z" }, { "checksumSHA1": "w5wu2avr5PWCJy5dlEhlY3rwb+w=", "origin": "github.com/TheThingsIndustries/otto/parser", "path": "github.com/robertkrimen/otto/parser", - "revision": "571d0fa31a277d8a63de1fd0afee431d627fbbaf", + "revision": "6ddbbb60554aaa88b1c38a9f5518165bc1160bd7", "revisionTime": "2017-11-07T14:36:49Z" }, { "checksumSHA1": "7J/7NaYRqKhBvZ+dTIutsEoEgFw=", "origin": "github.com/TheThingsIndustries/otto/registry", "path": "github.com/robertkrimen/otto/registry", - "revision": "571d0fa31a277d8a63de1fd0afee431d627fbbaf", + "revision": "6ddbbb60554aaa88b1c38a9f5518165bc1160bd7", "revisionTime": "2017-11-07T14:36:49Z" }, { "checksumSHA1": "/jMXYuXycBpTqWhRyJ2xsqvHvQI=", "origin": "github.com/TheThingsIndustries/otto/token", "path": "github.com/robertkrimen/otto/token", - "revision": "571d0fa31a277d8a63de1fd0afee431d627fbbaf", + "revision": "6ddbbb60554aaa88b1c38a9f5518165bc1160bd7", "revisionTime": "2017-11-07T14:36:49Z" }, { From dcc6081adf2d7412dac9544f01f4fc9cb12314fb Mon Sep 17 00:00:00 2001 From: Hylke Visser Date: Fri, 11 Jan 2019 11:39:03 +0100 Subject: [PATCH 06/14] Improve guessing of AS923 band --- core/band/band.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/band/band.go b/core/band/band.go index 9eeeb5655..acb2c6c69 100644 --- a/core/band/band.go +++ b/core/band/band.go @@ -48,7 +48,7 @@ func (f *FrequencyPlan) GetTxPowerIndexFor(txPower int) (int, error) { func Guess(frequency uint64) string { // Join frequencies switch { - case frequency == 923200000 && frequency <= 923400000: + case frequency == 923200000 || frequency == 923400000: // not considering AS_920_923 and AS_923_925 because we're not sure return pb_lorawan.FrequencyPlan_AS_923.String() case frequency == 922100000 || frequency == 922300000 || frequency == 922500000: From 766176aa297436bd40ddcb23b166cfb7f17f66fd Mon Sep 17 00:00:00 2001 From: Hylke Visser Date: Tue, 12 Feb 2019 14:07:36 +0100 Subject: [PATCH 07/14] Make US and AU FSB configurable --- cmd/docs/README.md | 2 ++ cmd/root.go | 2 ++ core/band/band.go | 10 ++++++++-- core/router/downlink_test.go | 5 ----- 4 files changed, 12 insertions(+), 7 deletions(-) diff --git a/cmd/docs/README.md b/cmd/docs/README.md index e79166dfe..5ef8c4264 100644 --- a/cmd/docs/README.md +++ b/cmd/docs/README.md @@ -6,6 +6,7 @@ The Things Network's backend servers. ``` --allow-insecure Allow insecure fallback if TLS unavailable + --au-fsb int Frequency sub-band for the AU band (0-indexed) (default 1) --auth-token string The JWT token to be used for the discovery server --config string config file (default "$HOME/.ttn.yml") --description string The description of this component @@ -23,6 +24,7 @@ The Things Network's backend servers. --no-cli-logs Disable CLI logs --public Announce this component as part of The Things Network (public community network) --tls Use TLS (default true) + --us-fsb int Frequency sub-band for the US band (0-indexed) (default 1) ``` diff --git a/cmd/root.go b/cmd/root.go index da063dfa1..d9d289848 100644 --- a/cmd/root.go +++ b/cmd/root.go @@ -169,6 +169,8 @@ func init() { RootCmd.PersistentFlags().String("key-dir", path.Clean(dir+"/.ttn/"), "The directory where public/private keys are stored") RootCmd.PersistentFlags().Int("eu-rx2-dr", 3, "RX2 data rate for the EU band (SF12=0,SF9=3)") + RootCmd.PersistentFlags().Int("us-fsb", 1, "Frequency sub-band for the US band (0-indexed)") + RootCmd.PersistentFlags().Int("au-fsb", 1, "Frequency sub-band for the AU band (0-indexed)") viper.BindPFlags(RootCmd.PersistentFlags()) } diff --git a/core/band/band.go b/core/band/band.go index acb2c6c69..8a9e0333b 100644 --- a/core/band/band.go +++ b/core/band/band.go @@ -64,6 +64,12 @@ func Guess(frequency uint64) string { return "" } +func init() { + viper.SetDefault("eu-rx2-dr", "3") + viper.SetDefault("us-fsb", "1") + viper.SetDefault("au-fsb", "1") +} + // Get the frequency plan for the given region func Get(region string) (frequencyPlan FrequencyPlan, err error) { defer func() { @@ -94,7 +100,7 @@ func Get(region string) (frequencyPlan FrequencyPlan, err error) { frequencyPlan.ADR = &ADRConfig{MinDataRate: 0, MaxDataRate: 5, MinTXPower: 2, MaxTXPower: 14, StepTXPower: 3} case pb_lorawan.FrequencyPlan_US_902_928.String(): frequencyPlan.Band, err = lora.GetConfig(lora.US_902_928, false, lorawan.DwellTime400ms) - fsb := 1 // Enable 903.9-905.3/200 kHz, 904.6/500kHz channels + fsb := viper.GetInt("us-fsb") // If this is 1, enables 903.9-905.3/200 kHz, 904.6/500kHz channels, etc. for channel := 0; channel < 72; channel++ { if (channel < fsb*8 || channel >= (fsb+1)*8) && channel != fsb+64 { frequencyPlan.DisableUplinkChannel(channel) @@ -107,7 +113,7 @@ func Get(region string) (frequencyPlan FrequencyPlan, err error) { frequencyPlan.Band, err = lora.GetConfig(lora.EU_433, false, lorawan.DwellTimeNoLimit) case pb_lorawan.FrequencyPlan_AU_915_928.String(): frequencyPlan.Band, err = lora.GetConfig(lora.AU_915_928, false, lorawan.DwellTime400ms) - fsb := 1 // Enable 916.8-918.2/200 kHz, 917.5/500kHz channels + fsb := viper.GetInt("au-fsb") // If this is 1, enables 916.8-918.2/200 kHz, 917.5/500kHz channels, etc. for channel := 0; channel < 72; channel++ { if (channel < fsb*8 || channel >= (fsb+1)*8) && channel != fsb+64 { frequencyPlan.DisableUplinkChannel(channel) diff --git a/core/router/downlink_test.go b/core/router/downlink_test.go index 5fdbf4704..147129cd6 100644 --- a/core/router/downlink_test.go +++ b/core/router/downlink_test.go @@ -20,14 +20,9 @@ import ( . "github.com/TheThingsNetwork/ttn/utils/testing" "github.com/golang/mock/gomock" . "github.com/smartystreets/assertions" - "github.com/spf13/viper" "golang.org/x/net/context" ) -func init() { - viper.Set("eu-rx2-dr", "3") -} - // newReferenceDownlink returns a default uplink message func newReferenceDownlink() *pb.DownlinkMessage { up := &pb.DownlinkMessage{ From 83317e4a573d481210447147c61e5e8a024e772b Mon Sep 17 00:00:00 2001 From: Hylke Visser Date: Tue, 12 Feb 2019 14:26:06 +0100 Subject: [PATCH 08/14] Initialize frequency plan and channel tables explicitly --- cmd/root.go | 3 +++ core/band/band.go | 61 ++++++++++++++++++++++-------------------- core/band/band_test.go | 2 ++ 3 files changed, 37 insertions(+), 29 deletions(-) diff --git a/cmd/root.go b/cmd/root.go index d9d289848..fe5a2dec6 100644 --- a/cmd/root.go +++ b/cmd/root.go @@ -18,6 +18,7 @@ import ( "github.com/TheThingsNetwork/go-utils/log/grpc" promlog "github.com/TheThingsNetwork/go-utils/log/prometheus" "github.com/TheThingsNetwork/ttn/api" + "github.com/TheThingsNetwork/ttn/core/band" esHandler "github.com/TheThingsNetwork/ttn/utils/elasticsearch/handler" "github.com/apex/log" jsonHandler "github.com/apex/log/handlers/json" @@ -110,6 +111,8 @@ var RootCmd = &cobra.Command{ "Auth Servers": viper.GetStringMapString("auth-servers"), "Monitors": viper.GetStringMapString("monitor-servers"), }).Info("Initializing The Things Network") + + band.InitializeTables() }, PersistentPostRun: func(cmd *cobra.Command, args []string) { if logFile != nil { diff --git a/core/band/band.go b/core/band/band.go index 8a9e0333b..4d2cc7b52 100644 --- a/core/band/band.go +++ b/core/band/band.go @@ -4,6 +4,8 @@ package band import ( + "sync" + pb_lorawan "github.com/TheThingsNetwork/api/protocol/lorawan" "github.com/TheThingsNetwork/ttn/core/types" "github.com/TheThingsNetwork/ttn/utils/errors" @@ -72,11 +74,6 @@ func init() { // Get the frequency plan for the given region func Get(region string) (frequencyPlan FrequencyPlan, err error) { - defer func() { - if err == nil && region == pb_lorawan.FrequencyPlan_EU_863_870.String() { - frequencyPlan.RX2DataRate = viper.GetInt("eu-rx2-dr") - } - }() if fp, ok := frequencyPlans[region]; ok { return fp, nil } @@ -98,6 +95,7 @@ func Get(region string) (frequencyPlan FrequencyPlan, err error) { frequencyPlan.DownlinkChannels = frequencyPlan.UplinkChannels frequencyPlan.CFList = &lorawan.CFList{867100000, 867300000, 867500000, 867700000, 867900000} frequencyPlan.ADR = &ADRConfig{MinDataRate: 0, MaxDataRate: 5, MinTXPower: 2, MaxTXPower: 14, StepTXPower: 3} + frequencyPlan.RX2DataRate = viper.GetInt("eu-rx2-dr") case pb_lorawan.FrequencyPlan_US_902_928.String(): frequencyPlan.Band, err = lora.GetConfig(lora.US_902_928, false, lorawan.DwellTime400ms) fsb := viper.GetInt("us-fsb") // If this is 1, enables 903.9-905.3/200 kHz, 904.6/500kHz channels, etc. @@ -200,31 +198,36 @@ func Get(region string) (frequencyPlan FrequencyPlan, err error) { var frequencyPlans map[string]FrequencyPlan var channels map[int]string -func init() { - frequencyPlans = make(map[string]FrequencyPlan) - channels = make(map[int]string) - for _, r := range []pb_lorawan.FrequencyPlan{ // ordering is important here - pb_lorawan.FrequencyPlan_EU_863_870, - pb_lorawan.FrequencyPlan_IN_865_867, - pb_lorawan.FrequencyPlan_US_902_928, - pb_lorawan.FrequencyPlan_CN_779_787, - pb_lorawan.FrequencyPlan_EU_433, - pb_lorawan.FrequencyPlan_AS_923, - pb_lorawan.FrequencyPlan_AS_920_923, - pb_lorawan.FrequencyPlan_AS_923_925, - pb_lorawan.FrequencyPlan_KR_920_923, - pb_lorawan.FrequencyPlan_AU_915_928, - pb_lorawan.FrequencyPlan_CN_470_510, - pb_lorawan.FrequencyPlan_RU_864_870, - } { - region := r.String() - frequencyPlans[region], _ = Get(region) - for _, ch := range frequencyPlans[region].UplinkChannels { - if len(ch.DataRates) > 1 { // ignore FSK channels - if _, ok := channels[ch.Frequency]; !ok { // ordering indicates priority - channels[ch.Frequency] = region +var initializeOnce sync.Once + +// InitializeTables initializes the frequency plan and channel tables. +func InitializeTables() { + initializeOnce.Do(func() { + frequencyPlans = make(map[string]FrequencyPlan) + channels = make(map[int]string) + for _, r := range []pb_lorawan.FrequencyPlan{ // ordering is important here + pb_lorawan.FrequencyPlan_EU_863_870, + pb_lorawan.FrequencyPlan_IN_865_867, + pb_lorawan.FrequencyPlan_US_902_928, + pb_lorawan.FrequencyPlan_CN_779_787, + pb_lorawan.FrequencyPlan_EU_433, + pb_lorawan.FrequencyPlan_AS_923, + pb_lorawan.FrequencyPlan_AS_920_923, + pb_lorawan.FrequencyPlan_AS_923_925, + pb_lorawan.FrequencyPlan_KR_920_923, + pb_lorawan.FrequencyPlan_AU_915_928, + pb_lorawan.FrequencyPlan_CN_470_510, + pb_lorawan.FrequencyPlan_RU_864_870, + } { + region := r.String() + frequencyPlans[region], _ = Get(region) + for _, ch := range frequencyPlans[region].UplinkChannels { + if len(ch.DataRates) > 1 { // ignore FSK channels + if _, ok := channels[ch.Frequency]; !ok { // ordering indicates priority + channels[ch.Frequency] = region + } } } } - } + }) } diff --git a/core/band/band_test.go b/core/band/band_test.go index 3bd33d509..577cbc9a8 100644 --- a/core/band/band_test.go +++ b/core/band/band_test.go @@ -12,6 +12,8 @@ import ( func TestGuess(t *testing.T) { a := New(t) + InitializeTables() + a.So(Guess(868100000), ShouldEqual, "EU_863_870") a.So(Guess(903900000), ShouldEqual, "US_902_928") a.So(Guess(779500000), ShouldEqual, "CN_779_787") From 664867496905cdb78a908869d70672e41dee0832 Mon Sep 17 00:00:00 2001 From: Hylke Visser Date: Mon, 18 Feb 2019 10:09:04 +0100 Subject: [PATCH 09/14] Make NetworkServer support ADR payloads for more regions --- core/networkserver/adr.go | 22 ++++++++++++++++++---- 1 file changed, 18 insertions(+), 4 deletions(-) diff --git a/core/networkserver/adr.go b/core/networkserver/adr.go index a6e8f4c07..1e332a060 100644 --- a/core/networkserver/adr.go +++ b/core/networkserver/adr.go @@ -303,11 +303,18 @@ func (n *networkServer) handleDownlinkADR(message *pb_broker.DownlinkMessage, de func getAdrReqPayloads(dev *device.Device, frequencyPlan *band.FrequencyPlan, drIdx int, powerIdx int) []lorawan.LinkADRReqPayload { payloads := []lorawan.LinkADRReqPayload{} switch dev.ADR.Band { - case pb_lorawan.FrequencyPlan_EU_863_870.String(): - if dev.ADR.Failed > 0 && powerIdx > 5 { + + // Frequency plans with three mandatory channels: + case pb_lorawan.FrequencyPlan_EU_863_870.String(), + pb_lorawan.FrequencyPlan_EU_433.String(), + pb_lorawan.FrequencyPlan_KR_920_923.String(), + pb_lorawan.FrequencyPlan_IN_865_867.String(): + + if dev.ADR.Band == pb_lorawan.FrequencyPlan_EU_863_870.String() && dev.ADR.Failed > 0 && powerIdx > 5 { // fall back to txPower 5 for LoRaWAN 1.0 powerIdx = 5 } + payloads = []lorawan.LinkADRReqPayload{ { DataRate: uint8(drIdx), @@ -332,7 +339,12 @@ func getAdrReqPayloads(dev *device.Device, frequencyPlan *band.FrequencyPlan, dr } } } - case pb_lorawan.FrequencyPlan_RU_864_870.String(): + + // Frequency plans with two default channels: + case pb_lorawan.FrequencyPlan_AS_923.String(), + pb_lorawan.FrequencyPlan_AS_920_923.String(), + pb_lorawan.FrequencyPlan_AS_923_925.String(), + pb_lorawan.FrequencyPlan_RU_864_870.String(): payloads = []lorawan.LinkADRReqPayload{ { DataRate: uint8(drIdx), @@ -344,7 +356,7 @@ func getAdrReqPayloads(dev *device.Device, frequencyPlan *band.FrequencyPlan, dr }, } if dev.ADR.Failed > 0 { - // Fall back to the mandatory RU_864_870 LoRaWAN channels + // Fall back to the mandatory LoRaWAN channels payloads[0].ChMask[0] = true payloads[0].ChMask[1] = true } else { @@ -356,6 +368,8 @@ func getAdrReqPayloads(dev *device.Device, frequencyPlan *band.FrequencyPlan, dr } } } + + // Frequency plans with 8 FSBs: case pb_lorawan.FrequencyPlan_US_902_928.String(), pb_lorawan.FrequencyPlan_AU_915_928.String(): var dr500 uint8 switch dev.ADR.Band { From 6f69b7e20249a36e5defaf879ce17bf31aa0f9c2 Mon Sep 17 00:00:00 2001 From: Hylke Visser Date: Mon, 18 Feb 2019 10:09:33 +0100 Subject: [PATCH 10/14] Add ADR config for KR frequency plan --- core/band/band.go | 1 + core/band/band_test.go | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/core/band/band.go b/core/band/band.go index 4d2cc7b52..59c686160 100644 --- a/core/band/band.go +++ b/core/band/band.go @@ -171,6 +171,7 @@ func Get(region string) (frequencyPlan FrequencyPlan, err error) { } frequencyPlan.DownlinkChannels = frequencyPlan.UplinkChannels frequencyPlan.CFList = &lorawan.CFList{922700000, 922900000, 923100000, 923300000, 0} + frequencyPlan.ADR = &ADRConfig{MinDataRate: 0, MaxDataRate: 5, MinTXPower: 2, MaxTXPower: 14, StepTXPower: 2} case pb_lorawan.FrequencyPlan_IN_865_867.String(): frequencyPlan.Band, err = lora.GetConfig(lora.IN_865_867, false, lorawan.DwellTimeNoLimit) case pb_lorawan.FrequencyPlan_RU_864_870.String(): diff --git a/core/band/band_test.go b/core/band/band_test.go index 577cbc9a8..1261a4869 100644 --- a/core/band/band_test.go +++ b/core/band/band_test.go @@ -100,7 +100,7 @@ func TestGet(t *testing.T) { fp, err := Get("KR_920_923") a.So(err, ShouldBeNil) a.So(fp.CFList, ShouldNotBeNil) - a.So(fp.ADR, ShouldBeNil) + a.So(fp.ADR, ShouldNotBeNil) } { From 843ed14e1c183cafeb2ebd419dd417bc487a7187 Mon Sep 17 00:00:00 2001 From: Hylke Visser Date: Mon, 18 Feb 2019 10:20:56 +0100 Subject: [PATCH 11/14] Add ADR unit tests for AS and KR --- core/networkserver/adr_test.go | 86 ++++++++++++++++++++++++++++++++++ 1 file changed, 86 insertions(+) diff --git a/core/networkserver/adr_test.go b/core/networkserver/adr_test.go index 905774ade..49ae599a7 100644 --- a/core/networkserver/adr_test.go +++ b/core/networkserver/adr_test.go @@ -298,6 +298,92 @@ func TestHandleDownlinkADR(t *testing.T) { a.So(payload.ChMask[8], ShouldBeFalse) // 9th channel (FSK) disabled } + ns.Component.Ctx.Info("Start AS ADR Test") + + dev.ADR.DataRate = "SF10BW125" + dev.ADR.TxPower = 10 + + { + dev.ADR.Band = "AS_923" + message := adrInitDownlinkMessage() + err := ns.handleDownlinkADR(message, dev) + a.So(err, ShouldBeNil) + fOpts := message.Message.GetLoRaWAN().GetMACPayload().FOpts + a.So(fOpts, ShouldHaveLength, 2) + a.So(fOpts[1].CID, ShouldEqual, lorawan.LinkADRReq) + payload := new(lorawan.LinkADRReqPayload) + payload.UnmarshalBinary(fOpts[1].Payload) + a.So(payload.DataRate, ShouldEqual, 5) // SF7BW125 + a.So(payload.TXPower, ShouldEqual, 2) // 10 + for i := 0; i < 1; i++ { // First 2 channels enabled + a.So(payload.ChMask[i], ShouldBeTrue) + } + for i := 2; i < 8; i++ { // Next 6 channels disabled + a.So(payload.ChMask[i], ShouldBeFalse) + } + } + + dev.ADR.DataRate = "SF10BW125" + dev.ADR.TxPower = 10 + + { + dev.ADR.Band = "AS_920_923" + message := adrInitDownlinkMessage() + err := ns.handleDownlinkADR(message, dev) + a.So(err, ShouldBeNil) + fOpts := message.Message.GetLoRaWAN().GetMACPayload().FOpts + a.So(fOpts, ShouldHaveLength, 2) + a.So(fOpts[1].CID, ShouldEqual, lorawan.LinkADRReq) + payload := new(lorawan.LinkADRReqPayload) + payload.UnmarshalBinary(fOpts[1].Payload) + a.So(payload.DataRate, ShouldEqual, 5) // SF7BW125 + a.So(payload.TXPower, ShouldEqual, 2) // 10 + for i := 0; i < 8; i++ { // First 8 channels enabled + a.So(payload.ChMask[i], ShouldBeTrue) + } + } + + dev.ADR.DataRate = "SF10BW125" + dev.ADR.TxPower = 10 + + { + dev.ADR.Band = "AS_923_925" + message := adrInitDownlinkMessage() + err := ns.handleDownlinkADR(message, dev) + a.So(err, ShouldBeNil) + fOpts := message.Message.GetLoRaWAN().GetMACPayload().FOpts + a.So(fOpts, ShouldHaveLength, 2) + a.So(fOpts[1].CID, ShouldEqual, lorawan.LinkADRReq) + payload := new(lorawan.LinkADRReqPayload) + payload.UnmarshalBinary(fOpts[1].Payload) + a.So(payload.DataRate, ShouldEqual, 5) // SF7BW125 + a.So(payload.TXPower, ShouldEqual, 2) // 10 + for i := 0; i < 8; i++ { // First 8 channels enabled + a.So(payload.ChMask[i], ShouldBeTrue) + } + } + + dev.ADR.DataRate = "SF10BW125" + dev.ADR.TxPower = 10 + + { + dev.ADR.Band = "KR_920_923" + message := adrInitDownlinkMessage() + err := ns.handleDownlinkADR(message, dev) + a.So(err, ShouldBeNil) + fOpts := message.Message.GetLoRaWAN().GetMACPayload().FOpts + a.So(fOpts, ShouldHaveLength, 2) + a.So(fOpts[1].CID, ShouldEqual, lorawan.LinkADRReq) + payload := new(lorawan.LinkADRReqPayload) + payload.UnmarshalBinary(fOpts[1].Payload) + a.So(payload.DataRate, ShouldEqual, 5) // SF7BW125 + a.So(payload.TXPower, ShouldEqual, 2) // 10 + for i := 0; i < 7; i++ { // First 7 channels enabled + a.So(payload.ChMask[i], ShouldBeTrue) + } + a.So(payload.ChMask[7], ShouldBeFalse) // 8th channel disabled + } + dev.ADR.DataRate = "SF10BW125" dev.ADR.TxPower = 20 From d9310f89a8abef5fa42a3d339eb5c2e534f27a3a Mon Sep 17 00:00:00 2001 From: Hylke Visser Date: Wed, 6 Mar 2019 11:02:22 +0100 Subject: [PATCH 12/14] Remove problematic ADR NbTrans --- core/networkserver/adr.go | 34 ++---------------------- core/networkserver/adr_test.go | 47 ---------------------------------- 2 files changed, 2 insertions(+), 79 deletions(-) diff --git a/core/networkserver/adr.go b/core/networkserver/adr.go index 1e332a060..78ce27174 100644 --- a/core/networkserver/adr.go +++ b/core/networkserver/adr.go @@ -4,7 +4,6 @@ package networkserver import ( - "math" "sort" pb_broker "github.com/TheThingsNetwork/api/broker" @@ -33,15 +32,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 { @@ -224,32 +214,12 @@ func (n *networkServer) setADR(mac *pb_lorawan.MACPayload, dev *device.Device) e 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 { + if dev.ADR.SentInitial && dev.ADR.DataRate == dataRate && dev.ADR.TxPower == txPower && dev.ADR.NbTrans == 1 { ctx.Debug("No ADR needed") return nil // Nothing to do } - dev.ADR.DataRate, dev.ADR.TxPower, dev.ADR.NbTrans = dataRate, txPower, nbTrans + dev.ADR.DataRate, dev.ADR.TxPower, dev.ADR.NbTrans = dataRate, txPower, 1 payloads := getAdrReqPayloads(dev, &fp, drIdx, powerIdx) if len(payloads) == 0 { diff --git a/core/networkserver/adr_test.go b/core/networkserver/adr_test.go index 49ae599a7..48aa0f2a3 100644 --- a/core/networkserver/adr_test.go +++ b/core/networkserver/adr_test.go @@ -71,15 +71,6 @@ func TestMaxSNR(t *testing.T) { a.So(maxSNR(buildFrames(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)), ShouldEqual, 9.8) } -func TestLossPercentage(t *testing.T) { - a := New(t) - a.So(lossPercentage(buildFrames()), ShouldEqual, 0) - a.So(lossPercentage(buildFrames(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)), ShouldEqual, 0) - a.So(lossPercentage(buildFrames(1, 2, 3, 4, 5, 6, 7, 8, 9, 11)), ShouldEqual, 9) // 1/11 missing - a.So(lossPercentage(buildFrames(1, 2, 3, 4, 5, 6, 7, 8, 9, 12)), ShouldEqual, 17) // 2/12 missing - a.So(lossPercentage(buildFrames(1, 2, 3, 6, 7, 8, 9, 12, 13, 14)), ShouldEqual, 29) // 4/14 missing -} - func TestHandleUplinkADR(t *testing.T) { a := New(t) ns := &networkServer{ @@ -405,44 +396,6 @@ func TestHandleDownlinkADR(t *testing.T) { a.So(payload.ChMask[7], ShouldBeFalse) // 8th channel disabled } - shouldHaveNbTrans := func(nbTrans int) { - a := New(t) - message := adrInitDownlinkMessage() - err := ns.handleDownlinkADR(message, dev) - a.So(err, ShouldBeNil) - fOpts := message.Message.GetLoRaWAN().GetMACPayload().FOpts - a.So(fOpts, ShouldHaveLength, 2) - a.So(fOpts[1].CID, ShouldEqual, lorawan.LinkADRReq) - payload := new(lorawan.LinkADRReqPayload) - payload.UnmarshalBinary(fOpts[1].Payload) - a.So(payload.DataRate, ShouldEqual, 5) // SF7BW125 - a.So(payload.TXPower, ShouldEqual, 1) // 14 - a.So(payload.Redundancy.NbRep, ShouldEqual, nbTrans) - if a.Failed() { - _, file, line, _ := runtime.Caller(1) - t.Errorf("\n%s:%d", file, line) - } - } - - tests := map[int]map[int]int{ - 1: map[int]int{0: 1, 1: 1, 2: 1, 4: 2, 10: 3}, - 2: map[int]int{0: 1, 1: 1, 2: 2, 4: 3, 10: 3}, - 3: map[int]int{0: 2, 1: 2, 2: 3, 4: 3, 10: 3}, - } - - for nbTrans, test := range tests { - for loss, exp := range test { - dev.ADR.NbTrans = nbTrans - resetFrames(dev.AppEUI, dev.DevEUI) - history.Push(&device.Frame{SNR: 10, GatewayCount: 3, FCnt: uint32(20 + loss)}) - if nbTrans == exp { - nothingShouldHappen() - } else { - shouldHaveNbTrans(exp) - } - } - } - // Invalid case message = adrInitDownlinkMessage() dev.ADR.DataRate = "INVALID" From 60d50d8d7e7882a806897e5bfb1d3cb709c43334 Mon Sep 17 00:00:00 2001 From: Hylke Visser Date: Wed, 6 Mar 2019 11:28:42 +0100 Subject: [PATCH 13/14] Calculate ADR on uplink, only execute on downlink --- core/networkserver/adr.go | 144 ++++----- core/networkserver/adr_test.go | 500 +++++++++++------------------- core/networkserver/uplink_test.go | 8 +- 3 files changed, 244 insertions(+), 408 deletions(-) diff --git a/core/networkserver/adr.go b/core/networkserver/adr.go index 78ce27174..d9e4e49de 100644 --- a/core/networkserver/adr.go +++ b/core/networkserver/adr.go @@ -65,57 +65,72 @@ 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 + } + 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 { @@ -128,7 +143,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 { @@ -156,71 +171,20 @@ 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) } - if dev.ADR.SentInitial && dev.ADR.DataRate == dataRate && dev.ADR.TxPower == txPower && dev.ADR.NbTrans == 1 { - ctx.Debug("No ADR needed") - return nil // Nothing to do - } - dev.ADR.DataRate, dev.ADR.TxPower, dev.ADR.NbTrans = dataRate, txPower, 1 - payloads := getAdrReqPayloads(dev, &fp, drIdx, powerIdx) if len(payloads) == 0 { ctx.Debug("No ADR payloads") diff --git a/core/networkserver/adr_test.go b/core/networkserver/adr_test.go index 48aa0f2a3..8e620d27e 100644 --- a/core/networkserver/adr_test.go +++ b/core/networkserver/adr_test.go @@ -5,7 +5,6 @@ package networkserver import ( "math" - "runtime" "sort" "testing" @@ -31,7 +30,7 @@ func adrInitUplinkMessage() *pb_broker.DeduplicatedUplinkMessage { downlink.Message.InitLoRaWAN().InitDownlink() message.ProtocolMetadata = pb_protocol.RxMetadata{Protocol: &pb_protocol.RxMetadata_LoRaWAN{ LoRaWAN: &pb_lorawan.Metadata{ - DataRate: "SF8BW125", + DataRate: "SF10BW125", }, }} message.GatewayMetadata = []*pb_gateway.RxMetadata{ @@ -71,333 +70,200 @@ func TestMaxSNR(t *testing.T) { a.So(maxSNR(buildFrames(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)), ShouldEqual, 9.8) } -func TestHandleUplinkADR(t *testing.T) { - a := New(t) +func TestADR(t *testing.T) { ns := &networkServer{ Component: &component.Component{ - Ctx: GetLogger(t, "TestHandleUplink"), + Ctx: GetLogger(t, "TestADR"), }, - devices: device.NewRedisDeviceStore(GetRedisClient(), "ns-test-handle-uplink-adr"), + devices: device.NewRedisDeviceStore(GetRedisClient(), "ns-test-adr"), } ns.InitStatus() defer func() { - keys, _ := GetRedisClient().Keys("*ns-test-handle-uplink-adr*").Result() + keys, _ := GetRedisClient().Keys("*ns-test-adr*").Result() for _, key := range keys { GetRedisClient().Del(key).Result() } }() - appEUI := types.AppEUI([8]byte{1}) - devEUI := types.DevEUI([8]byte{1}) - history, _ := ns.devices.Frames(appEUI, devEUI) - - // Setting ADR to true should start collecting frames - { - dev := &device.Device{AppEUI: appEUI, DevEUI: devEUI} - message := adrInitUplinkMessage() - message.Message.GetLoRaWAN().GetMACPayload().ADR = true - err := ns.handleUplinkADR(message, dev) - a.So(err, ShouldBeNil) - frames, _ := history.Get() - a.So(frames, ShouldHaveLength, 1) - a.So(dev.ADR.DataRate, ShouldEqual, "SF8BW125") - } - - // Resetting ADR to false should empty the frames - { - dev := &device.Device{AppEUI: appEUI, DevEUI: devEUI} - message := adrInitUplinkMessage() - err := ns.handleUplinkADR(message, dev) - a.So(err, ShouldBeNil) - frames, _ := history.Get() - a.So(frames, ShouldBeEmpty) - } - - // Setting ADRAckReq to true should set the ACK and schedule a LinkADRReq - { - dev := &device.Device{AppEUI: appEUI, DevEUI: devEUI} - message := adrInitUplinkMessage() - message.Message.GetLoRaWAN().GetMACPayload().ADR = true - message.Message.GetLoRaWAN().GetMACPayload().ADRAckReq = true - err := ns.handleUplinkADR(message, dev) - a.So(err, ShouldBeNil) - resMAC := message.ResponseTemplate.Message.GetLoRaWAN().GetMACPayload() - a.So(resMAC.Ack, ShouldBeTrue) - a.So(dev.ADR.SendReq, ShouldBeTrue) - } -} - -func TestHandleDownlinkADR(t *testing.T) { - a := New(t) - ns := &networkServer{ - Component: &component.Component{ - Ctx: GetLogger(t, "TestHandleUplink"), + for i, tt := range []struct { + Name string + Band string + DesiredInitialDataRate string + DesiredInitialDataRateIndex int + DesiredInitialTxPower int + DesiredInitialTxPowerIndex int + DesiredDataRate string + DesiredDataRateIndex int + DesiredTxPower int + DesiredTxPowerIndex int + }{ + { + Name: "EU Device", Band: "EU_863_870", + DesiredInitialDataRate: "SF8BW125", DesiredInitialDataRateIndex: 4, + DesiredInitialTxPower: 14, DesiredInitialTxPowerIndex: 1, + DesiredDataRate: "SF7BW125", DesiredDataRateIndex: 5, + DesiredTxPower: 14, DesiredTxPowerIndex: 1, }, - devices: device.NewRedisDeviceStore(GetRedisClient(), "ns-test-handle-downlink-adr"), - } - ns.InitStatus() - - defer func() { - keys, _ := GetRedisClient().Keys("*ns-test-handle-downlink-adr*").Result() - for _, key := range keys { - GetRedisClient().Del(key).Result() - } - }() - - appEUI := types.AppEUI([8]byte{1}) - devEUI := types.DevEUI([8]byte{1}) - history, _ := ns.devices.Frames(appEUI, devEUI) - dev := &device.Device{AppEUI: appEUI, DevEUI: devEUI} - dev.ADR.SentInitial = true - - message := adrInitDownlinkMessage() - - var shouldReturnError = func() { - a := New(t) - message = adrInitDownlinkMessage() - err := ns.handleDownlinkADR(message, dev) - a.So(err, ShouldBeNil) - a.So(message.Message.GetLoRaWAN().GetMACPayload().FOpts, ShouldHaveLength, 1) - if a.Failed() { - _, file, line, _ := runtime.Caller(1) - t.Errorf("\n%s:%d", file, line) - } - } - var nothingShouldHappen = func() { - a := New(t) - message = adrInitDownlinkMessage() - err := ns.handleDownlinkADR(message, dev) - a.So(err, ShouldBeNil) - a.So(message.Message.GetLoRaWAN().GetMACPayload().FOpts, ShouldHaveLength, 1) - if a.Failed() { - _, file, line, _ := runtime.Caller(1) - t.Errorf("\n%s:%d", file, line) - } - } - - // initially - nothingShouldHappen() - - dev.ADR.SendReq = true - nothingShouldHappen() - - var resetFrames = func(appEUI types.AppEUI, devEUI types.DevEUI) { - history.Clear() - for i := 0; i < 20; i++ { - history.Push(&device.Frame{SNR: 10, GatewayCount: 3, FCnt: uint32(i)}) - } - } - resetFrames(dev.AppEUI, dev.DevEUI) - - nothingShouldHappen() - - dev.ADR.DataRate = "SF8BW125" - nothingShouldHappen() - - dev.ADR.Band = "INVALID" - shouldReturnError() - - dev.ADR.DataRate = "SF10BW125" - dev.ADR.TxPower = 20 - - { - dev.ADR.Band = "US_902_928" - message := adrInitDownlinkMessage() - err := ns.handleDownlinkADR(message, dev) - a.So(err, ShouldBeNil) - fOpts := message.Message.GetLoRaWAN().GetMACPayload().FOpts - a.So(fOpts, ShouldHaveLength, 3) - a.So(fOpts[1].CID, ShouldEqual, lorawan.LinkADRReq) - payload := new(lorawan.LinkADRReqPayload) - payload.UnmarshalBinary(fOpts[1].Payload) // First LinkAdrReq - a.So(payload.DataRate, ShouldEqual, 4) // 500kHz channel, so DR4 - a.So(payload.TXPower, ShouldEqual, 0) // Max tx power - a.So(payload.Redundancy.ChMaskCntl, ShouldEqual, 7) - // Ch 64-71, All 125 kHz channels off - a.So(payload.ChMask[0], ShouldBeFalse) // Channel 64 disabled - a.So(payload.ChMask[1], ShouldBeTrue) // Channel 65 enabled - for i := 2; i < 8; i++ { // Channels 66-71 disabled - a.So(payload.ChMask[i], ShouldBeFalse) - } - payload = new(lorawan.LinkADRReqPayload) - payload.UnmarshalBinary(fOpts[2].Payload) // Second LinkAdrReq - a.So(payload.DataRate, ShouldEqual, 3) // SF7BW125 - a.So(payload.TXPower, ShouldEqual, 5) // 20 - a.So(payload.Redundancy.ChMaskCntl, ShouldEqual, 0) // Channels 0..15 - for i := 0; i < 8; i++ { // First 8 channels disabled - a.So(payload.ChMask[i], ShouldBeFalse) - } - for i := 8; i < 16; i++ { // Second 8 channels enabled - a.So(payload.ChMask[i], ShouldBeTrue) - } - } - - dev.ADR.DataRate = "SF10BW125" - dev.ADR.TxPower = 20 - - { - dev.ADR.Band = "AU_915_928" - message := adrInitDownlinkMessage() - err := ns.handleDownlinkADR(message, dev) - a.So(err, ShouldBeNil) - fOpts := message.Message.GetLoRaWAN().GetMACPayload().FOpts - a.So(fOpts, ShouldHaveLength, 3) - a.So(fOpts[1].CID, ShouldEqual, lorawan.LinkADRReq) - payload := new(lorawan.LinkADRReqPayload) - payload.UnmarshalBinary(fOpts[1].Payload) // First LinkAdrReq - a.So(payload.DataRate, ShouldEqual, 6) // 500kHz channel, so DR6 - a.So(payload.TXPower, ShouldEqual, 0) // Max tx power - a.So(payload.Redundancy.ChMaskCntl, ShouldEqual, 7) - // Ch 64-71, All 125 kHz channels off - a.So(payload.ChMask[0], ShouldBeFalse) // Channel 64 disabled - a.So(payload.ChMask[1], ShouldBeTrue) // Channel 65 enabled - for i := 2; i < 8; i++ { // Channels 66-71 disabled - a.So(payload.ChMask[i], ShouldBeFalse) - } - payload = new(lorawan.LinkADRReqPayload) - payload.UnmarshalBinary(fOpts[2].Payload) // Second LinkAdrReq - a.So(payload.DataRate, ShouldEqual, 5) // SF7BW125 - a.So(payload.TXPower, ShouldEqual, 5) // 20 - a.So(payload.Redundancy.ChMaskCntl, ShouldEqual, 0) // Channels 0..15 - for i := 0; i < 8; i++ { // First 8 channels disabled - a.So(payload.ChMask[i], ShouldBeFalse) - } - for i := 8; i < 16; i++ { // Second 8 channels enabled - a.So(payload.ChMask[i], ShouldBeTrue) - } - } - - dev.ADR.DataRate = "SF10BW125" - dev.ADR.TxPower = 20 - - { - dev.ADR.Band = "EU_863_870" - message := adrInitDownlinkMessage() - err := ns.handleDownlinkADR(message, dev) - a.So(err, ShouldBeNil) - fOpts := message.Message.GetLoRaWAN().GetMACPayload().FOpts - a.So(fOpts, ShouldHaveLength, 2) - a.So(fOpts[1].CID, ShouldEqual, lorawan.LinkADRReq) - payload := new(lorawan.LinkADRReqPayload) - payload.UnmarshalBinary(fOpts[1].Payload) - a.So(payload.DataRate, ShouldEqual, 5) // SF7BW125 - a.So(payload.TXPower, ShouldEqual, 1) // 14 - for i := 0; i < 8; i++ { // First 8 channels enabled - a.So(payload.ChMask[i], ShouldBeTrue) - } - a.So(payload.ChMask[8], ShouldBeFalse) // 9th channel (FSK) disabled - } - - ns.Component.Ctx.Info("Start AS ADR Test") - - dev.ADR.DataRate = "SF10BW125" - dev.ADR.TxPower = 10 - - { - dev.ADR.Band = "AS_923" - message := adrInitDownlinkMessage() - err := ns.handleDownlinkADR(message, dev) - a.So(err, ShouldBeNil) - fOpts := message.Message.GetLoRaWAN().GetMACPayload().FOpts - a.So(fOpts, ShouldHaveLength, 2) - a.So(fOpts[1].CID, ShouldEqual, lorawan.LinkADRReq) - payload := new(lorawan.LinkADRReqPayload) - payload.UnmarshalBinary(fOpts[1].Payload) - a.So(payload.DataRate, ShouldEqual, 5) // SF7BW125 - a.So(payload.TXPower, ShouldEqual, 2) // 10 - for i := 0; i < 1; i++ { // First 2 channels enabled - a.So(payload.ChMask[i], ShouldBeTrue) - } - for i := 2; i < 8; i++ { // Next 6 channels disabled - a.So(payload.ChMask[i], ShouldBeFalse) - } - } - - dev.ADR.DataRate = "SF10BW125" - dev.ADR.TxPower = 10 - - { - dev.ADR.Band = "AS_920_923" - message := adrInitDownlinkMessage() - err := ns.handleDownlinkADR(message, dev) - a.So(err, ShouldBeNil) - fOpts := message.Message.GetLoRaWAN().GetMACPayload().FOpts - a.So(fOpts, ShouldHaveLength, 2) - a.So(fOpts[1].CID, ShouldEqual, lorawan.LinkADRReq) - payload := new(lorawan.LinkADRReqPayload) - payload.UnmarshalBinary(fOpts[1].Payload) - a.So(payload.DataRate, ShouldEqual, 5) // SF7BW125 - a.So(payload.TXPower, ShouldEqual, 2) // 10 - for i := 0; i < 8; i++ { // First 8 channels enabled - a.So(payload.ChMask[i], ShouldBeTrue) - } - } - - dev.ADR.DataRate = "SF10BW125" - dev.ADR.TxPower = 10 - - { - dev.ADR.Band = "AS_923_925" - message := adrInitDownlinkMessage() - err := ns.handleDownlinkADR(message, dev) - a.So(err, ShouldBeNil) - fOpts := message.Message.GetLoRaWAN().GetMACPayload().FOpts - a.So(fOpts, ShouldHaveLength, 2) - a.So(fOpts[1].CID, ShouldEqual, lorawan.LinkADRReq) - payload := new(lorawan.LinkADRReqPayload) - payload.UnmarshalBinary(fOpts[1].Payload) - a.So(payload.DataRate, ShouldEqual, 5) // SF7BW125 - a.So(payload.TXPower, ShouldEqual, 2) // 10 - for i := 0; i < 8; i++ { // First 8 channels enabled - a.So(payload.ChMask[i], ShouldBeTrue) - } - } - - dev.ADR.DataRate = "SF10BW125" - dev.ADR.TxPower = 10 - - { - dev.ADR.Band = "KR_920_923" - message := adrInitDownlinkMessage() - err := ns.handleDownlinkADR(message, dev) - a.So(err, ShouldBeNil) - fOpts := message.Message.GetLoRaWAN().GetMACPayload().FOpts - a.So(fOpts, ShouldHaveLength, 2) - a.So(fOpts[1].CID, ShouldEqual, lorawan.LinkADRReq) - payload := new(lorawan.LinkADRReqPayload) - payload.UnmarshalBinary(fOpts[1].Payload) - a.So(payload.DataRate, ShouldEqual, 5) // SF7BW125 - a.So(payload.TXPower, ShouldEqual, 2) // 10 - for i := 0; i < 7; i++ { // First 7 channels enabled - a.So(payload.ChMask[i], ShouldBeTrue) - } - a.So(payload.ChMask[7], ShouldBeFalse) // 8th channel disabled - } - - dev.ADR.DataRate = "SF10BW125" - dev.ADR.TxPower = 20 - - { - dev.ADR.Band = "RU_864_870" - message := adrInitDownlinkMessage() - err := ns.handleDownlinkADR(message, dev) - a.So(err, ShouldBeNil) - fOpts := message.Message.GetLoRaWAN().GetMACPayload().FOpts - a.So(fOpts, ShouldHaveLength, 2) - a.So(fOpts[1].CID, ShouldEqual, lorawan.LinkADRReq) - payload := new(lorawan.LinkADRReqPayload) - payload.UnmarshalBinary(fOpts[1].Payload) - a.So(payload.DataRate, ShouldEqual, 5) // SF7BW125 - a.So(payload.TXPower, ShouldEqual, 1) // 14 - for i := 0; i < 7; i++ { // First 7 channels enabled - a.So(payload.ChMask[i], ShouldBeTrue) - } - a.So(payload.ChMask[7], ShouldBeFalse) // 8th channel disabled + { + Name: "AS Device", Band: "AS_923", + DesiredInitialDataRate: "SF8BW125", DesiredInitialDataRateIndex: 4, + DesiredInitialTxPower: 14, DesiredInitialTxPowerIndex: 0, + DesiredDataRate: "SF7BW125", DesiredDataRateIndex: 5, + DesiredTxPower: 14, DesiredTxPowerIndex: 0, + }, + { + Name: "AS1 Device", Band: "AS_920_923", + DesiredInitialDataRate: "SF8BW125", DesiredInitialDataRateIndex: 4, + DesiredInitialTxPower: 14, DesiredInitialTxPowerIndex: 0, + DesiredDataRate: "SF7BW125", DesiredDataRateIndex: 5, + DesiredTxPower: 14, DesiredTxPowerIndex: 0, + }, + { + Name: "AS2 Device", Band: "AS_923_925", + DesiredInitialDataRate: "SF8BW125", DesiredInitialDataRateIndex: 4, + DesiredInitialTxPower: 14, DesiredInitialTxPowerIndex: 0, + DesiredDataRate: "SF7BW125", DesiredDataRateIndex: 5, + DesiredTxPower: 14, DesiredTxPowerIndex: 0, + }, + { + Name: "KR Device", Band: "KR_920_923", + DesiredInitialDataRate: "SF8BW125", DesiredInitialDataRateIndex: 4, + DesiredInitialTxPower: 14, DesiredInitialTxPowerIndex: 1, + DesiredDataRate: "SF7BW125", DesiredDataRateIndex: 5, + DesiredTxPower: 14, DesiredTxPowerIndex: 1, + }, + { + Name: "RU Device", Band: "RU_864_870", + DesiredInitialDataRate: "SF8BW125", DesiredInitialDataRateIndex: 4, + DesiredInitialTxPower: 14, DesiredInitialTxPowerIndex: 1, + DesiredDataRate: "SF7BW125", DesiredDataRateIndex: 5, + DesiredTxPower: 14, DesiredTxPowerIndex: 1, + }, + { + Name: "US Device", Band: "US_902_928", + DesiredInitialDataRate: "SF8BW125", DesiredInitialDataRateIndex: 2, + DesiredInitialTxPower: 20, DesiredInitialTxPowerIndex: 5, + DesiredDataRate: "SF7BW125", DesiredDataRateIndex: 3, + DesiredTxPower: 20, DesiredTxPowerIndex: 5, + }, + { + Name: "AU Device", Band: "AU_915_928", + DesiredInitialDataRate: "SF8BW125", DesiredInitialDataRateIndex: 4, + DesiredInitialTxPower: 20, DesiredInitialTxPowerIndex: 5, + DesiredDataRate: "SF7BW125", DesiredDataRateIndex: 5, + DesiredTxPower: 20, DesiredTxPowerIndex: 5, + }, + } { + t.Run(tt.Name, func(t *testing.T) { + a := New(t) + + appEUI := types.AppEUI([8]byte{1}) + devEUI := types.DevEUI([8]byte{1, uint8(i)}) + dev := &device.Device{AppEUI: appEUI, DevEUI: devEUI} + + history, _ := ns.devices.Frames(appEUI, devEUI) + + uplink, downlink := adrInitUplinkMessage(), adrInitDownlinkMessage() + uplink.ProtocolMetadata.GetLoRaWAN().FrequencyPlan = pb_lorawan.FrequencyPlan(pb_lorawan.FrequencyPlan_value[tt.Band]) + + err := ns.handleUplinkADR(uplink, dev) + a.So(err, ShouldBeNil) + a.So(dev.ADR.SendReq, ShouldBeFalse) + + uplink.Message.GetLoRaWAN().GetMACPayload().ADR = true + + err = ns.handleUplinkADR(uplink, dev) + a.So(err, ShouldBeNil) + a.So(dev.ADR.SendReq, ShouldBeTrue) + a.So(dev.ADR.DataRate, ShouldEqual, tt.DesiredInitialDataRate) + a.So(dev.ADR.TxPower, ShouldEqual, tt.DesiredInitialTxPower) + + frames, err := history.Get() + a.So(err, ShouldBeNil) + a.So(frames, ShouldHaveLength, 1) + + err = ns.handleDownlinkADR(downlink, dev) + a.So(err, ShouldBeNil) + + a.So(dev.ADR.SentInitial, ShouldBeTrue) + + fOpts := downlink.Message.GetLoRaWAN().GetMACPayload().FOpts + fOpt := fOpts[1] + if tt.Band == "US_902_928" || tt.Band == "AU_915_928" { + fOpt = fOpts[2] + + var req lorawan.LinkADRReqPayload + req.UnmarshalBinary(fOpts[1].Payload) + + switch tt.Band { + case "US_902_928": + a.So(req.DataRate, ShouldEqual, 4) // 500kHz channel, so DR4 + case "AU_915_928": + a.So(req.DataRate, ShouldEqual, 6) // 500kHz channel, so DR6 + } + + a.So(req.TXPower, ShouldEqual, 0) // Max tx power + a.So(req.Redundancy.ChMaskCntl, ShouldEqual, 7) // Ch 64-71, All 125 kHz channels off + a.So(req.ChMask[0], ShouldBeFalse) // Channel 64 disabled + a.So(req.ChMask[1], ShouldBeTrue) // Channel 65 enabled + for i := 2; i < 8; i++ { // Channels 66-71 disabled + a.So(req.ChMask[i], ShouldBeFalse) + } + } + + a.So(fOpt.CID, ShouldEqual, lorawan.LinkADRReq) + var req lorawan.LinkADRReqPayload + req.UnmarshalBinary(fOpt.Payload) + + a.So(req.DataRate, ShouldEqual, tt.DesiredInitialDataRateIndex) + a.So(req.TXPower, ShouldEqual, tt.DesiredInitialTxPowerIndex) + + if tt.Band == "US_902_928" || tt.Band == "AU_915_928" { + a.So(req.Redundancy.ChMaskCntl, ShouldEqual, 0) // Channels 0..15 + for i := 0; i < 8; i++ { // First 8 channels disabled + a.So(req.ChMask[i], ShouldBeFalse) + } + for i := 8; i < 16; i++ { // Second 8 channels enabled + a.So(req.ChMask[i], ShouldBeTrue) + } + } + + for i := 0; i < 20; i++ { + uplink.Message.GetLoRaWAN().GetMACPayload().FCnt++ + ns.handleUplinkADR(uplink, dev) + } + + frames, err = history.Get() + a.So(err, ShouldBeNil) + a.So(frames, ShouldHaveLength, 20) + + a.So(dev.ADR.SendReq, ShouldBeTrue) + a.So(dev.ADR.DataRate, ShouldEqual, tt.DesiredDataRate) + a.So(dev.ADR.TxPower, ShouldEqual, tt.DesiredTxPower) + + err = ns.handleDownlinkADR(downlink, dev) + a.So(err, ShouldBeNil) + + fOpts = downlink.Message.GetLoRaWAN().GetMACPayload().FOpts + fOpt = fOpts[1] + if tt.Band == "US_902_928" || tt.Band == "AU_915_928" { + fOpt = fOpts[2] + } + + a.So(fOpt.CID, ShouldEqual, lorawan.LinkADRReq) + req.UnmarshalBinary(fOpt.Payload) + + a.So(req.DataRate, ShouldEqual, tt.DesiredDataRateIndex) + a.So(req.TXPower, ShouldEqual, tt.DesiredTxPowerIndex) + + uplink.ProtocolMetadata.GetLoRaWAN().DataRate = tt.DesiredDataRate + uplink.GatewayMetadata[0].SNR = 7.5 + + err = ns.handleUplinkADR(uplink, dev) + a.So(err, ShouldBeNil) + + a.So(dev.ADR.SendReq, ShouldBeFalse) + }) } - - // Invalid case - message = adrInitDownlinkMessage() - dev.ADR.DataRate = "INVALID" - shouldReturnError() } diff --git a/core/networkserver/uplink_test.go b/core/networkserver/uplink_test.go index c80a797d5..334223f7b 100644 --- a/core/networkserver/uplink_test.go +++ b/core/networkserver/uplink_test.go @@ -112,8 +112,14 @@ func TestHandleUplink(t *testing.T) { // ResponseTemplate should ACK the ADRACKReq a.So(macPayload.FHDR.FCtrl.ACK, ShouldBeTrue) - a.So(macPayload.FHDR.FOpts, ShouldHaveLength, 1) + a.So(macPayload.FHDR.FOpts, ShouldHaveLength, 2) a.So(macPayload.FHDR.FOpts[0].Payload, ShouldResemble, &lorawan.LinkCheckAnsPayload{GwCnt: 1, Margin: 7}) + a.So(macPayload.FHDR.FOpts[1].Payload, ShouldResemble, &lorawan.LinkADRReqPayload{ + DataRate: 5, + TXPower: 1, + ChMask: [16]bool{true, true, true, true, true, true, true, true}, + Redundancy: lorawan.Redundancy{NbRep: 1}, + }) // Frame Counter should have been updated dev, _ := ns.devices.Get(appEUI, devEUI) From f57e96559a1097243765b3c6fb3f7797ffc0a03c Mon Sep 17 00:00:00 2001 From: Hylke Visser Date: Wed, 6 Mar 2019 11:57:37 +0100 Subject: [PATCH 14/14] Add NS flag to force ADR optimizations --- cmd/docs/README.md | 1 + cmd/networkserver.go | 3 +++ core/networkserver/adr.go | 3 +++ 3 files changed, 7 insertions(+) diff --git a/cmd/docs/README.md b/cmd/docs/README.md index 5ef8c4264..d9287aa8f 100644 --- a/cmd/docs/README.md +++ b/cmd/docs/README.md @@ -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 diff --git a/cmd/networkserver.go b/cmd/networkserver.go index d91d20a84..6095b0964 100644 --- a/cmd/networkserver.go +++ b/cmd/networkserver.go @@ -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") diff --git a/core/networkserver/adr.go b/core/networkserver/adr.go index d9e4e49de..fd3e64926 100644 --- a/core/networkserver/adr.go +++ b/core/networkserver/adr.go @@ -14,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 @@ -120,6 +121,8 @@ func (n *networkServer) handleUplinkADR(message *pb_broker.DeduplicatedUplinkMes 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)