Skip to content

Commit 238bf98

Browse files
committed
Implement LR-FHSS modulation type.
1 parent 6fb803f commit 238bf98

File tree

5 files changed

+97
-8
lines changed

5 files changed

+97
-8
lines changed

go.mod

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ module github.com/brocaar/chirpstack-gateway-bridge
33
go 1.16
44

55
require (
6-
github.com/brocaar/chirpstack-api/go/v3 v3.9.7
6+
github.com/brocaar/chirpstack-api/go/v3 v3.10.1
77
github.com/brocaar/lorawan v0.0.0-20201030140234-f23da2d4a303
88
github.com/dgrijalva/jwt-go v3.2.0+incompatible
99
github.com/eclipse/paho.mqtt.golang v1.3.0

go.sum

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -51,8 +51,8 @@ github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kB
5151
github.com/bketelsen/crypt v0.0.3-0.20200106085610-5cbc8cc4026c/go.mod h1:MKsuJmJgSg28kpZDP6UIiPt0e0Oz0kqKNGyRaWEPv84=
5252
github.com/blakesmith/ar v0.0.0-20150311145944-8bd4349a67f2 h1:oMCHnXa6CCCafdPDbMh/lWRhRByN0VFLvv+g+ayx1SI=
5353
github.com/blakesmith/ar v0.0.0-20150311145944-8bd4349a67f2/go.mod h1:PkYb9DJNAwrSvRx5DYA+gUcOIgTGVMNkfSCbZM8cWpI=
54-
github.com/brocaar/chirpstack-api/go/v3 v3.9.7 h1:n5Zte6zIg+qbqtb4dwp3vGQwIXpXsk5nMR4WwMUcLgA=
55-
github.com/brocaar/chirpstack-api/go/v3 v3.9.7/go.mod h1:v8AWP19nOJK4rwJsr1+weDfpUc4UNLbRh8Eygn4Oh00=
54+
github.com/brocaar/chirpstack-api/go/v3 v3.10.1 h1:ijf3AKDw++aSPJE41lkiPxXtshxZF++cXzV2l9e7EYk=
55+
github.com/brocaar/chirpstack-api/go/v3 v3.10.1/go.mod h1:v8AWP19nOJK4rwJsr1+weDfpUc4UNLbRh8Eygn4Oh00=
5656
github.com/brocaar/lorawan v0.0.0-20201030140234-f23da2d4a303 h1:LkE19tFPfDaRh1HIKWLCZKSBZNonMu0rIOJPCLvEjC0=
5757
github.com/brocaar/lorawan v0.0.0-20201030140234-f23da2d4a303/go.mod h1:CciUmQHIpUYTHHMeICtyamM7d+47VV+WBZ5ReDozpoc=
5858
github.com/caarlos0/ctrlc v1.0.0 h1:2DtF8GSIcajgffDFJzyG15vO+1PuBWOMUdFut7NnXhw=

internal/backend/semtechudp/packets/packets.go

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -102,23 +102,34 @@ func (t *CompactTime) UnmarshalJSON(data []byte) error {
102102
// DatR implements the data rate which can be either a string (LoRa identifier)
103103
// or an unsigned integer in case of FSK (bits per second).
104104
type DatR struct {
105-
LoRa string
106-
FSK uint32
105+
LRFHSS string
106+
LoRa string
107+
FSK uint32
107108
}
108109

109110
// MarshalJSON implements the json.Marshaler interface.
110111
func (d DatR) MarshalJSON() ([]byte, error) {
111112
if d.LoRa != "" {
112113
return []byte(`"` + d.LoRa + `"`), nil
113114
}
115+
if d.LRFHSS != "" {
116+
return []byte(`"` + d.LRFHSS + `"`), nil
117+
}
114118
return []byte(strconv.FormatUint(uint64(d.FSK), 10)), nil
115119
}
116120

117121
// UnmarshalJSON implements the json.Unmarshaler interface.
118122
func (d *DatR) UnmarshalJSON(data []byte) error {
119123
i, err := strconv.ParseUint(string(data), 10, 32)
120124
if err != nil {
121-
d.LoRa = strings.Trim(string(data), `"`)
125+
// remove the trailing and leading quotes
126+
str := strings.Trim(string(data), `"`)
127+
128+
if strings.HasPrefix(str, "SF") {
129+
d.LoRa = str
130+
} else {
131+
d.LRFHSS = str
132+
}
122133
return nil
123134
}
124135
d.FSK = uint32(i)

internal/backend/semtechudp/packets/push_data.go

Lines changed: 31 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,9 +16,12 @@ import (
1616
"github.com/brocaar/lorawan"
1717
)
1818

19-
// loRaDataRateRegex contains a regexp for parsing the data-rate string.
19+
// loRaDataRateRegex contains a regexp for parsing the LoRa data-rate string.
2020
var loRaDataRateRegex = regexp.MustCompile(`SF(\d+)BW(\d+)`)
2121

22+
// lrFHSSDataRateRegex contains the regexp for parsing the LR-FHSS data-rate string.
23+
var lrFHSSDataRateRegex = regexp.MustCompile(`M0CW(\d+)`)
24+
2225
// PushDataPacket type is used by the gateway mainly to forward the RF packets
2326
// received, and associated metadata, to the server.
2427
type PushDataPacket struct {
@@ -203,8 +206,8 @@ func getUplinkFrame(gatewayID []byte, rxpk RXPK, FakeRxInfoTime bool) (gw.Uplink
203206
if rxpk.DatR.LoRa != "" {
204207
frame.TxInfo.Modulation = common.Modulation_LORA
205208

206-
match := loRaDataRateRegex.FindStringSubmatch(rxpk.DatR.LoRa)
207209
// parse e.g. SF12BW250 into separate variables
210+
match := loRaDataRateRegex.FindStringSubmatch(rxpk.DatR.LoRa)
208211
if len(match) != 3 {
209212
return frame, errors.New("backend/semtechudp/packets: could not parse LoRa data-rate")
210213
}
@@ -229,6 +232,31 @@ func getUplinkFrame(gatewayID []byte, rxpk RXPK, FakeRxInfoTime bool) (gw.Uplink
229232
}
230233
}
231234

235+
// LR-FHSS data-rate
236+
if rxpk.DatR.LRFHSS != "" {
237+
frame.TxInfo.Modulation = common.Modulation_LR_FHSS
238+
239+
// parse M0CW137 into CW (OCW) variable
240+
match := lrFHSSDataRateRegex.FindStringSubmatch(rxpk.DatR.LRFHSS)
241+
if len(match) != 2 {
242+
return frame, errors.New("backend/semtechudp/packets: could not parse LR-FHSS data-rate")
243+
}
244+
245+
// cast variable to int
246+
ocw, err := strconv.Atoi(match[1])
247+
if err != nil {
248+
return frame, errors.Wrap(err, "backend/semtechudp/packets: could not convert cw to int")
249+
}
250+
251+
frame.TxInfo.ModulationInfo = &gw.UplinkTXInfo_LrFhssModulationInfo{
252+
LrFhssModulationInfo: &gw.LRFHSSModulationInfo{
253+
OperatingChannelWidth: uint32(ocw) * 1000, // kHz -> Hz
254+
CodeRate: rxpk.CodR,
255+
GridSteps: uint32(rxpk.HPW),
256+
},
257+
}
258+
}
259+
232260
// FSK data-rate
233261
if rxpk.DatR.FSK != 0 {
234262
frame.TxInfo.Modulation = common.Modulation_FSK
@@ -302,6 +330,7 @@ type RXPK struct {
302330
Modu string `json:"modu"` // Modulation identifier "LORA" or "FSK"
303331
CodR string `json:"codr"` // LoRa ECC coding rate identifier
304332
LSNR float64 `json:"lsnr"` // Lora SNR ratio in dB (signed float, 0.1 dB precision)
333+
HPW uint8 `json:"hpw"` // LR-FHSS hopping grid number of steps.
305334
Data []byte `json:"data"` // Base64 encoded RF packet payload, padded
306335
RSig []RSig `json:"rsig"` // Received signal information, per antenna (Optional)
307336
}

internal/backend/semtechudp/packets/push_data_test.go

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -417,6 +417,55 @@ func TestGetUplinkFrame(t *testing.T) {
417417
},
418418
},
419419
},
420+
{
421+
Name: "LR-FHSS modulation",
422+
PushDataPacket: PushDataPacket{
423+
GatewayMAC: lorawan.EUI64{1, 2, 3, 4, 5, 6, 7, 8},
424+
ProtocolVersion: ProtocolVersion2,
425+
Payload: PushDataPayload{
426+
RXPK: []RXPK{
427+
{
428+
Tmst: 1000000,
429+
Freq: 868.3,
430+
Stat: 1,
431+
Modu: "LR-FHSS",
432+
DatR: DatR{LRFHSS: "M0CW137"},
433+
CodR: "4/6",
434+
HPW: 8,
435+
Size: 5,
436+
Data: []byte{1, 2, 3, 4, 5},
437+
RSig: []RSig{
438+
{
439+
RSSIC: -74,
440+
},
441+
},
442+
},
443+
},
444+
},
445+
},
446+
UplinkFrames: []gw.UplinkFrame{
447+
{
448+
PhyPayload: []byte{1, 2, 3, 4, 5},
449+
TxInfo: &gw.UplinkTXInfo{
450+
Frequency: 868300000,
451+
Modulation: common.Modulation_LR_FHSS,
452+
ModulationInfo: &gw.UplinkTXInfo_LrFhssModulationInfo{
453+
LrFhssModulationInfo: &gw.LRFHSSModulationInfo{
454+
OperatingChannelWidth: 137000,
455+
CodeRate: "4/6",
456+
GridSteps: 8,
457+
},
458+
},
459+
},
460+
RxInfo: &gw.UplinkRXInfo{
461+
GatewayId: []byte{1, 2, 3, 4, 5, 6, 7, 8},
462+
Rssi: -74,
463+
Context: []byte{0x00, 0x0f, 0x42, 0x40},
464+
CrcStatus: gw.CRCStatus_CRC_OK,
465+
},
466+
},
467+
},
468+
},
420469
}
421470

422471
for _, test := range testTable {

0 commit comments

Comments
 (0)