Skip to content

Commit

Permalink
Use uint32 for freq. values to support 2.4 GHz freqs on 32 bit.
Browse files Browse the repository at this point in the history
Previously the int type was used which would result in compile errors
on 32bit platforms since LoRaWAN 2.4 GHz frequency values do not fit
within int32 type (signed).
  • Loading branch information
brocaar committed May 7, 2021
1 parent 1afc204 commit 6b40ba3
Show file tree
Hide file tree
Showing 28 changed files with 87 additions and 97 deletions.
2 changes: 1 addition & 1 deletion cmd/chirpstack-network-server/cmd/root_run.go
Original file line number Diff line number Diff line change
Expand Up @@ -130,7 +130,7 @@ func setRXParameters() error {
}

if config.C.NetworkServer.NetworkSettings.RX2Frequency == -1 {
config.C.NetworkServer.NetworkSettings.RX2Frequency = defaults.RX2Frequency
config.C.NetworkServer.NetworkSettings.RX2Frequency = int64(defaults.RX2Frequency)
}

return nil
Expand Down
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ require (
github.com/Azure/azure-service-bus-go v0.9.1
github.com/NickBall/go-aes-key-wrap v0.0.0-20170929221519-1c3aa3e4dfc5
github.com/brocaar/chirpstack-api/go/v3 v3.9.8
github.com/brocaar/lorawan v0.0.0-20210506120432-7405abb3269b
github.com/brocaar/lorawan v0.0.0-20210507123736-8a8cd6f95de0
github.com/eclipse/paho.mqtt.golang v1.2.0
github.com/go-redis/redis/v7 v7.4.0
github.com/gofrs/uuid v3.2.0+incompatible
Expand Down
4 changes: 2 additions & 2 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -86,8 +86,8 @@ github.com/blakesmith/ar v0.0.0-20150311145944-8bd4349a67f2/go.mod h1:PkYb9DJNAw
github.com/bmizerany/assert v0.0.0-20160611221934-b7ed37b82869/go.mod h1:Ekp36dRnpXw/yCqJaO+ZrUyxD+3VXMFFr56k5XYrpB4=
github.com/brocaar/chirpstack-api/go/v3 v3.9.8 h1:3F1rXmmIRT4YopnUYaejQOxGKN9X3+Ccl7EvRQlj4Dg=
github.com/brocaar/chirpstack-api/go/v3 v3.9.8/go.mod h1:v8AWP19nOJK4rwJsr1+weDfpUc4UNLbRh8Eygn4Oh00=
github.com/brocaar/lorawan v0.0.0-20210506120432-7405abb3269b h1:juWCilf1JCInfc/QnR945i3J6JVnWkYaqMPC5pWxelM=
github.com/brocaar/lorawan v0.0.0-20210506120432-7405abb3269b/go.mod h1:Jj/KGGdNUJX02HJKVGvfwi9rcTFm27Z8FVB2b1w74bE=
github.com/brocaar/lorawan v0.0.0-20210507123736-8a8cd6f95de0 h1:XQUYzxYPJXUjJtYV+bU8jv3pS5eyeebzAvgtSmvP30o=
github.com/brocaar/lorawan v0.0.0-20210507123736-8a8cd6f95de0/go.mod h1:Jj/KGGdNUJX02HJKVGvfwi9rcTFm27Z8FVB2b1w74bE=
github.com/caarlos0/ctrlc v1.0.0 h1:2DtF8GSIcajgffDFJzyG15vO+1PuBWOMUdFut7NnXhw=
github.com/caarlos0/ctrlc v1.0.0/go.mod h1:CdXpj4rmq0q/1Eb44M9zi2nKB0QraNKuRGYGrrHhcQw=
github.com/campoy/unique v0.0.0-20180121183637-88950e537e7e h1:V9a67dfYqPLAvzk5hMQOXYJlZ4SLIXgyKIE+ZiHzgGQ=
Expand Down
28 changes: 9 additions & 19 deletions internal/api/ns/network_server.go
Original file line number Diff line number Diff line change
Expand Up @@ -343,27 +343,22 @@ func (n *NetworkServerAPI) CreateDeviceProfile(ctx context.Context, req *ns.Crea
var dpID uuid.UUID
copy(dpID[:], req.DeviceProfile.Id)

var factoryPresetFreqs []int
for _, f := range req.DeviceProfile.FactoryPresetFreqs {
factoryPresetFreqs = append(factoryPresetFreqs, int(f))
}

dp := storage.DeviceProfile{
ID: dpID,
SupportsClassB: req.DeviceProfile.SupportsClassB,
ClassBTimeout: int(req.DeviceProfile.ClassBTimeout),
PingSlotPeriod: int(req.DeviceProfile.PingSlotPeriod),
PingSlotDR: int(req.DeviceProfile.PingSlotDr),
PingSlotFreq: int(req.DeviceProfile.PingSlotFreq),
PingSlotFreq: req.DeviceProfile.PingSlotFreq,
SupportsClassC: req.DeviceProfile.SupportsClassC,
ClassCTimeout: int(req.DeviceProfile.ClassCTimeout),
MACVersion: req.DeviceProfile.MacVersion,
RegParamsRevision: req.DeviceProfile.RegParamsRevision,
RXDelay1: int(req.DeviceProfile.RxDelay_1),
RXDROffset1: int(req.DeviceProfile.RxDrOffset_1),
RXDataRate2: int(req.DeviceProfile.RxDatarate_2),
RXFreq2: int(req.DeviceProfile.RxFreq_2),
FactoryPresetFreqs: factoryPresetFreqs,
RXFreq2: req.DeviceProfile.RxFreq_2,
FactoryPresetFreqs: req.DeviceProfile.FactoryPresetFreqs,
MaxEIRP: int(req.DeviceProfile.MaxEirp),
MaxDutyCycle: int(req.DeviceProfile.MaxDutyCycle),
SupportsJoin: req.DeviceProfile.SupportsJoin,
Expand Down Expand Up @@ -450,25 +445,20 @@ func (n *NetworkServerAPI) UpdateDeviceProfile(ctx context.Context, req *ns.Upda
return nil, errToRPCError(err)
}

var factoryPresetFreqs []int
for _, f := range req.DeviceProfile.FactoryPresetFreqs {
factoryPresetFreqs = append(factoryPresetFreqs, int(f))
}

dp.SupportsClassB = req.DeviceProfile.SupportsClassB
dp.ClassBTimeout = int(req.DeviceProfile.ClassBTimeout)
dp.PingSlotPeriod = int(req.DeviceProfile.PingSlotPeriod)
dp.PingSlotDR = int(req.DeviceProfile.PingSlotDr)
dp.PingSlotFreq = int(req.DeviceProfile.PingSlotFreq)
dp.PingSlotFreq = req.DeviceProfile.PingSlotFreq
dp.SupportsClassC = req.DeviceProfile.SupportsClassC
dp.ClassCTimeout = int(req.DeviceProfile.ClassCTimeout)
dp.MACVersion = req.DeviceProfile.MacVersion
dp.RegParamsRevision = req.DeviceProfile.RegParamsRevision
dp.RXDelay1 = int(req.DeviceProfile.RxDelay_1)
dp.RXDROffset1 = int(req.DeviceProfile.RxDrOffset_1)
dp.RXDataRate2 = int(req.DeviceProfile.RxDatarate_2)
dp.RXFreq2 = int(req.DeviceProfile.RxFreq_2)
dp.FactoryPresetFreqs = factoryPresetFreqs
dp.RXFreq2 = req.DeviceProfile.RxFreq_2
dp.FactoryPresetFreqs = req.DeviceProfile.FactoryPresetFreqs
dp.MaxEIRP = int(req.DeviceProfile.MaxEirp)
dp.MaxDutyCycle = int(req.DeviceProfile.MaxDutyCycle)
dp.SupportsJoin = req.DeviceProfile.SupportsJoin
Expand Down Expand Up @@ -811,7 +801,7 @@ func (n *NetworkServerAPI) SendProprietaryPayload(ctx context.Context, req *ns.S
gwIDs = append(gwIDs, id)
}

err := proprietarydown.Handle(ctx, req.MacPayload, mic, gwIDs, req.PolarizationInversion, int(req.Frequency), int(req.Dr))
err := proprietarydown.Handle(ctx, req.MacPayload, mic, gwIDs, req.PolarizationInversion, req.Frequency, int(req.Dr))
if err != nil {
return nil, errToRPCError(err)
}
Expand Down Expand Up @@ -1510,7 +1500,7 @@ func (n *NetworkServerAPI) CreateMulticastGroup(ctx context.Context, req *ns.Cre
mg := storage.MulticastGroup{
FCnt: req.MulticastGroup.FCnt,
DR: int(req.MulticastGroup.Dr),
Frequency: int(req.MulticastGroup.Frequency),
Frequency: req.MulticastGroup.Frequency,
PingSlotPeriod: int(req.MulticastGroup.PingSlotPeriod),
}

Expand Down Expand Up @@ -1605,7 +1595,7 @@ func (n *NetworkServerAPI) UpdateMulticastGroup(ctx context.Context, req *ns.Upd
copy(mg.RoutingProfileID[:], req.MulticastGroup.RoutingProfileId)
mg.FCnt = req.MulticastGroup.FCnt
mg.DR = int(req.MulticastGroup.Dr)
mg.Frequency = int(req.MulticastGroup.Frequency)
mg.Frequency = req.MulticastGroup.Frequency
mg.PingSlotPeriod = int(req.MulticastGroup.PingSlotPeriod)

switch req.MulticastGroup.GroupType {
Expand Down
4 changes: 2 additions & 2 deletions internal/api/ns/network_server_new_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -386,7 +386,7 @@ func (ts *NetworkServerAPITestSuite) TestDevice() {
assert.NoError(storage.CreateServiceProfile(context.Background(), storage.DB(), &sp))

dp := storage.DeviceProfile{
FactoryPresetFreqs: []int{
FactoryPresetFreqs: []uint32{
868100000,
868300000,
868500000,
Expand Down Expand Up @@ -567,7 +567,7 @@ func (ts *NetworkServerAPITestSuite) TestDevice() {
AFCntDown: 12,
SkipFCntValidation: true,
EnabledUplinkChannels: []int{0, 1, 2, 3},
ChannelFrequencies: []int{868100000, 868300000, 868500000, 867100000},
ChannelFrequencies: []uint32{868100000, 868300000, 868500000, 867100000},
ExtraUplinkChannels: map[int]loraband.Channel{
3: loraband.Channel{
Frequency: 867100000,
Expand Down
2 changes: 1 addition & 1 deletion internal/api/ns/network_server_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -419,7 +419,7 @@ func TestNetworkServerAPI(t *testing.T) {
So(storage.CreateRoutingProfile(context.Background(), storage.DB(), &rp), ShouldBeNil)

dp := storage.DeviceProfile{
FactoryPresetFreqs: []int{
FactoryPresetFreqs: []uint32{
868100000,
868300000,
868500000,
Expand Down
4 changes: 2 additions & 2 deletions internal/api/roaming/roaming.go
Original file line number Diff line number Diff line change
Expand Up @@ -336,9 +336,9 @@ func (a *API) handlePRStartReqData(ctx context.Context, basePL backend.BasePaylo
}

// frequency in hz
var freq int
var freq uint32
if pl.ULMetaData.ULFreq != nil {
freq = int(*pl.ULMetaData.ULFreq * 1000000)
freq = uint32(*pl.ULMetaData.ULFreq * 1000000)
}

// data-rate
Expand Down
12 changes: 6 additions & 6 deletions internal/config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ type Config struct {
RX1Delay int `mapstructure:"rx1_delay"`
RX1DROffset int `mapstructure:"rx1_dr_offset"`
RX2DR int `mapstructure:"rx2_dr"`
RX2Frequency int `mapstructure:"rx2_frequency"`
RX2Frequency int64 `mapstructure:"rx2_frequency"`
RX2PreferOnRX1DRLt int `mapstructure:"rx2_prefer_on_rx1_dr_lt"`
RX2PreferOnLinkBudget bool `mapstructure:"rx2_prefer_on_link_budget"`
GatewayPreferMinMargin float64 `mapstructure:"gateway_prefer_min_margin"`
Expand All @@ -71,14 +71,14 @@ type Config struct {
ADRPlugins []string `mapstructure:"adr_plugins"`

ExtraChannels []struct {
Frequency int `mapstructure:"frequency"`
MinDR int `mapstructure:"min_dr"`
MaxDR int `mapstructure:"max_dr"`
Frequency uint32 `mapstructure:"frequency"`
MinDR int `mapstructure:"min_dr"`
MaxDR int `mapstructure:"max_dr"`
} `mapstructure:"extra_channels"`

ClassB struct {
PingSlotDR int `mapstructure:"ping_slot_dr"`
PingSlotFrequency int `mapstructure:"ping_slot_frequency"`
PingSlotDR int `mapstructure:"ping_slot_dr"`
PingSlotFrequency uint32 `mapstructure:"ping_slot_frequency"`
} `mapstructure:"class_b"`

RejoinRequest struct {
Expand Down
20 changes: 10 additions & 10 deletions internal/downlink/data/data.go
Original file line number Diff line number Diff line change
Expand Up @@ -60,15 +60,15 @@ var (

// Class-B
classBPingSlotDR int
classBPingSlotFrequency int
classBPingSlotFrequency uint32

// RX window
rxWindow int
rx2PreferOnRX1DRLt int
rx2PreferOnLinkBudget bool

// RX2 params
rx2Frequency int
rx2Frequency uint32
rx2DR int

// RX1 params
Expand Down Expand Up @@ -173,7 +173,7 @@ func Setup(conf config.Config) error {
classBPingSlotDR = nsConf.ClassB.PingSlotDR
classBPingSlotFrequency = nsConf.ClassB.PingSlotFrequency

rx2Frequency = nsConf.RX2Frequency
rx2Frequency = uint32(nsConf.RX2Frequency)
rx2DR = nsConf.RX2DR
rx1DROffset = nsConf.RX1DROffset
rx1Delay = nsConf.RX1Delay
Expand Down Expand Up @@ -510,7 +510,7 @@ func preferRX2LinkBudget(ctx *dataContext) (b bool, err error) {
}

// get RX1 and RX2 freq
rx1Freq, err := band.Band().GetRX1FrequencyForUplinkFrequency(int(ctx.RXPacket.TXInfo.GetFrequency()))
rx1Freq, err := band.Band().GetRX1FrequencyForUplinkFrequency(ctx.RXPacket.TXInfo.GetFrequency())
if err != nil {
return false, errors.Wrap(err, "get rx1 frequency for uplink frequency error")
}
Expand Down Expand Up @@ -606,11 +606,11 @@ func setTXInfoForRX1(ctx *dataContext) error {
}

// get rx1 frequency
freq, err := band.Band().GetRX1FrequencyForUplinkFrequency(int(ctx.RXPacket.TXInfo.Frequency))
freq, err := band.Band().GetRX1FrequencyForUplinkFrequency(ctx.RXPacket.TXInfo.Frequency)
if err != nil {
return errors.Wrap(err, "get rx1 frequency error")
}
txInfo.Frequency = uint32(freq)
txInfo.Frequency = freq

// get timestamp
delay := band.Band().GetDefaults().ReceiveDelay1
Expand All @@ -628,7 +628,7 @@ func setTXInfoForRX1(ctx *dataContext) error {
if downlinkTXPower != -1 {
txInfo.Power = int32(downlinkTXPower)
} else {
txInfo.Power = int32(band.Band().GetDownlinkTXPower(int(txInfo.Frequency)))
txInfo.Power = int32(band.Band().GetDownlinkTXPower(txInfo.Frequency))
}

// get remaining payload size
Expand Down Expand Up @@ -670,7 +670,7 @@ func setTXInfoForRX2(ctx *dataContext) error {
if downlinkTXPower != -1 {
txInfo.Power = int32(downlinkTXPower)
} else {
txInfo.Power = int32(band.Band().GetDownlinkTXPower(int(txInfo.Frequency)))
txInfo.Power = int32(band.Band().GetDownlinkTXPower(txInfo.Frequency))
}

// get timestamp (when not tx immediately)
Expand Down Expand Up @@ -728,7 +728,7 @@ func setTXInfoForClassB(ctx *dataContext) error {
if downlinkTXPower != -1 {
txInfo.Power = int32(downlinkTXPower)
} else {
txInfo.Power = int32(band.Band().GetDownlinkTXPower(int(txInfo.Frequency)))
txInfo.Power = int32(band.Band().GetDownlinkTXPower(txInfo.Frequency))
}

// get remaining payload size
Expand Down Expand Up @@ -1443,7 +1443,7 @@ func sendDownlinkFramePassiveRoaming(ctx *dataContext) error {
}

// DLFreq1
dlFreq1, err := band.Band().GetRX1FrequencyForUplinkFrequency(int(ctx.RXPacket.TXInfo.Frequency))
dlFreq1, err := band.Band().GetRX1FrequencyForUplinkFrequency(ctx.RXPacket.TXInfo.Frequency)
if err != nil {
return errors.Wrap(err, "get rx1 frequency error")
}
Expand Down
4 changes: 2 additions & 2 deletions internal/downlink/data/data_roaming_fns.go
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ func HandleRoamingFNS(ctx context.Context, pl backend.XmitDataReqPayload) error
},
}

item.TxInfo.Power = int32(band.Band().GetDownlinkTXPower(int(item.TxInfo.Frequency)))
item.TxInfo.Power = int32(band.Band().GetDownlinkTXPower(item.TxInfo.Frequency))

if err := helpers.SetDownlinkTXInfoDataRate(item.TxInfo, *pl.DLMetaData.DataRate1, band.Band()); err != nil {
return errors.Wrap(err, "set downlink txinfo data-rate error")
Expand All @@ -88,7 +88,7 @@ func HandleRoamingFNS(ctx context.Context, pl backend.XmitDataReqPayload) error
},
}

item.TxInfo.Power = int32(band.Band().GetDownlinkTXPower(int(item.TxInfo.Frequency)))
item.TxInfo.Power = int32(band.Band().GetDownlinkTXPower(item.TxInfo.Frequency))

if err := helpers.SetDownlinkTXInfoDataRate(item.TxInfo, *pl.DLMetaData.DataRate2, band.Band()); err != nil {
return errors.Wrap(err, "set downlink txinfo data-rate error")
Expand Down
8 changes: 4 additions & 4 deletions internal/downlink/join/join.go
Original file line number Diff line number Diff line change
Expand Up @@ -145,7 +145,7 @@ func setTXInfoForRX1(ctx *joinContext) error {
}

// set frequency
freq, err := band.Band().GetRX1FrequencyForUplinkFrequency(int(ctx.RXPacket.TXInfo.Frequency))
freq, err := band.Band().GetRX1FrequencyForUplinkFrequency(ctx.RXPacket.TXInfo.Frequency)
if err != nil {
return errors.Wrap(err, "get rx1 frequency error")
}
Expand All @@ -155,7 +155,7 @@ func setTXInfoForRX1(ctx *joinContext) error {
if downlinkTXPower != -1 {
txInfo.Power = int32(downlinkTXPower)
} else {
txInfo.Power = int32(band.Band().GetDownlinkTXPower(int(txInfo.Frequency)))
txInfo.Power = int32(band.Band().GetDownlinkTXPower(txInfo.Frequency))
}

// set timestamp
Expand All @@ -178,7 +178,7 @@ func setTXInfoForRX2(ctx *joinContext) error {
txInfo := gw.DownlinkTXInfo{
Board: ctx.DownlinkGateway.Board,
Antenna: ctx.DownlinkGateway.Antenna,
Frequency: uint32(band.Band().GetDefaults().RX2Frequency),
Frequency: band.Band().GetDefaults().RX2Frequency,
Context: ctx.DownlinkGateway.Context,
}

Expand All @@ -192,7 +192,7 @@ func setTXInfoForRX2(ctx *joinContext) error {
if downlinkTXPower != -1 {
txInfo.Power = int32(downlinkTXPower)
} else {
txInfo.Power = int32(band.Band().GetDownlinkTXPower(int(txInfo.Frequency)))
txInfo.Power = int32(band.Band().GetDownlinkTXPower(txInfo.Frequency))
}

// set timestamp
Expand Down
2 changes: 1 addition & 1 deletion internal/downlink/multicast/multicast.go
Original file line number Diff line number Diff line change
Expand Up @@ -146,7 +146,7 @@ func setTXInfo(ctx *multicastContext) error {
ctx.DownlinkFrame.GatewayId = ctx.MulticastQueueItem.GatewayID[:]

txInfo := gw.DownlinkTXInfo{
Frequency: uint32(ctx.MulticastGroup.Frequency),
Frequency: ctx.MulticastGroup.Frequency,
}

if ctx.MulticastQueueItem.EmitAtTimeSinceGPSEpoch == nil {
Expand Down
6 changes: 3 additions & 3 deletions internal/downlink/proprietary/proprietary.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ type proprietaryContext struct {
MIC lorawan.MIC
GatewayMACs []lorawan.EUI64
IPol bool
Frequency int
Frequency uint32
DR int
DownlinkFrames []gw.DownlinkFrame
}
Expand All @@ -48,7 +48,7 @@ func Setup(conf config.Config) error {
}

// Handle handles a proprietary downlink.
func Handle(ctx context.Context, macPayload []byte, mic lorawan.MIC, gwMACs []lorawan.EUI64, iPol bool, frequency, dr int) error {
func Handle(ctx context.Context, macPayload []byte, mic lorawan.MIC, gwMACs []lorawan.EUI64, iPol bool, frequency uint32, dr int) error {
pctx := proprietaryContext{
ctx: ctx,
MACPayload: macPayload,
Expand Down Expand Up @@ -97,7 +97,7 @@ func sendProprietaryDown(ctx *proprietaryContext) error {
token := binary.BigEndian.Uint16(downID[0:2])

txInfo := gw.DownlinkTXInfo{
Frequency: uint32(ctx.Frequency),
Frequency: ctx.Frequency,
Power: int32(txPower),

Timing: gw.DownlinkTiming_IMMEDIATELY,
Expand Down
4 changes: 2 additions & 2 deletions internal/downlink/roaming/roaming.go
Original file line number Diff line number Diff line change
Expand Up @@ -113,7 +113,7 @@ func (ctx *emitPRDownlinkContext) setDownlinkFrame() error {
},
}

item.TxInfo.Power = int32(band.Band().GetDownlinkTXPower(int(item.TxInfo.Frequency)))
item.TxInfo.Power = int32(band.Band().GetDownlinkTXPower(item.TxInfo.Frequency))
if err := helpers.SetDownlinkTXInfoDataRate(item.TxInfo, *ctx.dlMetaData.DataRate1, band.Band()); err != nil {
return errors.Wrap(err, "set txinfo data-rate error")
}
Expand All @@ -136,7 +136,7 @@ func (ctx *emitPRDownlinkContext) setDownlinkFrame() error {
},
}

item.TxInfo.Power = int32(band.Band().GetDownlinkTXPower(int(item.TxInfo.Frequency)))
item.TxInfo.Power = int32(band.Band().GetDownlinkTXPower(item.TxInfo.Frequency))
if err := helpers.SetDownlinkTXInfoDataRate(item.TxInfo, *ctx.dlMetaData.DataRate2, band.Band()); err != nil {
return errors.Wrap(err, "set txinfo data-rate error")
}
Expand Down
2 changes: 1 addition & 1 deletion internal/maccommand/new_channel.go
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,7 @@ func handleNewChannelAns(ctx context.Context, ds *storage.DeviceSession, block s
delete(ds.MACCommandErrorCount, lorawan.NewChannelAns)

ds.ExtraUplinkChannels[int(pendingPL.ChIndex)] = band.Channel{
Frequency: int(pendingPL.Freq),
Frequency: pendingPL.Freq,
MinDR: int(pendingPL.MinDR),
MaxDR: int(pendingPL.MaxDR),
}
Expand Down
Loading

0 comments on commit 6b40ba3

Please sign in to comment.