lorawan

package
v3.30.1 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: May 13, 2024 License: Apache-2.0 Imports: 12 Imported by: 1

Documentation

Overview

Package lorawan provides LoRaWAN decoding/encoding interfaces.

Index

Constants

View Source
const (
	// JoinAcceptWithoutCFListLength represents the length of a JoinAccept binary message which does
	// not contain a CFList field. It already takes into consideration the presence of the MHDR and the MIC.
	JoinAcceptWithoutCFListLength = 17
	// JoinAcceptWithCFListLength represents the length of a JoinAccept binary message which contains
	// a CFList field. It already takes into consideration the presence of the MHDR and the MIC.
	JoinAcceptWithCFListLength = JoinAcceptWithoutCFListLength + 16
)

Variables

View Source
var DefaultMACCommands = MACCommandSpec{
	ttnpb.MACCommandIdentifier_CID_RESET: &MACCommandDescriptor{
		InitiatedByDevice: true,

		UplinkLength: 1,
		AppendUplink: func(phy band.Band, b []byte, cmd *ttnpb.MACCommand) ([]byte, error) {
			pld := cmd.GetResetInd()
			if pld.MinorVersion > 15 {
				return nil, errExpectedLowerOrEqual("MinorVersion", 15)(pld.MinorVersion)
			}
			b = append(b, byte(pld.MinorVersion))
			return b, nil
		},
		UnmarshalUplink: newMACUnmarshaler(ttnpb.MACCommandIdentifier_CID_RESET, "ResetInd", 1, func(phy band.Band, b []byte, cmd *ttnpb.MACCommand) error {
			cmd.Payload = &ttnpb.MACCommand_ResetInd_{
				ResetInd: &ttnpb.MACCommand_ResetInd{
					MinorVersion: ttnpb.Minor(b[0] & 0xf),
				},
			}
			return nil
		}),

		DownlinkLength: 1,
		AppendDownlink: func(phy band.Band, b []byte, cmd *ttnpb.MACCommand) ([]byte, error) {
			pld := cmd.GetResetConf()
			if pld.MinorVersion > 15 {
				return nil, errExpectedLowerOrEqual("MinorVersion", 15)(pld.MinorVersion)
			}
			b = append(b, byte(pld.MinorVersion))
			return b, nil
		},
		UnmarshalDownlink: newMACUnmarshaler(ttnpb.MACCommandIdentifier_CID_RESET, "ResetConf", 1, func(phy band.Band, b []byte, cmd *ttnpb.MACCommand) error {
			cmd.Payload = &ttnpb.MACCommand_ResetConf_{
				ResetConf: &ttnpb.MACCommand_ResetConf{
					MinorVersion: ttnpb.Minor(b[0] & 0xf),
				},
			}
			return nil
		}),
	},

	ttnpb.MACCommandIdentifier_CID_LINK_CHECK: &MACCommandDescriptor{
		InitiatedByDevice: true,

		AppendUplink: func(phy band.Band, b []byte, _ *ttnpb.MACCommand) ([]byte, error) {
			return b, nil
		},
		UnmarshalUplink: newMACUnmarshaler(ttnpb.MACCommandIdentifier_CID_LINK_CHECK, "LinkCheckReq", 0, nil),

		DownlinkLength: 2,
		AppendDownlink: func(phy band.Band, b []byte, cmd *ttnpb.MACCommand) ([]byte, error) {
			pld := cmd.GetLinkCheckAns()
			if pld.Margin > 254 {
				return nil, errExpectedLowerOrEqual("Margin", 254)(pld.Margin)
			}
			if pld.GatewayCount > 255 {
				return nil, errExpectedLowerOrEqual("GatewayCount", 255)(pld.GatewayCount)
			}
			b = append(b, byte(pld.Margin), byte(pld.GatewayCount))
			return b, nil
		},
		UnmarshalDownlink: newMACUnmarshaler(ttnpb.MACCommandIdentifier_CID_LINK_CHECK, "LinkCheckAns", 2, func(phy band.Band, b []byte, cmd *ttnpb.MACCommand) error {
			cmd.Payload = &ttnpb.MACCommand_LinkCheckAns_{
				LinkCheckAns: &ttnpb.MACCommand_LinkCheckAns{
					Margin:       uint32(b[0]),
					GatewayCount: uint32(b[1]),
				},
			}
			return nil
		}),
	},

	ttnpb.MACCommandIdentifier_CID_LINK_ADR: &MACCommandDescriptor{
		InitiatedByDevice: false,

		UplinkLength: 1,
		AppendUplink: func(phy band.Band, b []byte, cmd *ttnpb.MACCommand) ([]byte, error) {
			pld := cmd.GetLinkAdrAns()
			var status byte
			if pld.ChannelMaskAck {
				status |= 1
			}
			if pld.DataRateIndexAck {
				status |= (1 << 1)
			}
			if pld.TxPowerIndexAck {
				status |= (1 << 2)
			}
			b = append(b, status)
			return b, nil
		},
		UnmarshalUplink: newMACUnmarshaler(ttnpb.MACCommandIdentifier_CID_LINK_ADR, "LinkADRAns", 1, func(phy band.Band, b []byte, cmd *ttnpb.MACCommand) error {
			cmd.Payload = &ttnpb.MACCommand_LinkAdrAns{
				LinkAdrAns: &ttnpb.MACCommand_LinkADRAns{
					ChannelMaskAck:   b[0]&1 == 1,
					DataRateIndexAck: (b[0]>>1)&1 == 1,
					TxPowerIndexAck:  (b[0]>>2)&1 == 1,
				},
			}
			return nil
		}),

		DownlinkLength: 4,
		AppendDownlink: func(phy band.Band, b []byte, cmd *ttnpb.MACCommand) ([]byte, error) {
			pld := cmd.GetLinkAdrReq()
			if pld.DataRateIndex > 15 {
				return nil, errExpectedLowerOrEqual("DataRateIndex", 15)(pld.DataRateIndex)
			}
			if pld.TxPowerIndex > 15 {
				return nil, errExpectedLowerOrEqual("TxPowerIndex", 15)(pld.TxPowerIndex)
			}
			if len(pld.ChannelMask) > 16 {
				return nil, errExpectedLowerOrEqual("length of ChannelMask", "16 bits")(len(pld.ChannelMask))
			}
			if pld.ChannelMaskControl > 7 {
				return nil, errExpectedLowerOrEqual("ChannelMaskControl", 7)(pld.ChannelMaskControl)
			}
			if pld.NbTrans > 15 {
				return nil, errExpectedLowerOrEqual("NbTrans", 15)(pld.NbTrans)
			}
			b = append(b, byte((pld.DataRateIndex&0xf)<<4)^byte(pld.TxPowerIndex&0xf))
			chMask := make([]byte, 2)
			for i, v := range pld.ChannelMask {
				chMask[i/8] = chMask[i/8] ^ boolToByte(v)<<(i%8)
			}
			b = append(b, chMask...)
			b = append(b, byte((pld.ChannelMaskControl&0x7)<<4)^byte(pld.NbTrans&0xf))
			return b, nil
		},
		UnmarshalDownlink: newMACUnmarshaler(ttnpb.MACCommandIdentifier_CID_LINK_ADR, "LinkADRReq", 4, func(phy band.Band, b []byte, cmd *ttnpb.MACCommand) error {
			var chMask [16]bool
			for i := 0; i < 16; i++ {
				chMask[i] = (b[1+i/8]>>(i%8))&1 == 1
			}
			cmd.Payload = &ttnpb.MACCommand_LinkAdrReq{
				LinkAdrReq: &ttnpb.MACCommand_LinkADRReq{
					DataRateIndex:      ttnpb.DataRateIndex(b[0] >> 4),
					TxPowerIndex:       uint32(b[0] & 0xf),
					ChannelMask:        chMask[:],
					ChannelMaskControl: uint32((b[3] >> 4) & 0x7),
					NbTrans:            uint32(b[3] & 0xf),
				},
			}
			return nil
		}),
	},

	ttnpb.MACCommandIdentifier_CID_DUTY_CYCLE: &MACCommandDescriptor{
		InitiatedByDevice: false,

		AppendUplink: func(phy band.Band, b []byte, _ *ttnpb.MACCommand) ([]byte, error) {
			return b, nil
		},
		UnmarshalUplink: newMACUnmarshaler(ttnpb.MACCommandIdentifier_CID_DUTY_CYCLE, "DutyCycleAns", 0, nil),

		DownlinkLength: 1,
		AppendDownlink: func(phy band.Band, b []byte, cmd *ttnpb.MACCommand) ([]byte, error) {
			pld := cmd.GetDutyCycleReq()
			if pld.MaxDutyCycle > 15 {
				return nil, errExpectedLowerOrEqual("MaxDutyCycle", 15)(pld.MaxDutyCycle)
			}
			b = append(b, byte(pld.MaxDutyCycle))
			return b, nil
		},
		UnmarshalDownlink: newMACUnmarshaler(ttnpb.MACCommandIdentifier_CID_DUTY_CYCLE, "DutyCycleReq", 1, func(phy band.Band, b []byte, cmd *ttnpb.MACCommand) error {
			cmd.Payload = &ttnpb.MACCommand_DutyCycleReq_{
				DutyCycleReq: &ttnpb.MACCommand_DutyCycleReq{
					MaxDutyCycle: ttnpb.AggregatedDutyCycle(b[0] & 0xf),
				},
			}
			return nil
		}),
	},

	ttnpb.MACCommandIdentifier_CID_RX_PARAM_SETUP: &MACCommandDescriptor{
		InitiatedByDevice: false,

		UplinkLength: 1,
		AppendUplink: func(phy band.Band, b []byte, cmd *ttnpb.MACCommand) ([]byte, error) {
			pld := cmd.GetRxParamSetupAns()
			var v byte
			if pld.Rx2FrequencyAck {
				v |= 1
			}
			if pld.Rx2DataRateIndexAck {
				v |= (1 << 1)
			}
			if pld.Rx1DataRateOffsetAck {
				v |= (1 << 2)
			}
			b = append(b, v)
			return b, nil
		},
		UnmarshalUplink: newMACUnmarshaler(ttnpb.MACCommandIdentifier_CID_RX_PARAM_SETUP, "RxParamSetupAns", 1, func(phy band.Band, b []byte, cmd *ttnpb.MACCommand) error {
			cmd.Payload = &ttnpb.MACCommand_RxParamSetupAns_{
				RxParamSetupAns: &ttnpb.MACCommand_RxParamSetupAns{
					Rx2FrequencyAck:      b[0]&1 == 1,
					Rx2DataRateIndexAck:  (b[0]>>1)&1 == 1,
					Rx1DataRateOffsetAck: (b[0]>>2)&1 == 1,
				},
			}
			return nil
		}),

		DownlinkLength: 4,
		AppendDownlink: func(phy band.Band, b []byte, cmd *ttnpb.MACCommand) ([]byte, error) {
			pld := cmd.GetRxParamSetupReq()
			if pld.Rx1DataRateOffset > 7 {
				return nil, errExpectedLowerOrEqual("Rx1DROffset", 7)(pld.Rx1DataRateOffset)
			}
			if pld.Rx2DataRateIndex > 15 {
				return nil, errExpectedLowerOrEqual("Rx2DR", 15)(pld.Rx2DataRateIndex)
			}
			b = append(b, byte(pld.Rx2DataRateIndex)|byte(pld.Rx1DataRateOffset<<4))
			if pld.Rx2Frequency < 100000 || pld.Rx2Frequency > byteutil.MaxUint24*phy.FreqMultiplier {
				return nil, errExpectedBetween("Rx2Frequency", 100000, byteutil.MaxUint24*phy.FreqMultiplier)(pld.Rx2Frequency)
			}
			b = byteutil.AppendUint64(b, pld.Rx2Frequency/phy.FreqMultiplier, 3)
			return b, nil
		},
		UnmarshalDownlink: newMACUnmarshaler(ttnpb.MACCommandIdentifier_CID_RX_PARAM_SETUP, "RxParamSetupReq", 4, func(phy band.Band, b []byte, cmd *ttnpb.MACCommand) error {
			cmd.Payload = &ttnpb.MACCommand_RxParamSetupReq_{
				RxParamSetupReq: &ttnpb.MACCommand_RxParamSetupReq{
					Rx1DataRateOffset: ttnpb.DataRateOffset(uint32((b[0] >> 4) & 0x7)),
					Rx2DataRateIndex:  ttnpb.DataRateIndex(b[0] & 0xf),
					Rx2Frequency:      byteutil.ParseUint64(b[1:4]) * phy.FreqMultiplier,
				},
			}
			return nil
		}),
	},

	ttnpb.MACCommandIdentifier_CID_DEV_STATUS: &MACCommandDescriptor{
		InitiatedByDevice: false,

		UplinkLength: 2,
		AppendUplink: func(phy band.Band, b []byte, cmd *ttnpb.MACCommand) ([]byte, error) {
			pld := cmd.GetDevStatusAns()
			if pld.Battery > math.MaxUint8 {
				return nil, errExpectedLowerOrEqual("Battery", math.MaxUint8)(math.MaxUint8)
			}
			if pld.Margin < -32 || pld.Margin > 31 {
				return nil, errExpectedBetween("Margin", -32, 31)(pld.Margin)
			}
			b = append(b, byte(pld.Battery))
			if pld.Margin < 0 {
				b = append(b, byte(-(pld.Margin+1)|(1<<5)))
			} else {
				b = append(b, byte(pld.Margin))
			}
			return b, nil
		},
		UnmarshalUplink: newMACUnmarshaler(ttnpb.MACCommandIdentifier_CID_DEV_STATUS, "DevStatusAns", 2, func(phy band.Band, b []byte, cmd *ttnpb.MACCommand) error {
			margin := int32(b[1] & 0x1f)
			if (b[1]>>5)&1 == 1 {
				margin = -margin - 1
			}
			cmd.Payload = &ttnpb.MACCommand_DevStatusAns_{
				DevStatusAns: &ttnpb.MACCommand_DevStatusAns{
					Battery: uint32(b[0]),
					Margin:  margin,
				},
			}
			return nil
		}),

		AppendDownlink: func(phy band.Band, b []byte, _ *ttnpb.MACCommand) ([]byte, error) {
			return b, nil
		},
		UnmarshalDownlink: newMACUnmarshaler(ttnpb.MACCommandIdentifier_CID_DEV_STATUS, "DevStatusReq", 0, nil),
	},

	ttnpb.MACCommandIdentifier_CID_NEW_CHANNEL: &MACCommandDescriptor{
		InitiatedByDevice: false,

		UplinkLength: 1,
		AppendUplink: func(phy band.Band, b []byte, cmd *ttnpb.MACCommand) ([]byte, error) {
			pld := cmd.GetNewChannelAns()
			var v byte
			if pld.FrequencyAck {
				v |= 1
			}
			if pld.DataRateAck {
				v |= (1 << 1)
			}
			b = append(b, v)
			return b, nil
		},
		UnmarshalUplink: newMACUnmarshaler(ttnpb.MACCommandIdentifier_CID_NEW_CHANNEL, "NewChannelAns", 1, func(phy band.Band, b []byte, cmd *ttnpb.MACCommand) error {
			cmd.Payload = &ttnpb.MACCommand_NewChannelAns_{
				NewChannelAns: &ttnpb.MACCommand_NewChannelAns{
					FrequencyAck: b[0]&1 == 1,
					DataRateAck:  (b[0]>>1)&1 == 1,
				},
			}
			return nil
		}),

		DownlinkLength: 5,
		AppendDownlink: func(phy band.Band, b []byte, cmd *ttnpb.MACCommand) ([]byte, error) {
			pld := cmd.GetNewChannelReq()
			if pld.ChannelIndex > math.MaxUint8 {
				return nil, errExpectedLowerOrEqual("ChannelIndex", math.MaxUint8)(pld.ChannelIndex)
			}
			b = append(b, byte(pld.ChannelIndex))

			if pld.Frequency > byteutil.MaxUint24*phy.FreqMultiplier {
				return nil, errExpectedLowerOrEqual("Frequency", byteutil.MaxUint24*phy.FreqMultiplier)(pld.Frequency)
			}
			b = byteutil.AppendUint64(b, pld.Frequency/phy.FreqMultiplier, 3)

			if pld.MinDataRateIndex > 15 {
				return nil, errExpectedLowerOrEqual("MinDataRateIndex", 15)(pld.MinDataRateIndex)
			}
			v := byte(pld.MinDataRateIndex)

			if pld.MaxDataRateIndex > 15 {
				return nil, errExpectedLowerOrEqual("MaxDataRateIndex", 15)(pld.MaxDataRateIndex)
			}
			v |= byte(pld.MaxDataRateIndex) << 4
			b = append(b, v)
			return b, nil
		},
		UnmarshalDownlink: newMACUnmarshaler(ttnpb.MACCommandIdentifier_CID_NEW_CHANNEL, "NewChannelReq", 5, func(phy band.Band, b []byte, cmd *ttnpb.MACCommand) error {
			cmd.Payload = &ttnpb.MACCommand_NewChannelReq_{
				NewChannelReq: &ttnpb.MACCommand_NewChannelReq{
					ChannelIndex:     uint32(b[0]),
					Frequency:        byteutil.ParseUint64(b[1:4]) * phy.FreqMultiplier,
					MinDataRateIndex: ttnpb.DataRateIndex(b[4] & 0xf),
					MaxDataRateIndex: ttnpb.DataRateIndex(b[4] >> 4),
				},
			}
			return nil
		}),
	},

	ttnpb.MACCommandIdentifier_CID_RX_TIMING_SETUP: &MACCommandDescriptor{
		InitiatedByDevice: false,

		AppendUplink: func(phy band.Band, b []byte, _ *ttnpb.MACCommand) ([]byte, error) {
			return b, nil
		},
		UnmarshalUplink: newMACUnmarshaler(ttnpb.MACCommandIdentifier_CID_RX_TIMING_SETUP, "RxTimingSetupAns", 0, nil),

		DownlinkLength: 1,
		AppendDownlink: func(phy band.Band, b []byte, cmd *ttnpb.MACCommand) ([]byte, error) {
			pld := cmd.GetRxTimingSetupReq()
			if pld.Delay > 15 {
				return nil, errExpectedLowerOrEqual("Delay", 15)(pld.Delay)
			}
			b = append(b, byte(pld.Delay))
			return b, nil
		},
		UnmarshalDownlink: newMACUnmarshaler(ttnpb.MACCommandIdentifier_CID_RX_TIMING_SETUP, "RxTimingSetupReq", 1, func(phy band.Band, b []byte, cmd *ttnpb.MACCommand) error {
			cmd.Payload = &ttnpb.MACCommand_RxTimingSetupReq_{
				RxTimingSetupReq: &ttnpb.MACCommand_RxTimingSetupReq{
					Delay: ttnpb.RxDelay(b[0] & 0xf),
				},
			}
			return nil
		}),
	},

	ttnpb.MACCommandIdentifier_CID_TX_PARAM_SETUP: &MACCommandDescriptor{
		InitiatedByDevice: false,

		AppendUplink: func(phy band.Band, b []byte, _ *ttnpb.MACCommand) ([]byte, error) {
			return b, nil
		},
		UnmarshalUplink: newMACUnmarshaler(ttnpb.MACCommandIdentifier_CID_TX_PARAM_SETUP, "TxParamSetupAns", 0, nil),

		DownlinkLength: 1,
		AppendDownlink: func(phy band.Band, b []byte, cmd *ttnpb.MACCommand) ([]byte, error) {
			pld := cmd.GetTxParamSetupReq()

			v := byte(pld.MaxEirpIndex)
			if pld.UplinkDwellTime {
				v |= (1 << 4)
			}
			if pld.DownlinkDwellTime {
				v |= (1 << 5)
			}
			b = append(b, v)
			return b, nil
		},
		UnmarshalDownlink: newMACUnmarshaler(ttnpb.MACCommandIdentifier_CID_TX_PARAM_SETUP, "TxParamSetupReq", 1, func(phy band.Band, b []byte, cmd *ttnpb.MACCommand) error {
			cmd.Payload = &ttnpb.MACCommand_TxParamSetupReq_{
				TxParamSetupReq: &ttnpb.MACCommand_TxParamSetupReq{
					MaxEirpIndex:      ttnpb.DeviceEIRP(b[0] & 0xf),
					UplinkDwellTime:   (b[0]>>4)&1 == 1,
					DownlinkDwellTime: (b[0]>>5)&1 == 1,
				},
			}
			return nil
		}),
	},

	ttnpb.MACCommandIdentifier_CID_DL_CHANNEL: &MACCommandDescriptor{
		InitiatedByDevice: false,

		UplinkLength: 1,
		AppendUplink: func(phy band.Band, b []byte, cmd *ttnpb.MACCommand) ([]byte, error) {
			pld := cmd.GetDlChannelAns()
			var v byte
			if pld.ChannelIndexAck {
				v |= 1
			}
			if pld.FrequencyAck {
				v |= (1 << 1)
			}
			b = append(b, v)
			return b, nil
		},
		UnmarshalUplink: newMACUnmarshaler(ttnpb.MACCommandIdentifier_CID_DL_CHANNEL, "DLChannelAns", 1, func(phy band.Band, b []byte, cmd *ttnpb.MACCommand) error {
			cmd.Payload = &ttnpb.MACCommand_DlChannelAns{
				DlChannelAns: &ttnpb.MACCommand_DLChannelAns{
					ChannelIndexAck: b[0]&1 == 1,
					FrequencyAck:    (b[0]>>1)&1 == 1,
				},
			}
			return nil
		}),

		DownlinkLength: 4,
		AppendDownlink: func(phy band.Band, b []byte, cmd *ttnpb.MACCommand) ([]byte, error) {
			pld := cmd.GetDlChannelReq()
			if pld.ChannelIndex > math.MaxUint8 {
				return nil, errExpectedLowerOrEqual("ChannelIndex", math.MaxUint8)(pld.ChannelIndex)
			}
			b = append(b, byte(pld.ChannelIndex))

			if pld.Frequency < 100000 || pld.Frequency > byteutil.MaxUint24*phy.FreqMultiplier {
				return nil, errExpectedBetween("Frequency", 100000, byteutil.MaxUint24*phy.FreqMultiplier)(pld.Frequency)
			}
			b = byteutil.AppendUint64(b, pld.Frequency/phy.FreqMultiplier, 3)
			return b, nil
		},
		UnmarshalDownlink: newMACUnmarshaler(ttnpb.MACCommandIdentifier_CID_DL_CHANNEL, "DLChannelReq", 4, func(phy band.Band, b []byte, cmd *ttnpb.MACCommand) error {
			cmd.Payload = &ttnpb.MACCommand_DlChannelReq{
				DlChannelReq: &ttnpb.MACCommand_DLChannelReq{
					ChannelIndex: uint32(b[0]),
					Frequency:    byteutil.ParseUint64(b[1:4]) * phy.FreqMultiplier,
				},
			}
			return nil
		}),
	},

	ttnpb.MACCommandIdentifier_CID_REKEY: &MACCommandDescriptor{
		InitiatedByDevice: true,

		UplinkLength: 1,
		AppendUplink: func(phy band.Band, b []byte, cmd *ttnpb.MACCommand) ([]byte, error) {
			pld := cmd.GetRekeyInd()
			if pld.MinorVersion > 15 {
				return nil, errExpectedLowerOrEqual("MinorVersion", 15)(pld.MinorVersion)
			}
			b = append(b, byte(pld.MinorVersion))
			return b, nil
		},
		UnmarshalUplink: newMACUnmarshaler(ttnpb.MACCommandIdentifier_CID_REKEY, "RekeyInd", 1, func(phy band.Band, b []byte, cmd *ttnpb.MACCommand) error {
			cmd.Payload = &ttnpb.MACCommand_RekeyInd_{
				RekeyInd: &ttnpb.MACCommand_RekeyInd{
					MinorVersion: ttnpb.Minor(b[0] & 0xf),
				},
			}
			return nil
		}),

		DownlinkLength: 1,
		AppendDownlink: func(phy band.Band, b []byte, cmd *ttnpb.MACCommand) ([]byte, error) {
			pld := cmd.GetRekeyConf()
			if pld.MinorVersion > 15 {
				return nil, errExpectedLowerOrEqual("MinorVersion", 15)(pld.MinorVersion)
			}
			b = append(b, byte(pld.MinorVersion))
			return b, nil
		},
		UnmarshalDownlink: newMACUnmarshaler(ttnpb.MACCommandIdentifier_CID_REKEY, "RekeyConf", 1, func(phy band.Band, b []byte, cmd *ttnpb.MACCommand) error {
			cmd.Payload = &ttnpb.MACCommand_RekeyConf_{
				RekeyConf: &ttnpb.MACCommand_RekeyConf{
					MinorVersion: ttnpb.Minor(b[0] & 0xf),
				},
			}
			return nil
		}),
	},

	ttnpb.MACCommandIdentifier_CID_ADR_PARAM_SETUP: &MACCommandDescriptor{
		InitiatedByDevice: false,

		AppendUplink: func(phy band.Band, b []byte, _ *ttnpb.MACCommand) ([]byte, error) {
			return b, nil
		},
		UnmarshalUplink: newMACUnmarshaler(ttnpb.MACCommandIdentifier_CID_ADR_PARAM_SETUP, "ADRParamSetupAns", 0, nil),

		DownlinkLength: 1,
		AppendDownlink: func(phy band.Band, b []byte, cmd *ttnpb.MACCommand) ([]byte, error) {
			pld := cmd.GetAdrParamSetupReq()
			if 1 > pld.AdrAckDelayExponent || pld.AdrAckDelayExponent > 32768 {
				return nil, errExpectedBetween("ADRAckDelay", 1, 32768)(pld.AdrAckDelayExponent)
			}
			v := byte(pld.AdrAckDelayExponent)

			if 1 > pld.AdrAckLimitExponent || pld.AdrAckLimitExponent > 32768 {
				return nil, errExpectedBetween("ADRAckLimit", 1, 32768)(pld.AdrAckLimitExponent)
			}
			v |= byte(pld.AdrAckLimitExponent) << 4

			b = append(b, v)
			return b, nil
		},
		UnmarshalDownlink: newMACUnmarshaler(ttnpb.MACCommandIdentifier_CID_ADR_PARAM_SETUP, "ADRParamSetupReq", 1, func(phy band.Band, b []byte, cmd *ttnpb.MACCommand) error {
			cmd.Payload = &ttnpb.MACCommand_AdrParamSetupReq{
				AdrParamSetupReq: &ttnpb.MACCommand_ADRParamSetupReq{
					AdrAckDelayExponent: ttnpb.ADRAckDelayExponent(b[0] & 0xf),
					AdrAckLimitExponent: ttnpb.ADRAckLimitExponent(b[0] >> 4),
				},
			}
			return nil
		}),
	},

	ttnpb.MACCommandIdentifier_CID_DEVICE_TIME: &MACCommandDescriptor{
		InitiatedByDevice: true,

		AppendUplink: func(phy band.Band, b []byte, _ *ttnpb.MACCommand) ([]byte, error) {
			return b, nil
		},
		UnmarshalUplink: newMACUnmarshaler(ttnpb.MACCommandIdentifier_CID_DEVICE_TIME, "DeviceTimeReq", 0, nil),

		DownlinkLength: 5,
		AppendDownlink: func(phy band.Band, b []byte, cmd *ttnpb.MACCommand) ([]byte, error) {
			pld := cmd.GetDeviceTimeAns()

			t := gpstime.ToGPS(*ttnpb.StdTime(pld.Time))
			sec := t / time.Second
			if sec > math.MaxUint32 {
				return nil, errExpectedLowerOrEqual("Time", uint32(math.MaxUint32))(sec)
			}
			b = byteutil.AppendUint32(b, uint32(sec), 4)
			b = append(b, byte((t-sec*time.Second)/fractStep))
			return b, nil
		},
		UnmarshalDownlink: newMACUnmarshaler(ttnpb.MACCommandIdentifier_CID_DEVICE_TIME, "DeviceTimeAns", 5, func(phy band.Band, b []byte, cmd *ttnpb.MACCommand) error {
			cmd.Payload = &ttnpb.MACCommand_DeviceTimeAns_{
				DeviceTimeAns: &ttnpb.MACCommand_DeviceTimeAns{
					Time: timestamppb.New(gpstime.Parse(time.Duration(byteutil.ParseUint32(b[0:4]))*time.Second + time.Duration(b[4])*fractStep)),
				},
			}
			return nil
		}),
	},

	ttnpb.MACCommandIdentifier_CID_FORCE_REJOIN: &MACCommandDescriptor{
		InitiatedByDevice: false,

		DownlinkLength: 2,
		AppendDownlink: func(phy band.Band, b []byte, cmd *ttnpb.MACCommand) ([]byte, error) {
			pld := cmd.GetForceRejoinReq()

			if pld.PeriodExponent > 7 {
				return nil, errExpectedLowerOrEqual("PeriodExponent", 7)(pld.PeriodExponent)
			}

			v := byte(pld.PeriodExponent) << 3

			if pld.MaxRetries > 7 {
				return nil, errExpectedLowerOrEqual("MaxRetries", 7)(pld.MaxRetries)
			}
			v |= byte(pld.MaxRetries)
			b = append(b, v)

			if pld.RejoinType > 2 {
				return nil, errExpectedLowerOrEqual("RejoinType", 2)(pld.RejoinType)
			}

			v = byte(pld.RejoinType) << 4

			if pld.DataRateIndex > 15 {
				return nil, errExpectedLowerOrEqual("DataRateIndex", 15)(pld.DataRateIndex)
			}
			v |= byte(pld.DataRateIndex)
			b = append(b, v)
			return b, nil
		},
		UnmarshalDownlink: newMACUnmarshaler(ttnpb.MACCommandIdentifier_CID_FORCE_REJOIN, "ForceRejoinReq", 2, func(phy band.Band, b []byte, cmd *ttnpb.MACCommand) error {
			cmd.Payload = &ttnpb.MACCommand_ForceRejoinReq_{
				ForceRejoinReq: &ttnpb.MACCommand_ForceRejoinReq{
					PeriodExponent: ttnpb.RejoinPeriodExponent(uint32(b[0] >> 3)),
					MaxRetries:     uint32(b[0] & 0x7),
					RejoinType:     ttnpb.RejoinRequestType(b[1] >> 4),
					DataRateIndex:  ttnpb.DataRateIndex(b[1] & 0xf),
				},
			}
			return nil
		}),
	},

	ttnpb.MACCommandIdentifier_CID_REJOIN_PARAM_SETUP: &MACCommandDescriptor{
		InitiatedByDevice: false,

		UplinkLength: 1,
		AppendUplink: func(phy band.Band, b []byte, cmd *ttnpb.MACCommand) ([]byte, error) {
			pld := cmd.GetRejoinParamSetupAns()

			var v byte
			if pld.MaxTimeExponentAck {
				v |= 1
			}
			b = append(b, v)
			return b, nil
		},
		UnmarshalUplink: newMACUnmarshaler(ttnpb.MACCommandIdentifier_CID_REJOIN_PARAM_SETUP, "RejoinParamSetupAns", 1, func(phy band.Band, b []byte, cmd *ttnpb.MACCommand) error {
			cmd.Payload = &ttnpb.MACCommand_RejoinParamSetupAns_{
				RejoinParamSetupAns: &ttnpb.MACCommand_RejoinParamSetupAns{
					MaxTimeExponentAck: b[0]&1 == 1,
				},
			}
			return nil
		}),

		DownlinkLength: 1,
		AppendDownlink: func(phy band.Band, b []byte, cmd *ttnpb.MACCommand) ([]byte, error) {
			pld := cmd.GetRejoinParamSetupReq()

			if pld.MaxTimeExponent > 15 {
				return nil, errExpectedLowerOrEqual("MaxTimeExponent", 15)(pld.MaxTimeExponent)
			}
			v := byte(pld.MaxTimeExponent) << 4

			if pld.MaxCountExponent > 15 {
				return nil, errExpectedLowerOrEqual("MaxCountExponent", 15)(pld.MaxCountExponent)
			}
			v |= byte(pld.MaxCountExponent)
			b = append(b, v)
			return b, nil
		},
		UnmarshalDownlink: newMACUnmarshaler(ttnpb.MACCommandIdentifier_CID_REJOIN_PARAM_SETUP, "RejoinParamSetupReq", 1, func(phy band.Band, b []byte, cmd *ttnpb.MACCommand) error {
			cmd.Payload = &ttnpb.MACCommand_RejoinParamSetupReq_{
				RejoinParamSetupReq: &ttnpb.MACCommand_RejoinParamSetupReq{
					MaxTimeExponent:  ttnpb.RejoinTimeExponent(uint32(b[0] >> 4)),
					MaxCountExponent: ttnpb.RejoinCountExponent(uint32(b[0] & 0xf)),
				},
			}
			return nil
		}),
	},

	ttnpb.MACCommandIdentifier_CID_PING_SLOT_INFO: &MACCommandDescriptor{
		InitiatedByDevice: true,

		UplinkLength: 1,
		AppendUplink: func(phy band.Band, b []byte, cmd *ttnpb.MACCommand) ([]byte, error) {
			pld := cmd.GetPingSlotInfoReq()

			if pld.Period > 7 {
				return nil, errExpectedLowerOrEqual("Period", 15)(pld.Period)
			}
			b = append(b, byte(pld.Period))
			return b, nil
		},
		UnmarshalUplink: newMACUnmarshaler(ttnpb.MACCommandIdentifier_CID_PING_SLOT_INFO, "PingSlotInfoReq", 1, func(phy band.Band, b []byte, cmd *ttnpb.MACCommand) error {
			cmd.Payload = &ttnpb.MACCommand_PingSlotInfoReq_{
				PingSlotInfoReq: &ttnpb.MACCommand_PingSlotInfoReq{
					Period: ttnpb.PingSlotPeriod(b[0] & 0x7),
				},
			}
			return nil
		}),

		AppendDownlink: func(phy band.Band, b []byte, _ *ttnpb.MACCommand) ([]byte, error) {
			return b, nil
		},
		UnmarshalDownlink: newMACUnmarshaler(ttnpb.MACCommandIdentifier_CID_PING_SLOT_INFO, "PingSlotInfoAns", 0, nil),
	},

	ttnpb.MACCommandIdentifier_CID_PING_SLOT_CHANNEL: &MACCommandDescriptor{
		InitiatedByDevice: false,

		UplinkLength: 1,
		AppendUplink: func(phy band.Band, b []byte, cmd *ttnpb.MACCommand) ([]byte, error) {
			pld := cmd.GetPingSlotChannelAns()

			var v byte
			if pld.FrequencyAck {
				v |= 1
			}
			if pld.DataRateIndexAck {
				v |= (1 << 1)
			}
			b = append(b, v)
			return b, nil
		},
		UnmarshalUplink: newMACUnmarshaler(ttnpb.MACCommandIdentifier_CID_PING_SLOT_CHANNEL, "PingSlotChannelAns", 1, func(phy band.Band, b []byte, cmd *ttnpb.MACCommand) error {
			cmd.Payload = &ttnpb.MACCommand_PingSlotChannelAns_{
				PingSlotChannelAns: &ttnpb.MACCommand_PingSlotChannelAns{
					FrequencyAck:     b[0]&1 == 1,
					DataRateIndexAck: (b[0]>>1)&1 == 1,
				},
			}
			return nil
		}),

		DownlinkLength: 4,
		AppendDownlink: func(phy band.Band, b []byte, cmd *ttnpb.MACCommand) ([]byte, error) {
			pld := cmd.GetPingSlotChannelReq()

			if pld.Frequency < 100000 || pld.Frequency > byteutil.MaxUint24*phy.FreqMultiplier {
				return nil, errExpectedBetween("Frequency", 100000, byteutil.MaxUint24*phy.FreqMultiplier)(pld.Frequency)
			}
			b = byteutil.AppendUint64(b, pld.Frequency/phy.FreqMultiplier, 3)

			if pld.DataRateIndex > 15 {
				return nil, errExpectedLowerOrEqual("DataRateIndex", 15)(pld.DataRateIndex)
			}
			b = append(b, byte(pld.DataRateIndex))
			return b, nil
		},
		UnmarshalDownlink: newMACUnmarshaler(ttnpb.MACCommandIdentifier_CID_PING_SLOT_CHANNEL, "PingSlotChannelReq", 4, func(phy band.Band, b []byte, cmd *ttnpb.MACCommand) error {
			cmd.Payload = &ttnpb.MACCommand_PingSlotChannelReq_{
				PingSlotChannelReq: &ttnpb.MACCommand_PingSlotChannelReq{
					Frequency:     byteutil.ParseUint64(b[0:3]) * phy.FreqMultiplier,
					DataRateIndex: ttnpb.DataRateIndex(b[3] & 0xf),
				},
			}
			return nil
		}),
	},

	ttnpb.MACCommandIdentifier_CID_BEACON_TIMING: &MACCommandDescriptor{
		InitiatedByDevice: true,

		AppendUplink: func(phy band.Band, b []byte, _ *ttnpb.MACCommand) ([]byte, error) {
			return b, nil
		},
		UnmarshalUplink: newMACUnmarshaler(ttnpb.MACCommandIdentifier_CID_BEACON_TIMING, "BeaconTimingReq", 0, nil),

		DownlinkLength: 3,
		AppendDownlink: func(phy band.Band, b []byte, cmd *ttnpb.MACCommand) ([]byte, error) {
			pld := cmd.GetBeaconTimingAns()

			if pld.Delay > math.MaxUint16 {
				return nil, errExpectedLowerOrEqual("Delay", math.MaxUint16)(pld.Delay)
			}
			b = byteutil.AppendUint32(b, pld.Delay, 2)

			if pld.ChannelIndex > math.MaxUint8 {
				return nil, errExpectedLowerOrEqual("ChannelIndex", math.MaxUint8)(pld.ChannelIndex)
			}
			b = append(b, byte(pld.ChannelIndex))

			return b, nil
		},
		UnmarshalDownlink: newMACUnmarshaler(ttnpb.MACCommandIdentifier_CID_BEACON_TIMING, "BeaconTimingAns", 3, func(phy band.Band, b []byte, cmd *ttnpb.MACCommand) error {
			cmd.Payload = &ttnpb.MACCommand_BeaconTimingAns_{
				BeaconTimingAns: &ttnpb.MACCommand_BeaconTimingAns{
					Delay:        byteutil.ParseUint32(b[0:2]),
					ChannelIndex: uint32(b[2]),
				},
			}
			return nil
		}),
	},

	ttnpb.MACCommandIdentifier_CID_BEACON_FREQ: &MACCommandDescriptor{
		InitiatedByDevice: false,

		UplinkLength: 1,
		AppendUplink: func(phy band.Band, b []byte, cmd *ttnpb.MACCommand) ([]byte, error) {
			pld := cmd.GetBeaconFreqAns()
			var v byte
			if pld.FrequencyAck {
				v |= 1
			}
			b = append(b, v)
			return b, nil
		},
		UnmarshalUplink: newMACUnmarshaler(ttnpb.MACCommandIdentifier_CID_BEACON_FREQ, "BeaconFreqAns", 1, func(phy band.Band, b []byte, cmd *ttnpb.MACCommand) error {
			cmd.Payload = &ttnpb.MACCommand_BeaconFreqAns_{
				BeaconFreqAns: &ttnpb.MACCommand_BeaconFreqAns{
					FrequencyAck: b[0]&1 == 1,
				},
			}
			return nil
		}),

		DownlinkLength: 3,
		AppendDownlink: func(phy band.Band, b []byte, cmd *ttnpb.MACCommand) ([]byte, error) {
			pld := cmd.GetBeaconFreqReq()
			if pld.Frequency < 100000 || pld.Frequency > byteutil.MaxUint24*phy.FreqMultiplier {
				return nil, errExpectedBetween("Frequency", 100000, byteutil.MaxUint24*phy.FreqMultiplier)(pld.Frequency)
			}
			b = byteutil.AppendUint64(b, pld.Frequency/phy.FreqMultiplier, 3)
			return b, nil
		},
		UnmarshalDownlink: newMACUnmarshaler(ttnpb.MACCommandIdentifier_CID_BEACON_FREQ, "BeaconFreqReq", 3, func(phy band.Band, b []byte, cmd *ttnpb.MACCommand) error {
			cmd.Payload = &ttnpb.MACCommand_BeaconFreqReq_{
				BeaconFreqReq: &ttnpb.MACCommand_BeaconFreqReq{
					Frequency: byteutil.ParseUint64(b[0:3]) * phy.FreqMultiplier,
				},
			}
			return nil
		}),
	},

	ttnpb.MACCommandIdentifier_CID_DEVICE_MODE: &MACCommandDescriptor{
		InitiatedByDevice: true,

		UplinkLength: 1,
		AppendUplink: func(phy band.Band, b []byte, cmd *ttnpb.MACCommand) ([]byte, error) {
			pld := cmd.GetDeviceModeInd()
			b = append(b, byte(pld.Class))
			return b, nil
		},
		UnmarshalUplink: newMACUnmarshaler(ttnpb.MACCommandIdentifier_CID_DEVICE_MODE, "DeviceModeInd", 1, func(phy band.Band, b []byte, cmd *ttnpb.MACCommand) error {
			cmd.Payload = &ttnpb.MACCommand_DeviceModeInd_{
				DeviceModeInd: &ttnpb.MACCommand_DeviceModeInd{
					Class: ttnpb.Class(b[0]),
				},
			}
			return nil
		}),

		DownlinkLength: 1,
		AppendDownlink: func(phy band.Band, b []byte, cmd *ttnpb.MACCommand) ([]byte, error) {
			pld := cmd.GetDeviceModeConf()
			b = append(b, byte(pld.Class))
			return b, nil
		},
		UnmarshalDownlink: newMACUnmarshaler(ttnpb.MACCommandIdentifier_CID_DEVICE_MODE, "DeviceModeConf", 1, func(phy band.Band, b []byte, cmd *ttnpb.MACCommand) error {
			cmd.Payload = &ttnpb.MACCommand_DeviceModeConf_{
				DeviceModeConf: &ttnpb.MACCommand_DeviceModeConf{
					Class: ttnpb.Class(b[0]),
				},
			}
			return nil
		}),
	},

	ttnpb.MACCommandIdentifier_CID_RELAY_CONF: &MACCommandDescriptor{
		InitiatedByDevice: false,

		UplinkLength: 1,
		AppendUplink: func(phy band.Band, b []byte, cmd *ttnpb.MACCommand) ([]byte, error) {
			pld := cmd.GetRelayConfAns()
			var status byte
			if pld.SecondChannelFrequencyAck {
				status |= 1
			}
			if pld.SecondChannelAckOffsetAck {
				status |= (1 << 1)
			}
			if pld.SecondChannelDataRateIndexAck {
				status |= (1 << 2)
			}
			if pld.SecondChannelIndexAck {
				status |= (1 << 3)
			}
			if pld.DefaultChannelIndexAck {
				status |= (1 << 4)
			}
			if pld.CadPeriodicityAck {
				status |= (1 << 5)
			}
			b = append(b, status)
			return b, nil
		},
		UnmarshalUplink: newMACUnmarshaler(
			ttnpb.MACCommandIdentifier_CID_RELAY_CONF,
			"RelayConfAns",
			1,
			func(phy band.Band, b []byte, cmd *ttnpb.MACCommand) error {
				cmd.Payload = &ttnpb.MACCommand_RelayConfAns_{
					RelayConfAns: &ttnpb.MACCommand_RelayConfAns{
						SecondChannelFrequencyAck:     b[0]&1 == 1,
						SecondChannelAckOffsetAck:     (b[0]>>1)&1 == 1,
						SecondChannelDataRateIndexAck: (b[0]>>2)&1 == 1,
						SecondChannelIndexAck:         (b[0]>>3)&1 == 1,
						DefaultChannelIndexAck:        (b[0]>>4)&1 == 1,
						CadPeriodicityAck:             (b[0]>>5)&1 == 1,
					},
				}
				return nil
			},
		),

		DownlinkLength: 5,
		AppendDownlink: func(phy band.Band, b []byte, cmd *ttnpb.MACCommand) ([]byte, error) {
			pld := cmd.GetRelayConfReq()
			conf := pld.GetConfiguration()
			if conf == nil {
				b = append(b, 0x00, 0x00, 0x00, 0x00, 0x00)
				return b, nil
			}
			var chSettings uint16
			chSettings |= 1 << 13
			if conf.CadPeriodicity > 5 {
				return nil, errExpectedLowerOrEqual("CADPeriodicity", 5)(conf.CadPeriodicity)
			}
			chSettings |= uint16(conf.CadPeriodicity&0x7) << 10
			if conf.DefaultChannelIndex > 1 {
				return nil, errExpectedLowerOrEqual("DefaultChannelIndex", 1)(conf.DefaultChannelIndex)
			}
			chSettings |= uint16(conf.DefaultChannelIndex&0x1) << 9
			var secondChFrequency uint64
			if secondCh := conf.SecondChannel; secondCh != nil {
				chSettings |= 1 << 7
				if secondCh.DataRateIndex > 15 {
					return nil, errExpectedLowerOrEqual("SecondChannelDataRateIndex", 15)(secondCh.DataRateIndex)
				}
				chSettings |= uint16(secondCh.DataRateIndex&0xf) << 3
				if secondCh.AckOffset > 5 {
					return nil, errExpectedLowerOrEqual("SecondChannelAckOffset", 5)(secondCh.AckOffset)
				}
				chSettings |= uint16(secondCh.AckOffset & 0x7)
				if secondCh.Frequency < 100000 || secondCh.Frequency > byteutil.MaxUint24*phy.FreqMultiplier {
					return nil, errExpectedBetween(
						"SecondChannelFrequency", 100000, byteutil.MaxUint24*phy.FreqMultiplier,
					)(secondCh.Frequency)
				}
				secondChFrequency = secondCh.Frequency
			}
			b = byteutil.AppendUint16(b, chSettings, 2)
			b = byteutil.AppendUint64(b, secondChFrequency/phy.FreqMultiplier, 3)
			return b, nil
		},
		UnmarshalDownlink: newMACUnmarshaler(
			ttnpb.MACCommandIdentifier_CID_RELAY_CONF,
			"RelayConfReq",
			5,
			func(phy band.Band, b []byte, cmd *ttnpb.MACCommand) error {
				payload := &ttnpb.MACCommand_RelayConfReq_{
					RelayConfReq: &ttnpb.MACCommand_RelayConfReq{},
				}
				cmd.Payload = payload
				chSettings := byteutil.ParseUint16(b[0:2])
				if chSettings&(1<<13) == 0 {
					return nil
				}
				conf := &ttnpb.MACCommand_RelayConfReq_Configuration{
					SecondChannel:       nil,
					DefaultChannelIndex: uint32(chSettings>>9) & 0x1,
					CadPeriodicity:      ttnpb.RelayCADPeriodicity(chSettings>>10) & 0x7,
				}
				if chSettings&(1<<7) != 0 {
					conf.SecondChannel = &ttnpb.RelaySecondChannel{
						Frequency:     byteutil.ParseUint64(b[2:5]) * phy.FreqMultiplier,
						AckOffset:     ttnpb.RelaySecondChAckOffset(chSettings) & 0x7,
						DataRateIndex: ttnpb.DataRateIndex(chSettings>>3) & 0xf,
					}
				}
				payload.RelayConfReq.Configuration = conf
				return nil
			},
		),
	},
	ttnpb.MACCommandIdentifier_CID_RELAY_END_DEVICE_CONF: &MACCommandDescriptor{
		InitiatedByDevice: false,

		UplinkLength: 1,
		AppendUplink: func(phy band.Band, b []byte, cmd *ttnpb.MACCommand) ([]byte, error) {
			pld := cmd.GetRelayEndDeviceConfAns()
			var status byte
			if pld.SecondChannelFrequencyAck {
				status |= 1
			}
			if pld.SecondChannelDataRateIndexAck {
				status |= (1 << 1)
			}
			if pld.SecondChannelIndexAck {
				status |= (1 << 2)
			}
			if pld.BackoffAck {
				status |= (1 << 3)
			}
			b = append(b, status)
			return b, nil
		},
		UnmarshalUplink: newMACUnmarshaler(
			ttnpb.MACCommandIdentifier_CID_RELAY_END_DEVICE_CONF,
			"RelayEndDeviceConfAns",
			1,
			func(phy band.Band, b []byte, cmd *ttnpb.MACCommand) error {
				cmd.Payload = &ttnpb.MACCommand_RelayEndDeviceConfAns_{
					RelayEndDeviceConfAns: &ttnpb.MACCommand_RelayEndDeviceConfAns{
						SecondChannelFrequencyAck:     b[0]&1 == 1,
						SecondChannelDataRateIndexAck: (b[0]>>1)&1 == 1,
						SecondChannelIndexAck:         (b[0]>>2)&1 == 1,
						BackoffAck:                    (b[0]>>3)&1 == 1,
					},
				}
				return nil
			},
		),

		DownlinkLength: 6,
		AppendDownlink: func(phy band.Band, b []byte, cmd *ttnpb.MACCommand) ([]byte, error) {
			pld := cmd.GetRelayEndDeviceConfReq()
			conf := pld.GetConfiguration()
			if conf == nil {
				b = append(b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00)
				return b, nil
			}
			var mode byte
			switch conf.Mode.(type) {
			case *ttnpb.MACCommand_RelayEndDeviceConfReq_Configuration_Always:
				mode |= 1 << 2
			case *ttnpb.MACCommand_RelayEndDeviceConfReq_Configuration_Dynamic:
				mode |= 2 << 2
				smartEnableLevel := conf.GetDynamic().GetSmartEnableLevel()
				if smartEnableLevel > 3 {
					return nil, errExpectedLowerOrEqual("SmartEnableLevel", 3)(smartEnableLevel)
				}
				mode |= byte(conf.GetDynamic().GetSmartEnableLevel() & 0x3)
			case *ttnpb.MACCommand_RelayEndDeviceConfReq_Configuration_EndDeviceControlled:
				mode |= 3 << 2
			default:
				return nil, errExpectedLengthLowerOrEqual("mode", 3)(conf.Mode)
			}
			b = append(b, mode)
			var chSettings uint16
			if conf.Backoff > 63 {
				return nil, errExpectedLowerOrEqual("Backoff", 63)(conf.Backoff)
			}
			chSettings |= uint16(conf.Backoff&0x3f) << 9
			var secondChFrequency uint64
			if secondCh := conf.SecondChannel; secondCh != nil {
				chSettings |= 1 << 7
				if secondCh.DataRateIndex > 15 {
					return nil, errExpectedLowerOrEqual("SecondChannelDataRateIndex", 15)(secondCh.DataRateIndex)
				}
				chSettings |= uint16(secondCh.DataRateIndex&0xf) << 3
				if secondCh.Frequency < 100000 || secondCh.Frequency > byteutil.MaxUint24*phy.FreqMultiplier {
					return nil, errExpectedBetween(
						"SecondChannelFrequency", 100000, byteutil.MaxUint24*phy.FreqMultiplier,
					)(secondCh.Frequency)
				}
				if secondCh.AckOffset > 5 {
					return nil, errExpectedLowerOrEqual("SecondChannelAckOffset", 5)(secondCh.AckOffset)
				}
				chSettings |= uint16(secondCh.AckOffset & 0x7)
				secondChFrequency = secondCh.Frequency
			}
			b = byteutil.AppendUint16(b, chSettings, 2)
			b = byteutil.AppendUint64(b, secondChFrequency/phy.FreqMultiplier, 3)
			return b, nil
		},
		UnmarshalDownlink: newMACUnmarshaler(
			ttnpb.MACCommandIdentifier_CID_RELAY_END_DEVICE_CONF,
			"RelayEndDeviceConfReq",
			6,
			func(phy band.Band, b []byte, cmd *ttnpb.MACCommand) error {
				payload := &ttnpb.MACCommand_RelayEndDeviceConfReq_{
					RelayEndDeviceConfReq: &ttnpb.MACCommand_RelayEndDeviceConfReq{},
				}
				cmd.Payload = payload
				var conf *ttnpb.MACCommand_RelayEndDeviceConfReq_Configuration
				switch mode := b[0] >> 2; mode {
				case 0:
					return nil
				case 1:
					conf = &ttnpb.MACCommand_RelayEndDeviceConfReq_Configuration{
						Mode: &ttnpb.MACCommand_RelayEndDeviceConfReq_Configuration_Always{},
					}
				case 2:
					conf = &ttnpb.MACCommand_RelayEndDeviceConfReq_Configuration{
						Mode: &ttnpb.MACCommand_RelayEndDeviceConfReq_Configuration_Dynamic{
							Dynamic: &ttnpb.RelayEndDeviceDynamicMode{
								SmartEnableLevel: ttnpb.RelaySmartEnableLevel(b[0] & 0x3),
							},
						},
					}
				case 3:
					conf = &ttnpb.MACCommand_RelayEndDeviceConfReq_Configuration{
						Mode: &ttnpb.MACCommand_RelayEndDeviceConfReq_Configuration_EndDeviceControlled{},
					}
				default:
					return errExpectedLengthLowerOrEqual("mode", 3)(mode)
				}
				payload.RelayEndDeviceConfReq.Configuration = conf
				chSettings := byteutil.ParseUint16(b[1:3])
				conf.Backoff = uint32(chSettings>>9) & 0x3f
				if chSettings&(1<<7) != 0 {
					conf.SecondChannel = &ttnpb.RelaySecondChannel{
						Frequency:     byteutil.ParseUint64(b[3:6]) * phy.FreqMultiplier,
						AckOffset:     ttnpb.RelaySecondChAckOffset(chSettings) & 0x7,
						DataRateIndex: ttnpb.DataRateIndex(chSettings>>3) & 0xf,
					}
				}
				return nil
			}),
	},
	ttnpb.MACCommandIdentifier_CID_RELAY_UPDATE_UPLINK_LIST: &MACCommandDescriptor{
		InitiatedByDevice: false,

		UplinkLength: 0,
		AppendUplink: func(phy band.Band, b []byte, cmd *ttnpb.MACCommand) ([]byte, error) {
			return b, nil
		},
		UnmarshalUplink: newMACUnmarshaler(
			ttnpb.MACCommandIdentifier_CID_RELAY_UPDATE_UPLINK_LIST,
			"RelayUpdateUplinkListAns",
			0,
			func(phy band.Band, b []byte, cmd *ttnpb.MACCommand) error {
				cmd.Payload = &ttnpb.MACCommand_RelayUpdateUplinkListAns_{
					RelayUpdateUplinkListAns: &ttnpb.MACCommand_RelayUpdateUplinkListAns{},
				}
				return nil
			},
		),

		DownlinkLength: 26,
		AppendDownlink: func(phy band.Band, b []byte, cmd *ttnpb.MACCommand) ([]byte, error) {
			pld := cmd.GetRelayUpdateUplinkListReq()
			if pld.RuleIndex > 15 {
				return nil, errExpectedLowerOrEqual("RuleIndex", 15)(pld.RuleIndex)
			}
			b = append(b, byte(pld.RuleIndex))
			var uplinkLimit byte = 0x3f
			if limits := pld.ForwardLimits; limits != nil {
				if limits.BucketSize > 3 {
					return nil, errExpectedLowerOrEqual("BucketSize", 3)(limits.BucketSize)
				}
				uplinkLimit = byte(limits.BucketSize&0x3) << 6
				if limits.ReloadRate > 62 {
					return nil, errExpectedLowerOrEqual("ReloadRate", 62)(limits.ReloadRate)
				}
				uplinkLimit |= byte(limits.ReloadRate)
			}
			b = append(b, uplinkLimit)
			if n := len(pld.DevAddr); n != 4 {
				return nil, errExpectedLengthEncodedEqual("DevAddr", 4)(n)
			}
			devAddr := make([]byte, 4)
			copyReverse(devAddr, pld.DevAddr)
			b = append(b, devAddr...)
			b = byteutil.AppendUint32(b, pld.WFCnt, 4)
			if n := len(pld.RootWorSKey); n != 16 {
				return nil, errExpectedLengthEncodedEqual("RootWorSKey", 16)(n)
			}
			b = append(b, pld.RootWorSKey...)
			return b, nil
		},
		UnmarshalDownlink: newMACUnmarshaler(
			ttnpb.MACCommandIdentifier_CID_RELAY_UPDATE_UPLINK_LIST,
			"RelayUpdateUplinkListReq",
			26,
			func(phy band.Band, b []byte, cmd *ttnpb.MACCommand) error {
				req := &ttnpb.MACCommand_RelayUpdateUplinkListReq{
					DevAddr: make([]byte, 4),
				}
				cmd.Payload = &ttnpb.MACCommand_RelayUpdateUplinkListReq_{
					RelayUpdateUplinkListReq: req,
				}
				req.RuleIndex = uint32(b[0])
				if req.RuleIndex > 7 {
					return errExpectedLowerOrEqual("RuleIndex", 7)(req.RuleIndex)
				}
				uplinkLimit := b[1]
				if (uplinkLimit & 0x3f) != 0x3f {
					req.ForwardLimits = &ttnpb.RelayUplinkForwardLimits{
						BucketSize: ttnpb.RelayLimitBucketSize(uplinkLimit >> 6),
						ReloadRate: uint32(uplinkLimit & 0x3f),
					}
				}
				copyReverse(req.DevAddr[:], b[2:6])
				req.WFCnt = byteutil.ParseUint32(b[6:10])
				req.RootWorSKey = b[10:26]
				return nil
			},
		),
	},
	ttnpb.MACCommandIdentifier_CID_RELAY_CTRL_UPLINK_LIST: &MACCommandDescriptor{
		InitiatedByDevice: false,

		UplinkLength: 5,
		AppendUplink: func(phy band.Band, b []byte, cmd *ttnpb.MACCommand) ([]byte, error) {
			pld := cmd.GetRelayCtrlUplinkListAns()
			var status byte
			if pld.RuleIndexAck {
				status |= 1
			}
			b = append(b, status)
			b = byteutil.AppendUint32(b, pld.WFCnt, 4)
			return b, nil
		},
		UnmarshalUplink: newMACUnmarshaler(
			ttnpb.MACCommandIdentifier_CID_RELAY_CTRL_UPLINK_LIST,
			"RelayCtrlUplinkListAns",
			5,
			func(phy band.Band, b []byte, cmd *ttnpb.MACCommand) error {
				cmd.Payload = &ttnpb.MACCommand_RelayCtrlUplinkListAns_{
					RelayCtrlUplinkListAns: &ttnpb.MACCommand_RelayCtrlUplinkListAns{
						RuleIndexAck: b[0]&1 == 1,
						WFCnt:        byteutil.ParseUint32(b[1:5]),
					},
				}
				return nil
			},
		),

		DownlinkLength: 1,
		AppendDownlink: func(phy band.Band, b []byte, cmd *ttnpb.MACCommand) ([]byte, error) {
			req := cmd.GetRelayCtrlUplinkListReq()
			if req.RuleIndex > 15 {
				return nil, errExpectedLowerOrEqual("RuleIndex", 7)(req.RuleIndex)
			}
			var action byte
			action |= byte(req.RuleIndex) & 0xf
			if req.Action > 1 {
				return nil, errExpectedLowerOrEqual("Action", 1)(req.Action)
			}
			action |= byte(req.Action&0x1) << 4
			b = append(b, action)
			return b, nil
		},
		UnmarshalDownlink: newMACUnmarshaler(
			ttnpb.MACCommandIdentifier_CID_RELAY_CTRL_UPLINK_LIST,
			"RelayCtrlUplinkListReq",
			1,
			func(phy band.Band, b []byte, cmd *ttnpb.MACCommand) error {
				cmd.Payload = &ttnpb.MACCommand_RelayCtrlUplinkListReq_{
					RelayCtrlUplinkListReq: &ttnpb.MACCommand_RelayCtrlUplinkListReq{
						RuleIndex: uint32(b[0] & 0xf),
						Action:    ttnpb.RelayCtrlUplinkListAction(b[0] >> 4),
					},
				}
				return nil
			},
		),
	},
	ttnpb.MACCommandIdentifier_CID_RELAY_CONFIGURE_FWD_LIMIT: &MACCommandDescriptor{
		InitiatedByDevice: false,

		UplinkLength: 0,
		AppendUplink: func(phy band.Band, b []byte, cmd *ttnpb.MACCommand) ([]byte, error) {
			return b, nil
		},
		UnmarshalUplink: newMACUnmarshaler(
			ttnpb.MACCommandIdentifier_CID_RELAY_CONFIGURE_FWD_LIMIT,
			"RelayConfigureFwdLimitAns",
			0,
			func(phy band.Band, b []byte, cmd *ttnpb.MACCommand) error {
				cmd.Payload = &ttnpb.MACCommand_RelayConfigureFwdLimitAns_{
					RelayConfigureFwdLimitAns: &ttnpb.MACCommand_RelayConfigureFwdLimitAns{},
				}
				return nil
			},
		),

		DownlinkLength: 5,
		AppendDownlink: func(phy band.Band, b []byte, cmd *ttnpb.MACCommand) ([]byte, error) {
			pld := cmd.GetRelayConfigureFwdLimitReq()
			if pld.ResetLimitCounter > 3 {
				return nil, errExpectedLowerOrEqual("ResetLimitCounter", 3)(pld.ResetLimitCounter)
			}
			var overallReloadRate byte = 0x7f
			var overallBucketSize byte
			if overall := pld.OverallLimits; overall != nil {
				if overall.ReloadRate > 126 {
					return nil, errExpectedLowerOrEqual("ReloadRate", 126)(overall.ReloadRate)
				}
				overallReloadRate = byte(overall.ReloadRate & 0x7f)
				if overall.BucketSize > 3 {
					return nil, errExpectedLowerOrEqual("BucketSize", 3)(overall.BucketSize)
				}
				overallBucketSize = byte(overall.BucketSize & 0x3)
			}
			var globalUplinkReloadRate byte = 0x7f
			var globalUplinkBucketSize byte
			if global := pld.GlobalUplinkLimits; global != nil {
				if global.ReloadRate > 126 {
					return nil, errExpectedLowerOrEqual("ReloadRate", 126)(global.ReloadRate)
				}
				globalUplinkReloadRate = byte(global.ReloadRate & 0x7f)
				if global.BucketSize > 3 {
					return nil, errExpectedLowerOrEqual("BucketSize", 3)(global.BucketSize)
				}
				globalUplinkBucketSize = byte(global.BucketSize & 0x3)
			}
			var notifyReloadRate byte = 0x7f
			var notifyBucketSize byte
			if notify := pld.NotifyLimits; notify != nil {
				if notify.ReloadRate > 126 {
					return nil, errExpectedLowerOrEqual("ReloadRate", 126)(notify.ReloadRate)
				}
				notifyReloadRate = byte(notify.ReloadRate & 0x7f)
				if notify.BucketSize > 3 {
					return nil, errExpectedLowerOrEqual("BucketSize", 3)(notify.BucketSize)
				}
				notifyBucketSize = byte(notify.BucketSize & 0x3)
			}
			var joinRequestLimits byte = 0x7f
			var joinRequestBucketSize byte
			if joinReq := pld.JoinRequestLimits; joinReq != nil {
				if joinReq.ReloadRate > 126 {
					return nil, errExpectedLowerOrEqual("ReloadRate", 126)(joinReq.ReloadRate)
				}
				joinRequestLimits = byte(joinReq.ReloadRate & 0x7f)
				if joinReq.BucketSize > 3 {
					return nil, errExpectedLowerOrEqual("BucketSize", 3)(joinReq.BucketSize)
				}
				joinRequestBucketSize = byte(joinReq.BucketSize & 0x3)
			}
			var reloadRate uint32
			reloadRate |= uint32(overallReloadRate)
			reloadRate |= uint32(globalUplinkReloadRate) << 7
			reloadRate |= uint32(notifyReloadRate) << 14
			reloadRate |= uint32(joinRequestLimits) << 21
			reloadRate |= uint32(pld.ResetLimitCounter&0x3) << 28
			b = byteutil.AppendUint32(b, reloadRate, 4)
			var bucketSize byte
			bucketSize |= overallBucketSize
			bucketSize |= globalUplinkBucketSize << 2
			bucketSize |= notifyBucketSize << 4
			bucketSize |= joinRequestBucketSize << 6
			b = append(b, bucketSize)
			return b, nil
		},
		UnmarshalDownlink: newMACUnmarshaler(
			ttnpb.MACCommandIdentifier_CID_RELAY_CONFIGURE_FWD_LIMIT,
			"RelayConfigureFwdLimitReq",
			5,
			func(phy band.Band, b []byte, cmd *ttnpb.MACCommand) error {
				req := &ttnpb.MACCommand_RelayConfigureFwdLimitReq{}
				cmd.Payload = &ttnpb.MACCommand_RelayConfigureFwdLimitReq_{
					RelayConfigureFwdLimitReq: req,
				}
				bucketSize, reloadRate := b[4], byteutil.ParseUint32(b[0:4])
				req.ResetLimitCounter = ttnpb.RelayResetLimitCounter((reloadRate >> 28) & 0x3)
				if reloadRate := reloadRate & 0x7f; reloadRate != 0x7f {
					req.OverallLimits = &ttnpb.RelayForwardLimits{
						BucketSize: ttnpb.RelayLimitBucketSize(bucketSize & 0x3),
						ReloadRate: reloadRate,
					}
				}
				if reloadRate := (reloadRate >> 7) & 0x7f; reloadRate != 0x7f {
					req.GlobalUplinkLimits = &ttnpb.RelayForwardLimits{
						BucketSize: ttnpb.RelayLimitBucketSize((bucketSize >> 2) & 0x3),
						ReloadRate: reloadRate,
					}
				}
				if reloadRate := (reloadRate >> 14) & 0x7f; reloadRate != 0x7f {
					req.NotifyLimits = &ttnpb.RelayForwardLimits{
						BucketSize: ttnpb.RelayLimitBucketSize((bucketSize >> 4) & 0x3),
						ReloadRate: reloadRate,
					}
				}
				if reloadRate := (reloadRate >> 21) & 0x7f; reloadRate != 0x7f {
					req.JoinRequestLimits = &ttnpb.RelayForwardLimits{
						BucketSize: ttnpb.RelayLimitBucketSize((bucketSize >> 6) & 0x3),
						ReloadRate: reloadRate,
					}
				}
				return nil
			},
		),
	},
	ttnpb.MACCommandIdentifier_CID_RELAY_NOTIFY_NEW_END_DEVICE: &MACCommandDescriptor{
		InitiatedByDevice: true,

		UplinkLength: 6,
		AppendUplink: func(phy band.Band, b []byte, cmd *ttnpb.MACCommand) ([]byte, error) {
			pld := cmd.GetRelayNotifyNewEndDeviceReq()
			var powerLevel uint16
			if pld.Snr < -20 || pld.Snr > 11 {
				return nil, errExpectedBetween("SNR", -20, 11)(pld.Snr)
			}
			powerLevel |= uint16(pld.Snr+20) & 0x1f
			if pld.Rssi < -142 || pld.Rssi > -15 {
				return nil, errExpectedBetween("RSSI", -142, -15)(pld.Rssi)
			}
			powerLevel |= uint16(-pld.Rssi-15) & 0x7f << 5
			b = byteutil.AppendUint16(b, powerLevel, 2)
			if n := len(pld.DevAddr); n != 4 {
				return nil, errExpectedLengthEncodedEqual("DevAddr", 4)(n)
			}
			devAddr := make([]byte, 4)
			copyReverse(devAddr, pld.DevAddr)
			b = append(b, devAddr...)
			return b, nil
		},
		UnmarshalUplink: newMACUnmarshaler(
			ttnpb.MACCommandIdentifier_CID_RELAY_NOTIFY_NEW_END_DEVICE,
			"RelayNotifyNewEndDeviceReq",
			6,
			func(phy band.Band, b []byte, cmd *ttnpb.MACCommand) error {
				req := &ttnpb.MACCommand_RelayNotifyNewEndDeviceReq{}
				cmd.Payload = &ttnpb.MACCommand_RelayNotifyNewEndDeviceReq_{
					RelayNotifyNewEndDeviceReq: req,
				}
				powerLevel := byteutil.ParseUint16(b[0:2])
				req.Snr = int32(powerLevel&0x1f) - 20
				req.Rssi = -int32(powerLevel>>5&0x7f) - 15
				req.DevAddr = make([]byte, 4)
				copyReverse(req.DevAddr, b[2:6])
				return nil
			},
		),
	},
}

DefaultMACCommands contains all the default MAC commands.

Functions

func ADRAckDelayExponentToUint32

func ADRAckDelayExponentToUint32(v ttnpb.ADRAckDelayExponent) uint32

ADRAckDelayExponentToUint32 returns v as a uint32 value.

func ADRAckLimitExponentToUint32

func ADRAckLimitExponentToUint32(v ttnpb.ADRAckLimitExponent) uint32

ADRAckLimitExponentToUint32 returns v as a uint32 value.

func AppendCFList

func AppendCFList(dst []byte, msg *ttnpb.CFList) ([]byte, error)

AppendCFList appends encoded msg to dst.

func AppendDLSettings

func AppendDLSettings(dst []byte, msg *ttnpb.DLSettings) ([]byte, error)

AppendDLSettings appends encoded msg to dst.

func AppendFCtrl

func AppendFCtrl(dst []byte, msg *ttnpb.FCtrl, isUplink bool, fOptsLen uint8) ([]byte, error)

AppendFCtrl appends encoded msg to dst.

func AppendFHDR

func AppendFHDR(dst []byte, msg *ttnpb.FHDR, isUplink bool) ([]byte, error)

AppendFHDR appends encoded msg to dst.

func AppendJoinAcceptPayload

func AppendJoinAcceptPayload(dst []byte, msg *ttnpb.JoinAcceptPayload) ([]byte, error)

AppendJoinAcceptPayload appends encoded msg to dst.

func AppendJoinRequestPayload

func AppendJoinRequestPayload(dst []byte, msg *ttnpb.JoinRequestPayload) ([]byte, error)

AppendJoinRequestPayload appends encoded msg to dst.

func AppendMACPayload

func AppendMACPayload(dst []byte, msg *ttnpb.MACPayload, isUplink bool) ([]byte, error)

AppendMACPayload appends encoded msg to dst.

func AppendMHDR

func AppendMHDR(dst []byte, msg *ttnpb.MHDR) ([]byte, error)

AppendMHDR appends encoded msg to dst.

func AppendMessage

func AppendMessage(dst []byte, msg *ttnpb.Message) ([]byte, error)

AppendMessage appends encoded msg to dst.

func AppendRejoinRequestPayload

func AppendRejoinRequestPayload(dst []byte, msg *ttnpb.RejoinRequestPayload) ([]byte, error)

AppendRejoinRequestPayload appends encoded msg to dst.

func DeviceEIRPToFloat32

func DeviceEIRPToFloat32(v ttnpb.DeviceEIRP) float32

DeviceEIRPToFloat32 returns v as a float32 value.

func Float32ToDeviceEIRP

func Float32ToDeviceEIRP(v float32) ttnpb.DeviceEIRP

Float32ToDeviceEIRP returns v as a highest possible DeviceEIRP.

func GetUplinkMessageIdentifiers

func GetUplinkMessageIdentifiers(phyPayload []byte) (*ttnpb.EndDeviceIdentifiers, error)

GetUplinkMessageIdentifiers parses the PHYPayload and retrieves the EndDeviceIdentifiers (except DeviceID).

func MarshalCFList

func MarshalCFList(msg *ttnpb.CFList) ([]byte, error)

MarshalCFList returns encoded msg.

func MarshalDLSettings

func MarshalDLSettings(msg *ttnpb.DLSettings) ([]byte, error)

MarshalDLSettings returns encoded msg.

func MarshalJoinAcceptPayload

func MarshalJoinAcceptPayload(msg *ttnpb.JoinAcceptPayload) ([]byte, error)

MarshalJoinAcceptPayload returns encoded msg.

func MarshalJoinRequestPayload

func MarshalJoinRequestPayload(msg *ttnpb.JoinRequestPayload) ([]byte, error)

MarshalJoinRequestPayload returns encoded msg.

func MarshalMACPayload

func MarshalMACPayload(msg *ttnpb.MACPayload, isUplink bool) ([]byte, error)

MarshalMACPayload returns encoded msg.

func MarshalMHDR

func MarshalMHDR(msg *ttnpb.MHDR) ([]byte, error)

MarshalMHDR returns encoded msg.

func MarshalMessage

func MarshalMessage(msg *ttnpb.Message) ([]byte, error)

MarshalMessage returns encoded msg.

func MarshalRejoinRequestPayload

func MarshalRejoinRequestPayload(msg *ttnpb.RejoinRequestPayload) ([]byte, error)

MarshalRejoinRequestPayload returns encoded msg.

func MarshalRelayForwardDownlinkReq added in v3.28.1

func MarshalRelayForwardDownlinkReq(req *ttnpb.RelayForwardDownlinkReq) ([]byte, error)

MarshalRelayForwardDownlinkReq marshals a RelayForwardDownlinkReq.

func MarshalRelayForwardUplinkReq added in v3.28.1

func MarshalRelayForwardUplinkReq(phy *band.Band, req *ttnpb.RelayForwardUplinkReq) ([]byte, error)

MarshalRelayForwardUplinkReq marshals a RelayForwardUplinkReq.

func Uint32ToADRAckDelayExponent

func Uint32ToADRAckDelayExponent(v uint32) ttnpb.ADRAckDelayExponent

Uint32ToADRAckDelayExponent returns v as a highest possible ADRAckDelayExponent.

func Uint32ToADRAckLimitExponent

func Uint32ToADRAckLimitExponent(v uint32) ttnpb.ADRAckLimitExponent

Uint32ToADRAckLimitExponent returns v as a highest possible ADRAckLimitExponent.

func UnmarshalCFList

func UnmarshalCFList(b []byte, msg *ttnpb.CFList) error

UnmarshalCFList unmarshals b into msg.

func UnmarshalDLSettings

func UnmarshalDLSettings(b []byte, msg *ttnpb.DLSettings) error

UnmarshalDLSettings unmarshals b into msg.

func UnmarshalFCtrl

func UnmarshalFCtrl(b []byte, msg *ttnpb.FCtrl, isUplink bool) error

UnmarshalFCtrl unmarshals b into msg.

func UnmarshalFHDR

func UnmarshalFHDR(b []byte, msg *ttnpb.FHDR, isUplink bool) error

UnmarshalFHDR unmarshals b into msg.

func UnmarshalJoinAcceptPayload

func UnmarshalJoinAcceptPayload(b []byte, msg *ttnpb.JoinAcceptPayload) error

UnmarshalJoinAcceptPayload unmarshals b into msg.

func UnmarshalJoinRequestPayload

func UnmarshalJoinRequestPayload(b []byte, msg *ttnpb.JoinRequestPayload) error

UnmarshalJoinRequestPayload unmarshals b into msg.

func UnmarshalMACPayload

func UnmarshalMACPayload(b []byte, msg *ttnpb.MACPayload, isUplink bool) error

UnmarshalMACPayload unmarshals b into msg.

func UnmarshalMHDR

func UnmarshalMHDR(b []byte, msg *ttnpb.MHDR) error

UnmarshalMHDR unmarshals b into msg.

func UnmarshalMessage

func UnmarshalMessage(b []byte, msg *ttnpb.Message) error

UnmarshalMessage unmarshals b into msg.

func UnmarshalRejoinRequestPayload

func UnmarshalRejoinRequestPayload(b []byte, msg *ttnpb.RejoinRequestPayload) error

UnmarshalRejoinRequestPayload unmarshals b into msg.

func UnmarshalRelayForwardDownlinkReq added in v3.28.1

func UnmarshalRelayForwardDownlinkReq(b []byte, req *ttnpb.RelayForwardDownlinkReq) error

UnmarshalRelayForwardDownlinkReq unmarshals b into req.

func UnmarshalRelayForwardUplinkReq added in v3.28.1

func UnmarshalRelayForwardUplinkReq(phy *band.Band, b []byte, req *ttnpb.RelayForwardUplinkReq) error

UnmarshalRelayForwardUplinkReq unmarshals b into req.

Types

type MACCommandDescriptor

type MACCommandDescriptor struct {
	InitiatedByDevice bool
	// UplinkLength is length of uplink payload.
	UplinkLength uint16
	// DownlinkLength is length of downlink payload.
	DownlinkLength uint16
	// AppendUplink appends uplink payload of cmd to b.
	AppendUplink func(phy band.Band, b []byte, cmd *ttnpb.MACCommand) ([]byte, error)
	// UnmarshalUplink unmarshals uplink payload b into cmd.
	UnmarshalUplink func(phy band.Band, b []byte, cmd *ttnpb.MACCommand) error
	// AppendDownlink appends uplink payload of cmd to b.
	AppendDownlink func(phy band.Band, b []byte, cmd *ttnpb.MACCommand) ([]byte, error)
	// UnmarshalDownlink unmarshals downlink payload b into cmd.
	UnmarshalDownlink func(phy band.Band, b []byte, cmd *ttnpb.MACCommand) error
}

MACCommandDescriptor descibes a MAC command.

type MACCommandSpec

MACCommandSpec maps the ttnpb.CID of MACCommand to a *MACCommandDescriptor.

func (spec MACCommandSpec) AppendDownlink(phy band.Band, b []byte, cmd *ttnpb.MACCommand) ([]byte, error)

AppendDownlink encodes downlink MAC command cmd, appends it to b and returns any errors encountered.

func (spec MACCommandSpec) AppendUplink(phy band.Band, b []byte, cmd *ttnpb.MACCommand) ([]byte, error)

AppendUplink encodes uplink MAC command cmd, appends it to b and returns any errors encountered.

func (spec MACCommandSpec) ReadDownlink(phy band.Band, r io.Reader, cmd *ttnpb.MACCommand) error

ReadDownlink reads a downlink MACCommand from r into cmd and returns any errors encountered.

func (spec MACCommandSpec) ReadUplink(phy band.Band, r io.Reader, cmd *ttnpb.MACCommand) error

ReadUplink reads an uplink MACCommand from r into cmd and returns any errors encountered.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL