chirpstack / chirpstack-gateway-bridge

ChirpStack Gateway Bridge abstracts Packet Forwarder protocols into Protobuf or JSON over MQTT.
https://www.chirpstack.io
MIT License
415 stars 269 forks source link

Basic Station backend: FineTimeSinceGpsEpoch missing from uplink event? #228

Open LouneCode opened 1 year ago

LouneCode commented 1 year ago

Summary

Hi all,

It looks like the ChirpStack Gateway Bridge with Basic Station backend don't support fine timestamps? Actually the websocket message of the Basic Station backend has a “fts” tag for fine timestamps, but this feature is missing from the ChirpStack Gateway bridge?

 

Basic Station backend websocket message (SX1303, SX1302):

2022-12-19 17:20:14.797 [AIO:XDEB] [3|WS] > {"msgtype":"updf","MHdr":64, ---> "fts":-1 <--- ,"rssi":-45,"snr":13.75,"rxtime":1671470414.687880516}}

What is the use-case?

A Fine timestamp is used to get the TDoA geolocation of the transmitter (sensor).

Implementation description

ChirpStack Gateway Bridge

The RadioMetaDataUpInfo structure in radio_meta_data.go implements which elements of a websocket message to parse.

// RadioMetaDataUpInfo contains the radio meta-data uplink info.
type RadioMetaDataUpInfo struct {
    RxTime  float64 `json:"rxtime"`
    RCtx    uint64  `json:"rctx"`
    XTime   uint64  `json:"xtime"`
    GPSTime int64   `json:"gpstime"`
    RSSI    float32 `json:"rssi"`
    SNR     float32 `json:"snr"`
}

A fine timestamp (Fts element) could be added quite simply to the RadioMetaDataUpInfo structure as follows:

// RadioMetaDataUpInfo contains the radio meta-data uplink info.
type RadioMetaDataUpInfo struct {
    RxTime  float64 `json:"rxtime"`
    RCtx    uint64  `json:"rctx"`
    XTime   uint64  `json:"xtime"`
    GPSTime int64   `json:"gpstime"`
    Fts     *int64  `json:"fts,omitempty"`
    RSSI    float32 `json:"rssi"`
    SNR     float32 `json:"snr"`
}

Func SetRadioMetaDataToProto() in the same file needs a little refactoring:

// SetRadioMetaDataToProto sets the given parameters to the given protobuf struct.
func SetRadioMetaDataToProto(loraBand band.Band, gatewayID lorawan.EUI64, rmd RadioMetaData, pb *gw.UplinkFrame) error {
    //
    // TxInfo
    //
    dr, err := loraBand.GetDataRate(rmd.DR)
    if err != nil {
        return errors.Wrap(err, "get data-rate error")
    }

    pb.TxInfo = &gw.UplinkTxInfo{
        Frequency: rmd.Frequency,
    }

    switch dr.Modulation {
    case band.LoRaModulation:
        pb.TxInfo.Modulation = &gw.Modulation{
            Parameters: &gw.Modulation_Lora{
                Lora: &gw.LoraModulationInfo{
                    Bandwidth:             uint32(dr.Bandwidth) * 1000,
                    SpreadingFactor:       uint32(dr.SpreadFactor),
                    CodeRate:              gw.CodeRate_CR_4_5,
                    PolarizationInversion: false,
                },
            },
        }
    case band.FSKModulation:
        pb.TxInfo.Modulation = &gw.Modulation{
            Parameters: &gw.Modulation_Fsk{
                Fsk: &gw.FskModulationInfo{
                    Datarate: uint32(dr.BitRate),
                },
            },
        }
    }

    //
    // RxInfo
    //
    pb.RxInfo = &gw.UplinkRxInfo{
        GatewayId: gatewayID.String(),
        Rssi:      int32(rmd.UpInfo.RSSI),
        Snr:       float32(rmd.UpInfo.SNR),
    }

    if rxTime := rmd.UpInfo.RxTime; rxTime != 0 {
        sec, nsec := math.Modf(rmd.UpInfo.RxTime)
        if sec != 0 {
            val := time.Unix(int64(sec), int64(nsec))
            pb.RxInfo.Time = timestamppb.New(val)
        }
    }

    if gpsTime := rmd.UpInfo.GPSTime; gpsTime != 0 {
        gpsTimeDur := time.Duration(gpsTime) * time.Microsecond
        gpsTimeTime := time.Time(gps.NewTimeFromTimeSinceGPSEpoch(gpsTimeDur))

        pb.RxInfo.TimeSinceGpsEpoch = durationpb.New(gpsTimeDur)
        pb.RxInfo.Time = timestamppb.New(gpsTimeTime)
    }

    if fts := rmd.UpInfo.Fts; fts != nil {
        if *fts > -1 {

            ftsTimeDur := time.Duration(*fts) * time.Nanosecond

            if gpsTime := rmd.UpInfo.GPSTime; gpsTime != 0 {
                gpsTimeDur := time.Duration(gpsTime) * time.Microsecond
                // take the seconds from the gps time
                gpsTimeDur = gpsTimeDur - (gpsTimeDur % time.Second)
                // add the nanos from the fine-timestamp
                ftsTimeDur = gpsTimeDur + ftsTimeDur
            }

            pb.RxInfo.FineTimeSinceGpsEpoch = durationpb.New(ftsTimeDur)
        }
    }

After these small changes, the fine timestamp information exist in the uplink event as follows (if you are using the GLS Basic Station instead of Basic Station backend):

"rxInfo": [
    {
        "gatewayId": "0016c001ffxxxxxx",
        "uplinkId": 3232276575,
        "time": "2022-12-26T12:01:47.709920+00:00",
        "timeSinceGpsEpoch": "1356091325.709920s",
        "fineTimeSinceGpsEpoch": "1356091325.263021396s",
        "rssi": -45,
        "snr": 14.25,
        "location": {
            "latitude": 62.1979847889177,
            "longitude": 21.123254060745244
        },
        "context": "AAAAAAAAAAAAMgABVWHvOA==",
        "metadata": {
            "region_common_name": "EU868",
            "region_name": "eu868"
        }
    },

This feature can be patched with the following commands:

$ git clone https://github.com/chirpstack/chirpstack-gateway-bridge.git chirpstack-gateway-bridge
$ cd chirpstack-gateway-bridge
$ curl https://raw.githubusercontent.com/LouneCode/GLS-chripstack-gateway-bridge/main/builder/GLS_CSGB_v4.0.8.1.patch | git apply

# Sanity check:
$ cat ./internal/backend/basicstation/structs/radio_meta_data.go

 

More information about this idea can be found at:

 

Can you implement this by yourself and make a pull request?

See the implementation proposal above.

brocaar commented 1 year ago

I'm happy to add support for the fine-timestamp, but I'm hesitant to implement features which are not part of the "official" documented protocol and implementation. I would rather wait until https://github.com/lorabasics/basicstation/issues/177 has been integrated in the "official" Basics Station implementation.

LouneCode commented 1 year ago

I agree that. Sounds like a good plan. Looking forward "official" support of the fine-timestamp in the Basic station implementation.

I have run about five months that "unofficial" version of the fine-timestamp patch and it works like a charm ;)