func CopyEndDevice(pb *ttnpb.EndDevice) *ttnpb.EndDevice {
	return deepcopy.Copy(pb).(*ttnpb.EndDevice)
}
CopyEndDevice returns a deep copy of ttnpb.EndDevice pb.
package internal
import (
	"context"
	"fmt"
	"github.com/mohae/deepcopy"
	"go.thethings.network/lorawan-stack/v3/pkg/band"
	"go.thethings.network/lorawan-stack/v3/pkg/errors"
	"go.thethings.network/lorawan-stack/v3/pkg/frequencyplans"
	"go.thethings.network/lorawan-stack/v3/pkg/networkserver/internal/time"
	"go.thethings.network/lorawan-stack/v3/pkg/ttnpb"
	"go.thethings.network/lorawan-stack/v3/pkg/unique"
)
var LoRaWANVersionPairs = map[ttnpb.MACVersion]map[ttnpb.PHYVersion]struct{}{
	ttnpb.MAC_V1_0: {
		ttnpb.TS001_V1_0: struct{}{},
	},
	ttnpb.MAC_V1_0_1: {
		ttnpb.TS001_V1_0_1: struct{}{},
	},
	ttnpb.MAC_V1_0_2: {
		ttnpb.RP001_V1_0_2:       struct{}{},
		ttnpb.RP001_V1_0_2_REV_B: struct{}{},
	},
	ttnpb.MAC_V1_0_3: {
		ttnpb.RP001_V1_0_3_REV_A: struct{}{},
	},
	ttnpb.MAC_V1_1: {
		ttnpb.RP001_V1_1_REV_A: struct{}{},
		ttnpb.RP001_V1_1_REV_B: struct{}{},
	},
}
var LoRaWANBands = func() map[string]map[ttnpb.PHYVersion]*band.Band {
	bands := make(map[string]map[ttnpb.PHYVersion]*band.Band, len(band.All))
	for id, vers := range band.All {
		m := make(map[ttnpb.PHYVersion]*band.Band, len(vers))
		for ver, b := range vers {
			b := b
			m[ver] = &b
		}
		bands[id] = m
	}
	return bands
}()
var errNoBandVersion = errors.DefineInvalidArgument("no_band_version", "specified version `{ver}` of band `{id}` does not exist")
func FrequencyPlanAndBand(frequencyPlanID string, phyVersion ttnpb.PHYVersion, fps *frequencyplans.Store) (*frequencyplans.FrequencyPlan, *band.Band, error) {
	fp, err := fps.GetByID(frequencyPlanID)
	if err != nil {
		return nil, nil, err
	}
	b, ok := LoRaWANBands[fp.BandID][phyVersion]
	if !ok || b == nil {
		return nil, nil, errNoBandVersion.WithAttributes(
			"ver", phyVersion,
			"id", fp.BandID,
		)
	}
	return fp, b, nil
}
func DeviceFrequencyPlanAndBand(dev *ttnpb.EndDevice, fps *frequencyplans.Store) (*frequencyplans.FrequencyPlan, *band.Band, error) {
	return FrequencyPlanAndBand(dev.FrequencyPlanId, dev.LorawanPhyVersion, fps)
}
func DeviceBand(dev *ttnpb.EndDevice, fps *frequencyplans.Store) (*band.Band, error) {
	_, phy, err := DeviceFrequencyPlanAndBand(dev, fps)
	return phy, err
}
func LastUplink(ups ...*ttnpb.UplinkMessage) *ttnpb.UplinkMessage {
	return ups[len(ups)-1]
}
func LastDownlink(downs ...*ttnpb.DownlinkMessage) *ttnpb.DownlinkMessage {
	return downs[len(downs)-1]
}
func RXMetadataStats(ctx context.Context, mds []*ttnpb.RxMetadata) (gateways int, maxSNR float32) {
	if len(mds) == 0 {
		return 0, 0
	}
	gtws := make(map[string]struct{}, len(mds))
	maxSNR = mds[0].Snr
	for _, md := range mds {
		if md.PacketBroker != nil {
			gtws[fmt.Sprintf("%s@%s/%s", md.PacketBroker.ForwarderClusterId, md.PacketBroker.ForwarderNetId, md.PacketBroker.ForwarderTenantId)] = struct{}{}
		} else {
			gtws[unique.ID(ctx, md.GatewayIdentifiers)] = struct{}{}
		}
		if md.Snr > maxSNR {
			maxSNR = md.Snr
		}
	}
	return len(gtws), maxSNR
}
func TimePtr(v time.Time) *time.Time {
	return &v
}
func EndDevicePtr(v ttnpb.EndDevice) *ttnpb.EndDevice {
	return &v
}