示例#1
0
func (u *utilization) AddTx(downlink *pb_router.DownlinkMessage) error {
	var t time.Duration
	var err error
	if lorawan := downlink.ProtocolConfiguration.GetLorawan(); lorawan != nil {
		if lorawan.Modulation == pb_lorawan.Modulation_LORA {
			t, err = toa.ComputeLoRa(uint(len(downlink.Payload)), lorawan.DataRate, lorawan.CodingRate)
			if err != nil {
				return err
			}
		}
		if lorawan.Modulation == pb_lorawan.Modulation_FSK {
			t, err = toa.ComputeFSK(uint(len(downlink.Payload)), int(lorawan.BitRate))
			if err != nil {
				return err
			}
		}
	}
	if t == 0 {
		return nil
	}
	u.overallTx.Update(int64(t) / 1000)
	frequency := downlink.GatewayConfiguration.Frequency
	u.channelTxLock.Lock()
	defer u.channelTxLock.Unlock()
	if _, ok := u.channelTx[frequency]; !ok {
		u.channelTx[frequency] = metrics.NewEWMA1()
	}
	u.channelTx[frequency].Update(int64(t) / 1000)
	return nil
}
示例#2
0
// see interface
func (s *schedule) Schedule(id string, downlink *router_pb.DownlinkMessage) error {
	ctx := s.ctx.WithField("Identifier", id)

	s.Lock()
	defer s.Unlock()
	if item, ok := s.items[id]; ok {
		item.payload = downlink

		if lorawan := downlink.GetProtocolConfiguration().GetLorawan(); lorawan != nil {
			var time time.Duration
			if lorawan.Modulation == pb_lorawan.Modulation_LORA {
				// Calculate max ToA
				time, _ = toa.ComputeLoRa(
					uint(len(downlink.Payload)),
					lorawan.DataRate,
					lorawan.CodingRate,
				)
			}
			if lorawan.Modulation == pb_lorawan.Modulation_FSK {
				// Calculate max ToA
				time, _ = toa.ComputeFSK(
					uint(len(downlink.Payload)),
					int(lorawan.BitRate),
				)
			}
			item.length = uint32(time / 1000)
		}

		if time.Now().Before(item.deadlineAt) {
			// Schedule transmission before the Deadline
			go func() {
				waitTime := item.deadlineAt.Sub(time.Now())
				ctx.WithField("Remaining", waitTime).Info("Scheduled downlink")
				<-time.After(waitTime)
				s.RLock()
				defer s.RUnlock()
				if s.downlink != nil {
					s.downlink <- item.payload
				}
			}()
		} else {
			go func() {
				s.RLock()
				defer s.RUnlock()
				if s.downlink != nil {
					overdue := time.Now().Sub(item.deadlineAt)
					if overdue < Deadline {
						// Immediately send it
						ctx.WithField("Overdue", overdue).Warn("Send Late Downlink")
						s.downlink <- item.payload
					} else {
						ctx.WithField("Overdue", overdue).Warn("Discard Late Downlink")
					}
				} else {
					ctx.Warn("Unable to send Downlink")
				}
			}()
		}

		return nil
	}
	return errors.NewErrNotFound(id)
}
示例#3
0
// Calculating the score for each downlink option; lower is better, 0 is best
// If a score is over 1000, it may should not be used as feasible option.
// TODO: The weights of these parameters should be optimized. I'm sure someone
// can do some computer simulations to find the right values.
func computeDownlinkScores(gateway *gateway.Gateway, uplink *pb.UplinkMessage, options []*pb_broker.DownlinkOption) {
	gatewayStatus, _ := gateway.Status.Get() // This just returns empty if non-existing

	region := gatewayStatus.Region
	if region == "" {
		region = band.Guess(uplink.GatewayMetadata.Frequency)
	}

	gatewayRx, _ := gateway.Utilization.Get()
	for _, option := range options {

		// Invalid if no LoRaWAN
		lorawan := option.GetProtocolConfig().GetLorawan()
		if lorawan == nil {
			option.Score = 1000
			continue
		}

		var time time.Duration

		if lorawan.Modulation == pb_lorawan.Modulation_LORA {
			// Calculate max ToA
			time, _ = toa.ComputeLoRa(
				51+13, // Max MACPayload plus LoRaWAN header, TODO: What is the length we should use?
				lorawan.DataRate,
				lorawan.CodingRate,
			)
		}

		if lorawan.Modulation == pb_lorawan.Modulation_FSK {
			// Calculate max ToA
			time, _ = toa.ComputeFSK(
				51+13, // Max MACPayload plus LoRaWAN header, TODO: What is the length we should use?
				int(lorawan.BitRate),
			)
		}

		// Invalid if time is zero
		if time == 0 {
			option.Score = 1000
			continue
		}

		timeScore := math.Min(time.Seconds()*5, 10) // 2 seconds will be 10 (max)

		signalScore := 0.0 // Between 0 and 20 (lower is better)
		{
			// Prefer high SNR
			if uplink.GatewayMetadata.Snr < 5 {
				signalScore += 10
			}
			// Prefer good RSSI
			signalScore += math.Min(float64(uplink.GatewayMetadata.Rssi*-0.1), 10)
		}

		utilizationScore := 0.0 // Between 0 and 40 (lower is better) will be over 100 if forbidden
		{
			// Avoid gateways that do more Rx
			utilizationScore += math.Min(gatewayRx*50, 20) / 2 // 40% utilization = 10 (max)

			// Avoid busy channels
			freq := option.GatewayConfig.Frequency
			channelRx, channelTx := gateway.Utilization.GetChannel(freq)
			utilizationScore += math.Min((channelTx+channelRx)*200, 20) / 2 // 10% utilization = 10 (max)

			// European Duty Cycle
			if region == "EU_863_870" {
				var duty float64
				switch {
				case freq >= 863000000 && freq < 868000000:
					duty = 0.01 // g 863.0 – 868.0 MHz 1%
				case freq >= 868000000 && freq < 868600000:
					duty = 0.01 // g1 868.0 – 868.6 MHz 1%
				case freq >= 868700000 && freq < 869200000:
					duty = 0.001 // g2 868.7 – 869.2 MHz 0.1%
				case freq >= 869400000 && freq < 869650000:
					duty = 0.1 // g3 869.4 – 869.65 MHz 10%
				case freq >= 869700000 && freq < 870000000:
					duty = 0.01 // g4 869.7 – 870.0 MHz 1%
				default:
					utilizationScore += 100 // Transmissions on this frequency are forbidden
				}
				if channelTx > duty {
					utilizationScore += 100 // Transmissions on this frequency are forbidden
				}
				if duty > 0 {
					utilizationScore += math.Min(time.Seconds()/duty/100, 20) // Impact on duty-cycle (in order to prefer RX2 for SF9BW125)
				}
			}
		}

		scheduleScore := 0.0 // Between 0 and 30 (lower is better) will be over 100 if forbidden
		{
			id, conflicts := gateway.Schedule.GetOption(option.GatewayConfig.Timestamp, uint32(time/1000))
			option.Identifier = id
			if conflicts >= 100 {
				scheduleScore += 100
			} else {
				scheduleScore += math.Min(float64(conflicts*10), 30) // max 30
			}
		}

		option.Score = uint32((timeScore + signalScore + utilizationScore + scheduleScore) * 10)
	}
}