Skip to content

Commit 80ec817

Browse files
committed
Add iBTS compatibility.
Note: a frame received by multiple antennas is emitted as multiple JSON over MQTT messages. Closes #44.
1 parent 0efb1dd commit 80ec817

File tree

6 files changed

+149
-33
lines changed

6 files changed

+149
-33
lines changed

docs/content/use/data.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,8 @@ Topic for received packets (from nodes). Example payload:
4848
{
4949
"phyPayload": "AAEBAQEBAQEBAgICAgICAgJpNbxrAh8=", // base64 encoded LoRaWAN packet
5050
"rxInfo": {
51+
"board": 0,
52+
"antenna": 0,
5153
"channel": 1,
5254
"codeRate": "4/5",
5355
"crcStatus": 1,
@@ -77,6 +79,8 @@ Example payload:
7779
{
7880
"phyPayload": "IKu70cumKom7BREUFrxlHtM=",
7981
"txInfo": {
82+
"board": 0,
83+
"antenna": 0,
8084
"codeRate": "4/5",
8185
"dataRate": {
8286
"bandwidth": 125,

gateway/backend.go

Lines changed: 35 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -342,18 +342,21 @@ func (b *Backend) handleRXPacket(addr *net.UDPAddr, mac lorawan.EUI64, rxpk RXPK
342342
}
343343
log.WithFields(logFields).Info("gateway: rxpk packet received")
344344

345-
// decode packet
346-
rxPacket, err := newRXPacketFromRXPK(mac, rxpk)
345+
// decode packet(s)
346+
rxPackets, err := newRXPacketsFromRXPK(mac, rxpk)
347347
if err != nil {
348348
return err
349349
}
350350

351-
// check CRC
352-
if !b.skipCRCCheck && rxPacket.RXInfo.CRCStatus != 1 {
353-
log.WithFields(logFields).Warningf("gateway: invalid packet CRC: %d", rxPacket.RXInfo.CRCStatus)
354-
return errors.New("gateway: invalid CRC")
351+
for i := range rxPackets {
352+
// check CRC
353+
if !b.skipCRCCheck && rxPackets[i].RXInfo.CRCStatus != 1 {
354+
log.WithFields(logFields).Warningf("gateway: invalid packet CRC: %d", rxPackets[i].RXInfo.CRCStatus)
355+
return errors.New("gateway: invalid CRC")
356+
}
357+
b.rxChan <- rxPackets[i]
355358
}
356-
b.rxChan <- rxPacket
359+
357360
return nil
358361
}
359362

@@ -406,18 +409,21 @@ func newGatewayStatsPacket(mac lorawan.EUI64, stat Stat) gw.GatewayStatsPacket {
406409
return gwStat
407410
}
408411

409-
// newRXPacketFromRXPK transforms a Semtech packet into a gw.RXPacketBytes.
410-
func newRXPacketFromRXPK(mac lorawan.EUI64, rxpk RXPK) (gw.RXPacketBytes, error) {
412+
// newRXPacketsFromRXPK transforms a Semtech packet into a slice of
413+
// gw.RXPacketBytes.
414+
func newRXPacketsFromRXPK(mac lorawan.EUI64, rxpk RXPK) ([]gw.RXPacketBytes, error) {
411415
dataRate, err := newDataRateFromDatR(rxpk.DatR)
412416
if err != nil {
413-
return gw.RXPacketBytes{}, fmt.Errorf("gateway: could not get DataRate from DatR: %s", err)
417+
return nil, fmt.Errorf("gateway: could not get DataRate from DatR: %s", err)
414418
}
415419

416420
b, err := base64.StdEncoding.DecodeString(rxpk.Data)
417421
if err != nil {
418-
return gw.RXPacketBytes{}, fmt.Errorf("gateway: could not base64 decode data: %s", err)
422+
return nil, fmt.Errorf("gateway: could not base64 decode data: %s", err)
419423
}
420424

425+
var rxPackets []gw.RXPacketBytes
426+
421427
rxPacket := gw.RXPacketBytes{
422428
PHYPayload: b,
423429
RXInfo: gw.RXInfo{
@@ -433,9 +439,24 @@ func newRXPacketFromRXPK(mac lorawan.EUI64, rxpk RXPK) (gw.RXPacketBytes, error)
433439
RSSI: int(rxpk.RSSI),
434440
LoRaSNR: rxpk.LSNR,
435441
Size: int(rxpk.Size),
442+
Board: int(rxpk.Brd),
436443
},
437444
}
438-
return rxPacket, nil
445+
446+
if len(rxpk.RSig) == 0 {
447+
rxPackets = append(rxPackets, rxPacket)
448+
}
449+
450+
for _, s := range rxpk.RSig {
451+
rxPacket.RXInfo.Antenna = int(s.Ant)
452+
rxPacket.RXInfo.Channel = int(s.Chan)
453+
rxPacket.RXInfo.LoRaSNR = s.LSNR
454+
rxPacket.RXInfo.RSSI = int(s.RSSIC)
455+
456+
rxPackets = append(rxPackets, rxPacket)
457+
}
458+
459+
return rxPackets, nil
439460
}
440461

441462
// newTXPKFromTXPacket transforms a gw.TXPacketBytes into a Semtech
@@ -451,6 +472,8 @@ func newTXPKFromTXPacket(txPacket gw.TXPacketBytes) (TXPK, error) {
451472
CodR: txPacket.TXInfo.CodeRate,
452473
Size: uint16(len(txPacket.PHYPayload)),
453474
Data: base64.StdEncoding.EncodeToString(txPacket.PHYPayload),
475+
Ant: uint8(txPacket.TXInfo.Antenna),
476+
Brd: uint8(txPacket.TXInfo.Board),
454477
}
455478

456479
if txPacket.TXInfo.DataRate.Modulation == band.FSKModulation {

gateway/backend_test.go

Lines changed: 82 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -141,9 +141,9 @@ func TestBackend(t *testing.T) {
141141
Convey("Then the packet is returned by the RX packet channel", func() {
142142
rxPacket := <-backend.RXPacketChan()
143143

144-
rxPacket2, err := newRXPacketFromRXPK(p.GatewayMAC, p.Payload.RXPK[0])
144+
rxPackets, err := newRXPacketsFromRXPK(p.GatewayMAC, p.Payload.RXPK[0])
145145
So(err, ShouldBeNil)
146-
So(rxPacket, ShouldResemble, rxPacket2)
146+
So(rxPacket, ShouldResemble, rxPackets[0])
147147
})
148148
})
149149

@@ -239,9 +239,9 @@ func TestBackend(t *testing.T) {
239239
Convey("Then the packet is returned by the RX packet channel", func() {
240240
rxPacket := <-backend.RXPacketChan()
241241

242-
rxPacket2, err := newRXPacketFromRXPK(p.GatewayMAC, p.Payload.RXPK[0])
242+
rxPackets, err := newRXPacketsFromRXPK(p.GatewayMAC, p.Payload.RXPK[0])
243243
So(err, ShouldBeNil)
244-
So(rxPacket, ShouldResemble, rxPacket2)
244+
So(rxPacket, ShouldResemble, rxPackets[0])
245245
})
246246
})
247247

@@ -288,9 +288,9 @@ func TestBackend(t *testing.T) {
288288
Convey("Then the packet is returned by the RX packet channel", func() {
289289
rxPacket := <-backend.RXPacketChan()
290290

291-
rxPacket2, err := newRXPacketFromRXPK(p.GatewayMAC, p.Payload.RXPK[0])
291+
rxPackets, err := newRXPacketsFromRXPK(p.GatewayMAC, p.Payload.RXPK[0])
292292
So(err, ShouldBeNil)
293-
So(rxPacket, ShouldResemble, rxPacket2)
293+
So(rxPacket, ShouldResemble, rxPackets[0])
294294
})
295295
})
296296
})
@@ -463,6 +463,8 @@ func TestNewTXPKFromTXPacket(t *testing.T) {
463463
SpreadFactor: 9,
464464
Bandwidth: 250,
465465
},
466+
Board: 1,
467+
Antenna: 2,
466468
},
467469
PHYPayload: []byte{1, 2, 3, 4},
468470
}
@@ -483,6 +485,8 @@ func TestNewTXPKFromTXPacket(t *testing.T) {
483485
Size: 4,
484486
Data: "AQIDBA==",
485487
IPol: true,
488+
Brd: 1,
489+
Ant: 2,
486490
})
487491
})
488492

@@ -519,14 +523,15 @@ func TestNewRXPacketFromRXPK(t *testing.T) {
519523
}
520524
mac := [8]byte{1, 2, 3, 4, 5, 6, 7, 8}
521525

522-
Convey("When calling newRXPacketFromRXPK(", func() {
523-
rxPacket, err := newRXPacketFromRXPK(mac, rxpk)
526+
Convey("When calling newRXPacketsFromRXPK without RSig field", func() {
527+
rxPackets, err := newRXPacketsFromRXPK(mac, rxpk)
524528
So(err, ShouldBeNil)
529+
So(rxPackets, ShouldHaveLength, 1)
525530

526531
Convey("Then all fields are set correctly", func() {
527-
So(rxPacket.PHYPayload, ShouldResemble, []byte{1, 2, 3, 4})
532+
So(rxPackets[0].PHYPayload, ShouldResemble, []byte{1, 2, 3, 4})
528533

529-
So(rxPacket.RXInfo, ShouldResemble, gw.RXInfo{
534+
So(rxPackets[0].RXInfo, ShouldResemble, gw.RXInfo{
530535
MAC: mac,
531536
Time: now,
532537
Timestamp: 708016819,
@@ -546,6 +551,73 @@ func TestNewRXPacketFromRXPK(t *testing.T) {
546551
})
547552
})
548553
})
554+
555+
Convey("When calling newRXPacketsFromRXPK with multiple RSig elements", func() {
556+
rxpk.Brd = 2
557+
rxpk.RSig = []RSig{
558+
{
559+
Ant: 1,
560+
Chan: 3,
561+
LSNR: 1.5,
562+
RSSIC: -50,
563+
},
564+
{
565+
Ant: 2,
566+
Chan: 3,
567+
LSNR: 2,
568+
RSSIC: -30,
569+
},
570+
}
571+
rxPackets, err := newRXPacketsFromRXPK(mac, rxpk)
572+
So(err, ShouldBeNil)
573+
So(rxPackets, ShouldHaveLength, 2)
574+
575+
Convey("Then all fields are set correctly", func() {
576+
So(rxPackets[0].PHYPayload, ShouldResemble, []byte{1, 2, 3, 4})
577+
So(rxPackets[0].RXInfo, ShouldResemble, gw.RXInfo{
578+
MAC: mac,
579+
Time: now,
580+
Timestamp: 708016819,
581+
Frequency: 868500000,
582+
Channel: 3,
583+
RFChain: 1,
584+
CRCStatus: 1,
585+
DataRate: band.DataRate{
586+
Modulation: band.LoRaModulation,
587+
SpreadFactor: 7,
588+
Bandwidth: 125,
589+
},
590+
CodeRate: "4/5",
591+
RSSI: -50,
592+
LoRaSNR: 1.5,
593+
Size: 16,
594+
Antenna: 1,
595+
Board: 2,
596+
})
597+
598+
So(rxPackets[1].PHYPayload, ShouldResemble, []byte{1, 2, 3, 4})
599+
So(rxPackets[1].RXInfo, ShouldResemble, gw.RXInfo{
600+
MAC: mac,
601+
Time: now,
602+
Timestamp: 708016819,
603+
Frequency: 868500000,
604+
Channel: 3,
605+
RFChain: 1,
606+
CRCStatus: 1,
607+
DataRate: band.DataRate{
608+
Modulation: band.LoRaModulation,
609+
SpreadFactor: 7,
610+
Bandwidth: 125,
611+
},
612+
CodeRate: "4/5",
613+
RSSI: -30,
614+
LoRaSNR: 2,
615+
Size: 16,
616+
Antenna: 2,
617+
Board: 2,
618+
})
619+
})
620+
})
549621
})
550622
}
551623

gateway/structs.go

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -379,6 +379,7 @@ type RXPK struct {
379379
Time CompactTime `json:"time"` // UTC time of pkt RX, us precision, ISO 8601 'compact' format (e.g. 2013-03-31T16:21:17.528002Z)
380380
Tmst uint32 `json:"tmst"` // Internal timestamp of "RX finished" event (32b unsigned)
381381
Freq float64 `json:"freq"` // RX central frequency in MHz (unsigned float, Hz precision)
382+
Brd uint8 `json:"brd"` // Concentrator board used for RX (unsigned integer)
382383
Chan uint8 `json:"chan"` // Concentrator "IF" channel used for RX (unsigned integer)
383384
RFCh uint8 `json:"rfch"` // Concentrator "RF chain" used for RX (unsigned integer)
384385
Stat int8 `json:"stat"` // CRC status: 1 = OK, -1 = fail, 0 = no CRC
@@ -389,6 +390,15 @@ type RXPK struct {
389390
LSNR float64 `json:"lsnr"` // Lora SNR ratio in dB (signed float, 0.1 dB precision)
390391
Size uint16 `json:"size"` // RF packet payload size in bytes (unsigned integer)
391392
Data string `json:"data"` // Base64 encoded RF packet payload, padded
393+
RSig []RSig `json:"rsig"` // Received signal information, per antenna (Optional)
394+
}
395+
396+
// RSig contains the received signal information per antenna.
397+
type RSig struct {
398+
Ant uint8 `json:"ant"` // Antenna number on which signal has been received
399+
Chan uint8 `json:"chan"` // Concentrator "IF" channel used for RX (unsigned integer)
400+
LSNR float64 `json:"lsnr"` // Lora SNR ratio in dB (signed float, 0.1 dB precision)
401+
RSSIC int16 `json:"rssic"` // RSSI in dBm of the channel (signed integer, 1 dB precision)
392402
}
393403

394404
// Stat contains the status of the gateway.
@@ -422,6 +432,8 @@ type TXPK struct {
422432
Size uint16 `json:"size"` // RF packet payload size in bytes (unsigned integer)
423433
NCRC bool `json:"ncrc,omitempty"` // If true, disable the CRC of the physical layer (optional)
424434
Data string `json:"data"` // Base64 encoded RF packet payload, padding optional
435+
Brd uint8 `json:"brd"` // Concentrator board used for RX (unsigned integer)
436+
Ant uint8 `json:"ant"` // Antenna number on which signal has been received
425437
}
426438

427439
// GetPacketType returns the packet type for the given packet data.

vendor/github.com/brocaar/loraserver/api/gw/gw.go

Lines changed: 7 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

vendor/vendor.json

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -3,16 +3,10 @@
33
"ignore": "test",
44
"package": [
55
{
6-
"checksumSHA1": "Liv7AJyNinmP3pvwohcXp4R6xY4=",
7-
"path": "github.com/sirupsen/logrus",
8-
"revision": "85b1699d505667d13f8ac4478c1debbf85d6c5de",
9-
"revisionTime": "2017-06-08T22:14:41Z"
10-
},
11-
{
12-
"checksumSHA1": "6EjJ4tPAes1IG51evTGi4DMLoSc=",
6+
"checksumSHA1": "a0hL5kRhk5IzfoKWGFh/tdLzsKI=",
137
"path": "github.com/brocaar/loraserver/api/gw",
14-
"revision": "1d605e3a5d305fe089e0737778dc78bc5fcb7b7b",
15-
"revisionTime": "2017-06-02T09:33:00Z"
8+
"revision": "a28abf4a29f5c735c3b46d705436c290e1c1282f",
9+
"revisionTime": "2017-11-25T09:41:02Z"
1610
},
1711
{
1812
"checksumSHA1": "u52BzfoKG0x7CgBgg/PB1AEVSag=",
@@ -62,6 +56,12 @@
6256
"revision": "77f18212c9c7edc9bd6a33d383a7b545ce62f064",
6357
"revisionTime": "2017-05-03T22:40:06Z"
6458
},
59+
{
60+
"checksumSHA1": "Liv7AJyNinmP3pvwohcXp4R6xY4=",
61+
"path": "github.com/sirupsen/logrus",
62+
"revision": "85b1699d505667d13f8ac4478c1debbf85d6c5de",
63+
"revisionTime": "2017-06-08T22:14:41Z"
64+
},
6565
{
6666
"checksumSHA1": "AikWjAOvWHSJ8G1iU+wNReZnyCk=",
6767
"path": "github.com/smartystreets/assertions",

0 commit comments

Comments
 (0)