lorawan

package
v3.15.1 Latest Latest
Warning

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

Go to latest
Published: Oct 1, 2021 License: Apache-2.0 Imports: 12 Imported by: 1

Documentation

Overview

Package lorawan provides LoRaWAN decoding/encoding interfaces.

Index

Constants

This section is empty.

Variables

View Source
var DefaultMACCommands = MACCommandSpec{
	ttnpb.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.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.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.CID_LINK_CHECK: &MACCommandDescriptor{
		InitiatedByDevice: true,

		AppendUplink: func(phy band.Band, b []byte, _ ttnpb.MACCommand) ([]byte, error) {
			return b, nil
		},
		UnmarshalUplink: newMACUnmarshaler(ttnpb.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.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.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.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.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.CID_DUTY_CYCLE: &MACCommandDescriptor{
		InitiatedByDevice: false,

		AppendUplink: func(phy band.Band, b []byte, _ ttnpb.MACCommand) ([]byte, error) {
			return b, nil
		},
		UnmarshalUplink: newMACUnmarshaler(ttnpb.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.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.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.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.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.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.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.CID_DEV_STATUS, "DevStatusReq", 0, nil),
	},

	ttnpb.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.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.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.CID_RX_TIMING_SETUP: &MACCommandDescriptor{
		InitiatedByDevice: false,

		AppendUplink: func(phy band.Band, b []byte, _ ttnpb.MACCommand) ([]byte, error) {
			return b, nil
		},
		UnmarshalUplink: newMACUnmarshaler(ttnpb.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.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.CID_TX_PARAM_SETUP: &MACCommandDescriptor{
		InitiatedByDevice: false,

		AppendUplink: func(phy band.Band, b []byte, _ ttnpb.MACCommand) ([]byte, error) {
			return b, nil
		},
		UnmarshalUplink: newMACUnmarshaler(ttnpb.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.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.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.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.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.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.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.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.CID_ADR_PARAM_SETUP: &MACCommandDescriptor{
		InitiatedByDevice: false,

		AppendUplink: func(phy band.Band, b []byte, _ ttnpb.MACCommand) ([]byte, error) {
			return b, nil
		},
		UnmarshalUplink: newMACUnmarshaler(ttnpb.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.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.CID_DEVICE_TIME: &MACCommandDescriptor{
		InitiatedByDevice: true,

		AppendUplink: func(phy band.Band, b []byte, _ ttnpb.MACCommand) ([]byte, error) {
			return b, nil
		},
		UnmarshalUplink: newMACUnmarshaler(ttnpb.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(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.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: gpstime.Parse(time.Duration(byteutil.ParseUint32(b[0:4]))*time.Second + time.Duration(b[4])*fractStep),
				},
			}
			return nil
		}),
	},

	ttnpb.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.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.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.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.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.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.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.CID_PING_SLOT_INFO, "PingSlotInfoAns", 0, nil),
	},

	ttnpb.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.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.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.CID_BEACON_TIMING: &MACCommandDescriptor{
		InitiatedByDevice: true,

		AppendUplink: func(phy band.Band, b []byte, _ ttnpb.MACCommand) ([]byte, error) {
			return b, nil
		},
		UnmarshalUplink: newMACUnmarshaler(ttnpb.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.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.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.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.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.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.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.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
		}),
	},
}

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 EndDeviceIdentifers (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 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.

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