public LoRaADRResult ComputeResult(LoRaADRTable table, float requiredSnr, DataRateIndex upstreamDataRate, int minTxPower, DataRateIndex maxDr) { // We do not have enough frame to calculate ADR. We can assume that a crash was the cause. if (table == null || table.Entries.Count < 20) { this.logger.LogDebug("ADR: not enough frames captured. Sending default power values"); return(null); } // This is the first contact case to harmonize the txpower state between device and server or the crash case. if (!table.CurrentNbRep.HasValue || !table.CurrentTxPower.HasValue) { this.logger.LogDebug("ADR: Sending the device default power values"); return(null); } // This is a case of standard ADR calculus. var newNbRep = ComputeNbRepetion(table.Entries[0].FCnt, table.Entries[LoRaADRTable.FrameCountCaptureCount - 1].FCnt, table.CurrentNbRep.GetValueOrDefault()); (var newTxPowerIndex, var newDatarate) = GetPowerAndDRConfiguration(requiredSnr, upstreamDataRate, table.Entries.Max(x => x.Snr), table.CurrentTxPower.GetValueOrDefault(), minTxPower, maxDr); if (newNbRep != table.CurrentNbRep || newTxPowerIndex != table.CurrentTxPower || newDatarate != upstreamDataRate) { return(new LoRaADRResult() { DataRate = newDatarate, NbRepetition = newNbRep, TxPower = newTxPowerIndex }); } return(null); }
protected static void AddEntryToTable(LoRaADRTable table, LoRaADRTableEntry entry) { var existing = table.Entries.FirstOrDefault(itm => itm.FCnt == entry.FCnt); if (existing == null) { // first for this framecount, simply add it entry.GatewayCount = 1; table.Entries.Add(entry); } else { if (existing.Snr < entry.Snr) { // better SNR. Update existing.Snr = entry.Snr; existing.GatewayId = entry.GatewayId; } existing.GatewayCount++; } if (table.Entries.Count > LoRaADRTable.FrameCountCaptureCount) { table.Entries.RemoveAt(0); } }
public virtual async Task <LoRaADRResult> CalculateADRResultAndAddEntryAsync(DevEui devEUI, string gatewayId, uint fCntUp, uint fCntDown, float requiredSnr, DataRateIndex dataRate, int minTxPower, DataRateIndex maxDr, LoRaADRTableEntry newEntry = null) { var table = newEntry != null ? await this.store.AddTableEntry(newEntry) : await this.store.GetADRTable(devEUI); var currentStrategy = this.strategyProvider.GetStrategy(); var result = currentStrategy.ComputeResult(table, requiredSnr, dataRate, minTxPower, maxDr); if (result == null) { // In this case we want to reset the device to default values as we have null values if (table == null || !table.CurrentNbRep.HasValue || !table.CurrentTxPower.HasValue || fCntUp > currentStrategy.MinimumNumberOfResult) { result = ReturnDefaultValues(dataRate, currentStrategy.DefaultNbRep, currentStrategy.DefaultTxPower); } else { result = await GetLastResultAsync(devEUI) ?? new LoRaADRResult(); result.NumberOfFrames = table.Entries.Count; return(result); } } var nextFcntDown = await NextFCntDown(devEUI, gatewayId, fCntUp, fCntDown); result.CanConfirmToDevice = nextFcntDown > 0; if (result.CanConfirmToDevice) { if (table == null) { // in a reset case, we may not have a table, but still want to store the default // values that we sent to the client table = new LoRaADRTable(); } table.CurrentNbRep = result.NbRepetition; table.CurrentTxPower = result.TxPower; await this.store.UpdateADRTable(devEUI, table); UpdateState(result); result.FCntDown = nextFcntDown; } result.NumberOfFrames = table.Entries.Count; this.logger.LogDebug($"calculated ADR: CanConfirmToDevice: {result.CanConfirmToDevice}, TxPower: {result.TxPower}, DataRate: {result.DataRate}"); return(result); }
public Task UpdateADRTable(string devEUI, LoRaADRTable table) { // void: the reference is up to date already return(Task.CompletedTask); }