Add the ability to schedule Class C Downlinks

As of right now, there are only 3 downlink timing options: delay, immediate, or class B (GPS). I propose adding a fourth option to allow us to schedule a downlink using the concentrator timestamp (tmst). This field is already used but only for Class A downlinks which must reference the Class A uplink context id. We would like to schedule a Class C downlink using the tmst field directly. Is this possible? Any drawbacks?

ChirpStack Gateway Bridge provides 3 options:

  • Delay timing (e.g. Class-A)
  • GPS timing (e.g. Class-B)
  • Immediately (e.g. Class-C)

The first option maps to the tmst (the uplink tmst which is encoded in the context + provided delay), the second option to tmms

I do not see an advantage of adding tmst based Class-C support. The tmst is a gateway internal counter and ChirpStack has no knowledge about this value and it is not able to calculate the current value for each gateway.

Using tmms in your case sounds like a better solution :slight_smile:

tmms requires GPS which we are trying to avoid.

According to the code below, the gateway bridge records the tmst value on an uplink and provides a context variable for the network server to reference for a Class A downlink. Am I misunderstanding?

case gw.DownlinkTiming_DELAY:
timingInfo := frame.TxInfo.GetDelayTimingInfo()
if timingInfo == nil {
return packet, errors.New(“delay_timing_info must not be nil”)
delay, err := ptypes.Duration(timingInfo.Delay)
if err != nil {
return packet, errors.Wrap(err, “get delay duration error”)
if len(frame.TxInfo.Context) < 4 {
return packet, fmt.Errorf(“context must contain at least 4 bytes, got: %d”, len(frame.TxInfo.Context))
timestamp := binary.BigEndian.Uint32(frame.TxInfo.Context[0:4])
timestamp += uint32(delay / time.Microsecond)
packet.Payload.TXPK.Tmst = &timestamp

Correct. Please note that the implementation of the context is packet-forwarder backend dependent (Concentratord / Semtech UDP Packet Forwarder / BasicStation). From the NS perspective, it should be treated as a binary blob.

Yes, I can see where it shouldn’t be a function of the NS, but it would be nice to be able to do it via integration on the gateway bridge. It is a feature of the Semtech protocol that is unavailable to applications.

The idea of the ChirpStack Gateway Bridge is not to expose the Semtech UDP protocol, but to provide an unified API which can be used with different packet-forwarders.

However, if you know what you are doing, then you can still use the tmst based scheduling yourself when interacting directly with the Gateway Bridge:

As you can see, with the Semtech UDP Packet Forwarder backend, the context (byte array) field contains the tmst (uint32) encoded as bigendian. On downlink, the ChirpStack Gateway Bridge will then convert the context back to an uint32 and increment this value by the delay:

Please note that these are internals, and the encoding of context might change over time (which is fine as the publisher of the context is also the consumer so this does have to be backwards compatible).

Thanks for the insight. I will give that a try.