diff --git a/internal/backend/semtechudp/packets/push_data.go b/internal/backend/semtechudp/packets/push_data.go index 9cd96616..256d9ef1 100644 --- a/internal/backend/semtechudp/packets/push_data.go +++ b/internal/backend/semtechudp/packets/push_data.go @@ -91,7 +91,7 @@ func (p PushDataPacket) GetUplinkFrames(skipCRCCheck bool, FakeRxInfoTime bool) } if len(p.Payload.RXPK[i].RSig) == 0 { - frame, err := getUplinkFrame(p.GatewayMAC, p.Payload.RXPK[i], FakeRxInfoTime) + frame, err := getUplinkFrame(p.GatewayMAC, p.Payload.Stat, p.Payload.RXPK[i], FakeRxInfoTime) if err != nil { return nil, errors.Wrap(err, "backend/semtechudp/packets: get uplink frame error") } @@ -99,7 +99,7 @@ func (p PushDataPacket) GetUplinkFrames(skipCRCCheck bool, FakeRxInfoTime bool) frames = append(frames, frame) } else { for j := range p.Payload.RXPK[i].RSig { - frame, err := getUplinkFrame(p.GatewayMAC, p.Payload.RXPK[i], FakeRxInfoTime) + frame, err := getUplinkFrame(p.GatewayMAC, p.Payload.Stat, p.Payload.RXPK[i], FakeRxInfoTime) if err != nil { return nil, errors.Wrap(err, "backend/semtechudp/packets: get uplink frame error") } @@ -127,7 +127,7 @@ func setUplinkFrameRSig(frame *gw.UplinkFrame, rxPK RXPK, rSig RSig) *gw.UplinkF return frame } -func getUplinkFrame(gatewayID lorawan.EUI64, rxpk RXPK, FakeRxInfoTime bool) (*gw.UplinkFrame, error) { +func getUplinkFrame(gatewayID lorawan.EUI64, stat *Stat, rxpk RXPK, FakeRxInfoTime bool) (*gw.UplinkFrame, error) { frame := gw.UplinkFrame{ PhyPayload: rxpk.Data, TxInfo: &gw.UplinkTxInfo{ @@ -145,6 +145,18 @@ func getUplinkFrame(gatewayID lorawan.EUI64, rxpk RXPK, FakeRxInfoTime bool) (*g }, } + // If a Stat is present and it contains a location, immediately set the location for this uplink. + // This is for example the case of Helium, where the UDP frame contains both a rxpk and stat + // payload to provide additional gateway context. + if stat != nil && (stat.Lati != 0 || stat.Long != 0 || stat.Alti != 0) { + frame.RxInfo.Location = &common.Location{ + Latitude: stat.Lati, + Longitude: stat.Long, + Altitude: float64(stat.Alti), + Source: common.LocationSource_GPS, + } + } + switch rxpk.Stat { case 1: frame.RxInfo.CrcStatus = gw.CRCStatus_CRC_OK diff --git a/internal/backend/semtechudp/packets/push_data_test.go b/internal/backend/semtechudp/packets/push_data_test.go index 4db1ecb3..24c68d42 100644 --- a/internal/backend/semtechudp/packets/push_data_test.go +++ b/internal/backend/semtechudp/packets/push_data_test.go @@ -554,6 +554,74 @@ func TestGetUplinkFrame(t *testing.T) { }, }, }, + { + Name: "uplink with stat (with location)", + PushDataPacket: PushDataPacket{ + GatewayMAC: lorawan.EUI64{1, 2, 3, 4, 5, 6, 7, 8}, + ProtocolVersion: ProtocolVersion2, + Payload: PushDataPayload{ + RXPK: []RXPK{ + { + Time: &ctNow, + Tmst: 1000000, + Freq: 868.3, + Brd: 2, + Chan: 1, + RFCh: 3, + Stat: 1, + Modu: "LORA", + DatR: DatR{LoRa: "SF12BW500"}, + CodR: "4/5", + RSSI: -60, + LSNR: 5.5, + Size: 5, + Data: []byte{1, 2, 3, 4, 5}, + }, + }, + Stat: &Stat{ + Lati: 1.1, + Long: 1.2, + Alti: 10, + }, + }, + }, + UplinkFrames: []*gw.UplinkFrame{ + { + PhyPayload: []byte{1, 2, 3, 4, 5}, + TxInfo: &gw.UplinkTxInfo{ + Frequency: 868300000, + Modulation: &gw.Modulation{ + Parameters: &gw.Modulation_Lora{ + Lora: &gw.LoraModulationInfo{ + Bandwidth: 500000, + SpreadingFactor: 12, + CodeRate: gw.CodeRate_CR_4_5, + PolarizationInversion: false, + }, + }, + }, + }, + RxInfo: &gw.UplinkRxInfo{ + GatewayId: "0102030405060708", + Time: pbTime, + Rssi: -60, + Snr: 5.5, + Channel: 1, + RfChain: 3, + Board: 2, + Antenna: 0, + Context: []byte{0x00, 0x0f, 0x42, 0x40}, + CrcStatus: gw.CRCStatus_CRC_OK, + Location: &common.Location{ + Latitude: 1.1, + Longitude: 1.2, + Altitude: 10, + Source: common.LocationSource_GPS, + }, + }, + }, + }, + }, } for _, test := range testTable {