MultiTech / mDot_Channel_Plans

The channel plans in this library can be used as starting points for new channel plans and used as a reference for implementation.

Information

To use source version of a channel plan, first remove the Channel Plans folder from libmDot-Custom library.

Not all plans are complete to LoRaWAN specifications.

AS923 and KR920 have the default channels defined and can accept in channels in the Join Accept message or from New Channel MAC commands.

Channel Set must match those expected by the network server in order for ADR to work

AS923 regional settings can be adjusted by the network server using Tx Param Setup MAC command to set max EIRP and dwell time for uplinks.

Committer:
jreiss
Date:
Thu Jan 26 13:07:53 2017 +0000
Revision:
12:2e8fda56093f
Parent:
11:829f8c2ec1c3
Child:
13:996f1663d12e
Adjust ADR DR step down packet by one; Initialize DL channel list with frequency:0 in AS923,EU868,IN865,KR920 plans

Who changed what in which revision?

UserRevisionLine numberNew contents of line
jreiss 12:2e8fda56093f 1 /**********************************************************************
jreiss 12:2e8fda56093f 2 * COPYRIGHT 2016 MULTI-TECH SYSTEMS, INC.
jreiss 12:2e8fda56093f 3 *
jreiss 12:2e8fda56093f 4 * ALL RIGHTS RESERVED BY AND FOR THE EXCLUSIVE BENEFIT OF
jreiss 12:2e8fda56093f 5 * MULTI-TECH SYSTEMS, INC.
jreiss 12:2e8fda56093f 6 *
jreiss 12:2e8fda56093f 7 * MULTI-TECH SYSTEMS, INC. - CONFIDENTIAL AND PROPRIETARY
jreiss 12:2e8fda56093f 8 * INFORMATION AND/OR TRADE SECRET.
jreiss 12:2e8fda56093f 9 *
jreiss 12:2e8fda56093f 10 * NOTICE: ALL CODE, PROGRAM, INFORMATION, SCRIPT, INSTRUCTION,
jreiss 12:2e8fda56093f 11 * DATA, AND COMMENT HEREIN IS AND SHALL REMAIN THE CONFIDENTIAL
jreiss 12:2e8fda56093f 12 * INFORMATION AND PROPERTY OF MULTI-TECH SYSTEMS, INC.
jreiss 12:2e8fda56093f 13 * USE AND DISCLOSURE THEREOF, EXCEPT AS STRICTLY AUTHORIZED IN A
jreiss 12:2e8fda56093f 14 * WRITTEN AGREEMENT SIGNED BY MULTI-TECH SYSTEMS, INC. IS PROHIBITED.
jreiss 12:2e8fda56093f 15 *
jreiss 12:2e8fda56093f 16 ***********************************************************************/
jreiss 12:2e8fda56093f 17
jreiss 12:2e8fda56093f 18 #include "CustomChannelPlan_KR920.h"
jreiss 12:2e8fda56093f 19 #include "limits.h"
jreiss 12:2e8fda56093f 20
jreiss 12:2e8fda56093f 21 using namespace lora;
jreiss 12:2e8fda56093f 22
jreiss 12:2e8fda56093f 23 const uint8_t CustomChannelPlan_KR920::KR920_TX_POWERS[] = { 20, 14, 10, 8, 5, 2, 0 };
jreiss 12:2e8fda56093f 24 const uint8_t CustomChannelPlan_KR920::KR920_RADIO_POWERS[] = { 3, 3, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 18, 19, 20 };
jreiss 12:2e8fda56093f 25 const uint8_t CustomChannelPlan_KR920::KR920_MAX_PAYLOAD_SIZE[] = { 65, 151, 242, 242, 242, 242, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
jreiss 12:2e8fda56093f 26 const uint8_t CustomChannelPlan_KR920::KR920_MAX_PAYLOAD_SIZE_REPEATER[] = { 45, 131, 222, 222, 222, 222, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
jreiss 12:2e8fda56093f 27
jreiss 12:2e8fda56093f 28 CustomChannelPlan_KR920::CustomChannelPlan_KR920(SxRadio& radio, Settings& settings)
jreiss 12:2e8fda56093f 29 :
jreiss 12:2e8fda56093f 30 ChannelPlan(radio, settings)
jreiss 12:2e8fda56093f 31 {
jreiss 12:2e8fda56093f 32
jreiss 12:2e8fda56093f 33 }
jreiss 12:2e8fda56093f 34
jreiss 12:2e8fda56093f 35 CustomChannelPlan_KR920::~CustomChannelPlan_KR920() {
jreiss 12:2e8fda56093f 36
jreiss 12:2e8fda56093f 37 }
jreiss 12:2e8fda56093f 38
jreiss 12:2e8fda56093f 39 void CustomChannelPlan_KR920::Init() {
jreiss 12:2e8fda56093f 40
jreiss 12:2e8fda56093f 41 _datarates.clear();
jreiss 12:2e8fda56093f 42 _channels.clear();
jreiss 12:2e8fda56093f 43 _dutyBands.clear();
jreiss 12:2e8fda56093f 44
jreiss 12:2e8fda56093f 45 DutyBand band;
jreiss 12:2e8fda56093f 46
jreiss 12:2e8fda56093f 47 band.Index = 0;
jreiss 12:2e8fda56093f 48 band.DutyCycle = 0;
jreiss 12:2e8fda56093f 49
jreiss 12:2e8fda56093f 50 Datarate dr;
jreiss 12:2e8fda56093f 51
jreiss 12:2e8fda56093f 52 _type = DYNAMIC;
jreiss 12:2e8fda56093f 53 _planName = "KR920";
jreiss 12:2e8fda56093f 54 _maxTxPower = 14;
jreiss 12:2e8fda56093f 55 _minTxPower = 2;
jreiss 12:2e8fda56093f 56
jreiss 12:2e8fda56093f 57 _minFrequency = 920900000;
jreiss 12:2e8fda56093f 58 _maxFrequency = 923300000;
jreiss 12:2e8fda56093f 59
jreiss 12:2e8fda56093f 60 _LBT_TimeMs = 5;
jreiss 12:2e8fda56093f 61 _LBT_Threshold = -65;
jreiss 12:2e8fda56093f 62
jreiss 12:2e8fda56093f 63 TX_POWERS = KR920_TX_POWERS;
jreiss 12:2e8fda56093f 64 RADIO_POWERS = KR920_RADIO_POWERS;
jreiss 12:2e8fda56093f 65 MAX_PAYLOAD_SIZE = KR920_MAX_PAYLOAD_SIZE;
jreiss 12:2e8fda56093f 66 MAX_PAYLOAD_SIZE_REPEATER = KR920_MAX_PAYLOAD_SIZE_REPEATER;
jreiss 12:2e8fda56093f 67
jreiss 12:2e8fda56093f 68 _minDatarate = 0;
jreiss 12:2e8fda56093f 69 _maxDatarate = 5;
jreiss 12:2e8fda56093f 70
jreiss 12:2e8fda56093f 71 _minRx2Datarate = DR_0;
jreiss 12:2e8fda56093f 72 _maxRx2Datarate = DR_5;
jreiss 12:2e8fda56093f 73
jreiss 12:2e8fda56093f 74 _minDatarateOffset = 0;
jreiss 12:2e8fda56093f 75 _maxDatarateOffset = 5;
jreiss 12:2e8fda56093f 76
jreiss 12:2e8fda56093f 77 _numChans125k = 16;
jreiss 12:2e8fda56093f 78 _numChans500k = 0;
jreiss 12:2e8fda56093f 79
jreiss 12:2e8fda56093f 80 _settings.Session.Rx2Frequency = 921900000;
jreiss 12:2e8fda56093f 81 _settings.Session.Rx2DatarateIndex = DR_0;
jreiss 12:2e8fda56093f 82
jreiss 12:2e8fda56093f 83 logInfo("Initialize datarates...");
jreiss 12:2e8fda56093f 84
jreiss 12:2e8fda56093f 85 dr.SpreadingFactor = SF_12;
jreiss 12:2e8fda56093f 86
jreiss 12:2e8fda56093f 87 // Add DR0-5
jreiss 12:2e8fda56093f 88 while (dr.SpreadingFactor >= SF_7) {
jreiss 12:2e8fda56093f 89 AddDatarate(-1, dr);
jreiss 12:2e8fda56093f 90 dr.SpreadingFactor--;
jreiss 12:2e8fda56093f 91 dr.Index++;
jreiss 12:2e8fda56093f 92 }
jreiss 12:2e8fda56093f 93
jreiss 12:2e8fda56093f 94 // Skip DR6-15 RFU
jreiss 12:2e8fda56093f 95 dr.SpreadingFactor = SF_INVALID;
jreiss 12:2e8fda56093f 96 while (dr.Index++ < DR_15) {
jreiss 12:2e8fda56093f 97 AddDatarate(-1, dr);
jreiss 12:2e8fda56093f 98 }
jreiss 12:2e8fda56093f 99
jreiss 12:2e8fda56093f 100 _settings.Session.TxDatarate = 0;
jreiss 12:2e8fda56093f 101
jreiss 12:2e8fda56093f 102 logInfo("Initialize channels...");
jreiss 12:2e8fda56093f 103
jreiss 12:2e8fda56093f 104 Channel chan;
jreiss 12:2e8fda56093f 105 chan.DrRange.Fields.Min = DR_0;
jreiss 12:2e8fda56093f 106 chan.DrRange.Fields.Max = DR_5;
jreiss 12:2e8fda56093f 107 chan.Index = 0;
jreiss 12:2e8fda56093f 108 chan.Frequency = 922100000;
jreiss 12:2e8fda56093f 109 SetNumberOfChannels(16);
jreiss 12:2e8fda56093f 110
jreiss 12:2e8fda56093f 111 uint8_t numDefaultChannels = 3;
jreiss 12:2e8fda56093f 112
jreiss 12:2e8fda56093f 113 for (uint8_t i = 0; i < numDefaultChannels; i++) {
jreiss 12:2e8fda56093f 114 AddChannel(i, chan);
jreiss 12:2e8fda56093f 115 chan.Index++;
jreiss 12:2e8fda56093f 116 chan.Frequency += 200000;
jreiss 12:2e8fda56093f 117 }
jreiss 12:2e8fda56093f 118
jreiss 12:2e8fda56093f 119 chan.DrRange.Value = 0;
jreiss 12:2e8fda56093f 120 chan.Frequency = 0;
jreiss 12:2e8fda56093f 121
jreiss 12:2e8fda56093f 122 for (uint8_t i = numDefaultChannels; i < 16; i++) {
jreiss 12:2e8fda56093f 123 AddChannel(i, chan);
jreiss 12:2e8fda56093f 124 chan.Index++;
jreiss 12:2e8fda56093f 125 }
jreiss 12:2e8fda56093f 126
jreiss 12:2e8fda56093f 127 // Add downlink channel defaults
jreiss 12:2e8fda56093f 128 chan.Index = 0;
jreiss 12:2e8fda56093f 129 _dlChannels.resize(16);
jreiss 12:2e8fda56093f 130 for (uint8_t i = 0; i < 16; i++) {
jreiss 12:2e8fda56093f 131 AddDownlinkChannel(i, chan);
jreiss 12:2e8fda56093f 132 chan.Index++;
jreiss 12:2e8fda56093f 133 }
jreiss 12:2e8fda56093f 134
jreiss 12:2e8fda56093f 135 SetChannelMask(0, 0x07);
jreiss 12:2e8fda56093f 136
jreiss 12:2e8fda56093f 137 band.Index = 0;
jreiss 12:2e8fda56093f 138 band.FrequencyMin = _minFrequency;
jreiss 12:2e8fda56093f 139 band.FrequencyMax = _maxFrequency;
jreiss 12:2e8fda56093f 140 band.PowerMax = 14;
jreiss 12:2e8fda56093f 141 band.TimeOffEnd = 0;
jreiss 12:2e8fda56093f 142
jreiss 12:2e8fda56093f 143 // Disable duty-cycle limits
jreiss 12:2e8fda56093f 144 band.DutyCycle = 0;
jreiss 12:2e8fda56093f 145
jreiss 12:2e8fda56093f 146 AddDutyBand(-1, band);
jreiss 12:2e8fda56093f 147 }
jreiss 12:2e8fda56093f 148
jreiss 12:2e8fda56093f 149 uint8_t CustomChannelPlan_KR920::AddChannel(int8_t index, Channel channel) {
jreiss 12:2e8fda56093f 150 logTrace("Add Channel %d : %lu : %02x %d", index, channel.Frequency, channel.DrRange.Value, _channels.size());
jreiss 12:2e8fda56093f 151
jreiss 12:2e8fda56093f 152 assert(index < (int) _channels.size());
jreiss 12:2e8fda56093f 153
jreiss 12:2e8fda56093f 154 if (index >= 0) {
jreiss 12:2e8fda56093f 155 _channels[index] = channel;
jreiss 12:2e8fda56093f 156 } else {
jreiss 12:2e8fda56093f 157 _channels.push_back(channel);
jreiss 12:2e8fda56093f 158 }
jreiss 12:2e8fda56093f 159
jreiss 12:2e8fda56093f 160 return LORA_OK;
jreiss 12:2e8fda56093f 161 }
jreiss 12:2e8fda56093f 162
jreiss 12:2e8fda56093f 163 uint8_t CustomChannelPlan_KR920::HandleJoinAccept(const uint8_t* buffer, uint8_t size) {
jreiss 12:2e8fda56093f 164
jreiss 12:2e8fda56093f 165 if (size == 33) {
jreiss 12:2e8fda56093f 166 Channel ch;
jreiss 12:2e8fda56093f 167 int index = 3;
jreiss 12:2e8fda56093f 168 for (int i = 13; i < size - 5; i += 3) {
jreiss 12:2e8fda56093f 169
jreiss 12:2e8fda56093f 170 ch.Frequency = ((buffer[i]) | (buffer[i + 1] << 8) | (buffer[i + 2] << 16)) * 100u;
jreiss 12:2e8fda56093f 171
jreiss 12:2e8fda56093f 172 if (ch.Frequency > 0) {
jreiss 12:2e8fda56093f 173 ch.Index = index;
jreiss 12:2e8fda56093f 174 ch.DrRange.Fields.Min = static_cast<int8_t>(DR_0);
jreiss 12:2e8fda56093f 175 ch.DrRange.Fields.Max = static_cast<int8_t>(DR_5);
jreiss 12:2e8fda56093f 176 AddChannel(index, ch);
jreiss 12:2e8fda56093f 177
jreiss 12:2e8fda56093f 178 if (GetDutyBand(ch.Frequency) > -1)
jreiss 12:2e8fda56093f 179 _channelMask[0] |= (1 << index);
jreiss 12:2e8fda56093f 180 else
jreiss 12:2e8fda56093f 181 _channelMask[0] |= ~(1 << index);
jreiss 12:2e8fda56093f 182
jreiss 12:2e8fda56093f 183 index += 1;
jreiss 12:2e8fda56093f 184 }
jreiss 12:2e8fda56093f 185 }
jreiss 12:2e8fda56093f 186 }
jreiss 12:2e8fda56093f 187
jreiss 12:2e8fda56093f 188 return LORA_OK;
jreiss 12:2e8fda56093f 189 }
jreiss 12:2e8fda56093f 190
jreiss 12:2e8fda56093f 191 uint8_t CustomChannelPlan_KR920::SetTxConfig() {
jreiss 12:2e8fda56093f 192
jreiss 12:2e8fda56093f 193 logInfo("Configure radio for TX");
jreiss 12:2e8fda56093f 194
jreiss 12:2e8fda56093f 195 uint8_t band = GetDutyBand(GetChannel(_txChannel).Frequency);
jreiss 12:2e8fda56093f 196 Datarate txDr = GetDatarate(_settings.Session.TxDatarate);
jreiss 12:2e8fda56093f 197 int8_t max_pwr = _dutyBands[band].PowerMax;
jreiss 12:2e8fda56093f 198
jreiss 12:2e8fda56093f 199 if (GetChannel(_txChannel).Frequency < 922100000) {
jreiss 12:2e8fda56093f 200 max_pwr = 10;
jreiss 12:2e8fda56093f 201 } else {
jreiss 12:2e8fda56093f 202 max_pwr = 14;
jreiss 12:2e8fda56093f 203 }
jreiss 12:2e8fda56093f 204
jreiss 12:2e8fda56093f 205 int8_t pwr = 0;
jreiss 12:2e8fda56093f 206
jreiss 12:2e8fda56093f 207 pwr = std::min < int8_t > (_settings.Session.TxPower, (max_pwr - _settings.Network.AntennaGain));
jreiss 12:2e8fda56093f 208
jreiss 12:2e8fda56093f 209 for (int i = 20; i >= 0; i--) {
jreiss 12:2e8fda56093f 210 if (RADIO_POWERS[i] <= pwr) {
jreiss 12:2e8fda56093f 211 pwr = i;
jreiss 12:2e8fda56093f 212 break;
jreiss 12:2e8fda56093f 213 }
jreiss 12:2e8fda56093f 214 if (i == 0) {
jreiss 12:2e8fda56093f 215 pwr = i;
jreiss 12:2e8fda56093f 216 }
jreiss 12:2e8fda56093f 217 }
jreiss 12:2e8fda56093f 218
jreiss 12:2e8fda56093f 219 logDebug("Session pwr: %d ant: %d max: %d", _settings.Session.TxPower, _settings.Network.AntennaGain, max_pwr);
jreiss 12:2e8fda56093f 220 logDebug("Radio Power index: %d output: %d total: %d", pwr, RADIO_POWERS[pwr], RADIO_POWERS[pwr] + _settings.Network.AntennaGain);
jreiss 12:2e8fda56093f 221
jreiss 12:2e8fda56093f 222 uint32_t bw = txDr.Bandwidth;
jreiss 12:2e8fda56093f 223 uint32_t sf = txDr.SpreadingFactor;
jreiss 12:2e8fda56093f 224 uint8_t cr = txDr.Coderate;
jreiss 12:2e8fda56093f 225 uint8_t pl = txDr.PreambleLength;
jreiss 12:2e8fda56093f 226 uint16_t fdev = 0;
jreiss 12:2e8fda56093f 227 bool crc = txDr.Crc;
jreiss 12:2e8fda56093f 228 bool iq = txDr.TxIQ;
jreiss 12:2e8fda56093f 229
jreiss 12:2e8fda56093f 230 if (_settings.Network.DisableCRC == true)
jreiss 12:2e8fda56093f 231 crc = false;
jreiss 12:2e8fda56093f 232
jreiss 12:2e8fda56093f 233 SxRadio::RadioModems_t modem = SxRadio::MODEM_LORA;
jreiss 12:2e8fda56093f 234
jreiss 12:2e8fda56093f 235 if (sf == SF_FSK) {
jreiss 12:2e8fda56093f 236 modem = SxRadio::MODEM_FSK;
jreiss 12:2e8fda56093f 237 sf = 50e3;
jreiss 12:2e8fda56093f 238 fdev = 25e3;
jreiss 12:2e8fda56093f 239 bw = 0;
jreiss 12:2e8fda56093f 240 }
jreiss 12:2e8fda56093f 241
jreiss 12:2e8fda56093f 242 _radio.SetTxConfig(modem, pwr, fdev, bw, sf, cr, pl, false, crc, false, 0, iq, 3e3);
jreiss 12:2e8fda56093f 243
jreiss 12:2e8fda56093f 244 return LORA_OK;
jreiss 12:2e8fda56093f 245 }
jreiss 12:2e8fda56093f 246
jreiss 12:2e8fda56093f 247
jreiss 12:2e8fda56093f 248 uint8_t CustomChannelPlan_KR920::SetRxConfig(uint8_t window, bool continuous) {
jreiss 12:2e8fda56093f 249
jreiss 12:2e8fda56093f 250 RxWindow rxw = GetRxWindow(window);
jreiss 12:2e8fda56093f 251 _radio.SetChannel(rxw.Frequency);
jreiss 12:2e8fda56093f 252
jreiss 12:2e8fda56093f 253 Datarate rxDr = GetDatarate(rxw.DatarateIndex);
jreiss 12:2e8fda56093f 254 uint32_t bw = rxDr.Bandwidth;
jreiss 12:2e8fda56093f 255 uint32_t sf = rxDr.SpreadingFactor;
jreiss 12:2e8fda56093f 256 uint8_t cr = rxDr.Coderate;
jreiss 12:2e8fda56093f 257 uint8_t pl = rxDr.PreambleLength;
jreiss 12:2e8fda56093f 258 uint16_t sto = rxDr.SymbolTimeout();
jreiss 12:2e8fda56093f 259 uint32_t afc = 0;
jreiss 12:2e8fda56093f 260 bool crc = rxDr.Crc;
jreiss 12:2e8fda56093f 261
jreiss 12:2e8fda56093f 262 if (_settings.Network.DisableCRC == true)
jreiss 12:2e8fda56093f 263 crc = false;
jreiss 12:2e8fda56093f 264
jreiss 12:2e8fda56093f 265 Datarate txDr = GetDatarate(_settings.Session.TxDatarate);
jreiss 12:2e8fda56093f 266 bool iq = txDr.RxIQ;
jreiss 12:2e8fda56093f 267
jreiss 12:2e8fda56093f 268 if (P2PEnabled()) {
jreiss 12:2e8fda56093f 269 iq = txDr.TxIQ;
jreiss 12:2e8fda56093f 270 }
jreiss 12:2e8fda56093f 271
jreiss 12:2e8fda56093f 272 SxRadio::RadioModems_t modem = SxRadio::MODEM_LORA;
jreiss 12:2e8fda56093f 273
jreiss 12:2e8fda56093f 274 if (sf == SF_FSK) {
jreiss 12:2e8fda56093f 275 modem = SxRadio::MODEM_FSK;
jreiss 12:2e8fda56093f 276 sf = 50e3;
jreiss 12:2e8fda56093f 277 cr = 0;
jreiss 12:2e8fda56093f 278 bw = 50e3;
jreiss 12:2e8fda56093f 279 afc = 83333;
jreiss 12:2e8fda56093f 280 iq = false;
jreiss 12:2e8fda56093f 281 }
jreiss 12:2e8fda56093f 282
jreiss 12:2e8fda56093f 283 // Disable printf's to actually receive packets, printing to debug may mess up the timing
jreiss 12:2e8fda56093f 284 // logTrace("Configure radio for RX%d on freq: %lu", window, rxw.Frequency);
jreiss 12:2e8fda56093f 285 // logTrace("RX SF: %u BW: %u CR: %u PL: %u STO: %u CRC: %d IQ: %d", sf, bw, cr, pl, sto, crc, iq);
jreiss 12:2e8fda56093f 286
jreiss 12:2e8fda56093f 287 _radio.SetRxConfig(modem, bw, sf, cr, afc, pl, sto, false, 0, crc, false, 0, iq, continuous);
jreiss 12:2e8fda56093f 288
jreiss 12:2e8fda56093f 289 return LORA_OK;
jreiss 12:2e8fda56093f 290 }
jreiss 12:2e8fda56093f 291
jreiss 12:2e8fda56093f 292 Channel CustomChannelPlan_KR920::GetChannel(int8_t index) {
jreiss 12:2e8fda56093f 293 Channel chan;
jreiss 12:2e8fda56093f 294 memset(&chan, 0, sizeof(Channel));
jreiss 12:2e8fda56093f 295
jreiss 12:2e8fda56093f 296 chan = _channels[index];
jreiss 12:2e8fda56093f 297
jreiss 12:2e8fda56093f 298 return chan;
jreiss 12:2e8fda56093f 299 }
jreiss 12:2e8fda56093f 300
jreiss 12:2e8fda56093f 301 uint8_t CustomChannelPlan_KR920::SetChannelGroup(uint8_t group) {
jreiss 12:2e8fda56093f 302 return LORA_OK;
jreiss 12:2e8fda56093f 303 }
jreiss 12:2e8fda56093f 304
jreiss 12:2e8fda56093f 305 void CustomChannelPlan_KR920::LogRxWindow(uint8_t wnd) {
jreiss 12:2e8fda56093f 306
jreiss 12:2e8fda56093f 307 RxWindow rxw = GetRxWindow(wnd);
jreiss 12:2e8fda56093f 308 Datarate rxDr = GetDatarate(rxw.DatarateIndex);
jreiss 12:2e8fda56093f 309 uint8_t bw = rxDr.Bandwidth;
jreiss 12:2e8fda56093f 310 uint8_t sf = rxDr.SpreadingFactor;
jreiss 12:2e8fda56093f 311 uint8_t cr = rxDr.Coderate;
jreiss 12:2e8fda56093f 312 uint8_t pl = rxDr.PreambleLength;
jreiss 12:2e8fda56093f 313 uint16_t sto = rxDr.SymbolTimeout();
jreiss 12:2e8fda56093f 314 bool crc = rxDr.Crc;
jreiss 12:2e8fda56093f 315 bool iq = GetTxDatarate().RxIQ;
jreiss 12:2e8fda56093f 316
jreiss 12:2e8fda56093f 317 logTrace("RX%d on freq: %lu", wnd, rxw.Frequency);
jreiss 12:2e8fda56093f 318 logTrace("RX DR: %u SF: %u BW: %u CR: %u PL: %u STO: %u CRC: %d IQ: %d", rxDr.Index, sf, bw, cr, pl, sto, crc, iq);
jreiss 12:2e8fda56093f 319 }
jreiss 12:2e8fda56093f 320
jreiss 12:2e8fda56093f 321 RxWindow CustomChannelPlan_KR920::GetRxWindow(uint8_t window) {
jreiss 12:2e8fda56093f 322 RxWindow rxw;
jreiss 12:2e8fda56093f 323 int index = 0;
jreiss 12:2e8fda56093f 324
jreiss 12:2e8fda56093f 325 if (P2PEnabled()) {
jreiss 12:2e8fda56093f 326 rxw.Frequency = _settings.Network.TxFrequency;
jreiss 12:2e8fda56093f 327 index = _settings.Session.TxDatarate;
jreiss 12:2e8fda56093f 328 } else {
jreiss 12:2e8fda56093f 329 if (window == 1) {
jreiss 12:2e8fda56093f 330 // Use same frequency as TX
jreiss 12:2e8fda56093f 331 rxw.Frequency = _channels[_txChannel].Frequency;
jreiss 12:2e8fda56093f 332
jreiss 12:2e8fda56093f 333 if (_settings.Session.TxDatarate > _settings.Session.Rx1DatarateOffset) {
jreiss 12:2e8fda56093f 334 index = _settings.Session.TxDatarate - _settings.Session.Rx1DatarateOffset;
jreiss 12:2e8fda56093f 335 } else {
jreiss 12:2e8fda56093f 336 index = 0;
jreiss 12:2e8fda56093f 337 }
jreiss 12:2e8fda56093f 338
jreiss 12:2e8fda56093f 339 } else {
jreiss 12:2e8fda56093f 340 // Use session RX2 frequency
jreiss 12:2e8fda56093f 341 rxw.Frequency = _settings.Session.Rx2Frequency;
jreiss 12:2e8fda56093f 342 index = _settings.Session.Rx2DatarateIndex;
jreiss 12:2e8fda56093f 343 }
jreiss 12:2e8fda56093f 344 }
jreiss 12:2e8fda56093f 345
jreiss 12:2e8fda56093f 346 rxw.DatarateIndex = index;
jreiss 12:2e8fda56093f 347
jreiss 12:2e8fda56093f 348 return rxw;
jreiss 12:2e8fda56093f 349 }
jreiss 12:2e8fda56093f 350
jreiss 12:2e8fda56093f 351 uint8_t CustomChannelPlan_KR920::HandleRxParamSetup(const uint8_t* payload, uint8_t index, uint8_t size, uint8_t& status) {
jreiss 12:2e8fda56093f 352 status = 0x07;
jreiss 12:2e8fda56093f 353 int8_t datarate = 0;
jreiss 12:2e8fda56093f 354 int8_t drOffset = 0;
jreiss 12:2e8fda56093f 355 uint32_t freq = 0;
jreiss 12:2e8fda56093f 356
jreiss 12:2e8fda56093f 357 drOffset = payload[index++];
jreiss 12:2e8fda56093f 358 datarate = drOffset & 0x0F;
jreiss 12:2e8fda56093f 359 drOffset = (drOffset >> 4) & 0x07;
jreiss 12:2e8fda56093f 360
jreiss 12:2e8fda56093f 361 freq = payload[index++];
jreiss 12:2e8fda56093f 362 freq |= payload[index++] << 8;
jreiss 12:2e8fda56093f 363 freq |= payload[index++] << 16;
jreiss 12:2e8fda56093f 364 freq *= 100;
jreiss 12:2e8fda56093f 365
jreiss 12:2e8fda56093f 366 if (!CheckRfFrequency(freq)) {
jreiss 12:2e8fda56093f 367 logInfo("Freq KO");
jreiss 12:2e8fda56093f 368 status &= 0xFE; // Channel frequency KO
jreiss 12:2e8fda56093f 369 }
jreiss 12:2e8fda56093f 370
jreiss 12:2e8fda56093f 371 if (datarate < _minRx2Datarate || datarate > _maxRx2Datarate) {
jreiss 12:2e8fda56093f 372 logInfo("DR KO");
jreiss 12:2e8fda56093f 373 status &= 0xFD; // Datarate KO
jreiss 12:2e8fda56093f 374 }
jreiss 12:2e8fda56093f 375
jreiss 12:2e8fda56093f 376 if (drOffset < 0 || drOffset > _maxDatarateOffset) {
jreiss 12:2e8fda56093f 377 logInfo("DR Offset KO");
jreiss 12:2e8fda56093f 378 status &= 0xFB; // Rx1DrOffset range KO
jreiss 12:2e8fda56093f 379 }
jreiss 12:2e8fda56093f 380
jreiss 12:2e8fda56093f 381 if ((status & 0x07) == 0x07) {
jreiss 12:2e8fda56093f 382 logInfo("RxParamSetup accepted Rx2DR: %d Rx2Freq: %d Rx1Offset: %d", datarate, freq, drOffset);
jreiss 12:2e8fda56093f 383 SetRx2DatarateIndex(datarate);
jreiss 12:2e8fda56093f 384 SetRx2Frequency(freq);
jreiss 12:2e8fda56093f 385 SetRx1Offset(drOffset);
jreiss 12:2e8fda56093f 386 } else {
jreiss 12:2e8fda56093f 387 logInfo("RxParamSetup rejected Rx2DR: %d Rx2Freq: %d Rx1Offset: %d", datarate, freq, drOffset);
jreiss 12:2e8fda56093f 388 }
jreiss 12:2e8fda56093f 389
jreiss 12:2e8fda56093f 390 return LORA_OK;
jreiss 12:2e8fda56093f 391 }
jreiss 12:2e8fda56093f 392
jreiss 12:2e8fda56093f 393 uint8_t CustomChannelPlan_KR920::HandleNewChannel(const uint8_t* payload, uint8_t index, uint8_t size, uint8_t& status) {
jreiss 12:2e8fda56093f 394
jreiss 12:2e8fda56093f 395 status = 0x03;
jreiss 12:2e8fda56093f 396 uint8_t channelIndex = 0;
jreiss 12:2e8fda56093f 397 Channel chParam;
jreiss 12:2e8fda56093f 398
jreiss 12:2e8fda56093f 399 channelIndex = payload[index++];
jreiss 12:2e8fda56093f 400 lora::CopyFreqtoInt(payload + index, chParam.Frequency);
jreiss 12:2e8fda56093f 401 index += 3;
jreiss 12:2e8fda56093f 402 chParam.DrRange.Value = payload[index++];
jreiss 12:2e8fda56093f 403
jreiss 12:2e8fda56093f 404 if (channelIndex < 3 || channelIndex > _channels.size() - 1) {
jreiss 12:2e8fda56093f 405 logError("New Channel index KO");
jreiss 12:2e8fda56093f 406 status &= 0xFE; // Channel index KO
jreiss 12:2e8fda56093f 407 }
jreiss 12:2e8fda56093f 408
jreiss 12:2e8fda56093f 409 if (!_radio.CheckRfFrequency(chParam.Frequency)) {
jreiss 12:2e8fda56093f 410 logError("New Channel frequency KO");
jreiss 12:2e8fda56093f 411 status &= 0xFE; // Channel frequency KO
jreiss 12:2e8fda56093f 412 }
jreiss 12:2e8fda56093f 413
jreiss 12:2e8fda56093f 414 if (chParam.DrRange.Fields.Min > chParam.DrRange.Fields.Max) {
jreiss 12:2e8fda56093f 415 logError("New Channel datarate min/max KO");
jreiss 12:2e8fda56093f 416 status &= 0xFD; // Datarate range KO
jreiss 12:2e8fda56093f 417 } else if (chParam.DrRange.Fields.Min < _minDatarate || chParam.DrRange.Fields.Min > _maxDatarate) {
jreiss 12:2e8fda56093f 418 logError("New Channel datarate min KO");
jreiss 12:2e8fda56093f 419 status &= 0xFD; // Datarate range KO
jreiss 12:2e8fda56093f 420 } else if (chParam.DrRange.Fields.Max < _minDatarate || chParam.DrRange.Fields.Max > _maxDatarate) {
jreiss 12:2e8fda56093f 421 logError("New Channel datarate max KO");
jreiss 12:2e8fda56093f 422 status &= 0xFD; // Datarate range KO
jreiss 12:2e8fda56093f 423 }
jreiss 12:2e8fda56093f 424
jreiss 12:2e8fda56093f 425 if ((status & 0x03) == 0x03) {
jreiss 12:2e8fda56093f 426 logInfo("New Channel accepted index: %d freq: %lu drRange: %02x", channelIndex, chParam.Frequency, chParam.DrRange.Value);
jreiss 12:2e8fda56093f 427 AddChannel(channelIndex, chParam);
jreiss 12:2e8fda56093f 428 SetChannelMask(0, _channelMask[0] | 1 << (channelIndex));
jreiss 12:2e8fda56093f 429 }
jreiss 12:2e8fda56093f 430
jreiss 12:2e8fda56093f 431 return LORA_OK;
jreiss 12:2e8fda56093f 432 }
jreiss 12:2e8fda56093f 433
jreiss 12:2e8fda56093f 434 uint8_t CustomChannelPlan_KR920::HandlePingSlotChannelReq(const uint8_t* payload, uint8_t index, uint8_t size, uint8_t& status) {
jreiss 12:2e8fda56093f 435
jreiss 12:2e8fda56093f 436 lora::CopyFreqtoInt(payload + index, _beaconRxChannel.Frequency);
jreiss 12:2e8fda56093f 437 index += 3;
jreiss 12:2e8fda56093f 438
jreiss 12:2e8fda56093f 439 if (_beaconRxChannel.Frequency != 0) {
jreiss 12:2e8fda56093f 440 _beaconRxChannel.DrRange.Value = payload[index];
jreiss 12:2e8fda56093f 441 } else {
jreiss 12:2e8fda56093f 442 // TODO: set to default beacon rx channel
jreiss 12:2e8fda56093f 443 }
jreiss 12:2e8fda56093f 444
jreiss 12:2e8fda56093f 445 status = 0x03;
jreiss 12:2e8fda56093f 446 return LORA_OK;
jreiss 12:2e8fda56093f 447 }
jreiss 12:2e8fda56093f 448
jreiss 12:2e8fda56093f 449 uint8_t CustomChannelPlan_KR920::HandleBeaconFrequencyReq(const uint8_t* payload, uint8_t index, uint8_t size, uint8_t& status) {
jreiss 12:2e8fda56093f 450
jreiss 12:2e8fda56093f 451 status = 0x03;
jreiss 12:2e8fda56093f 452 Channel chParam;
jreiss 12:2e8fda56093f 453
jreiss 12:2e8fda56093f 454 // Skip channel index
jreiss 12:2e8fda56093f 455 index++;
jreiss 12:2e8fda56093f 456
jreiss 12:2e8fda56093f 457 lora::CopyFreqtoInt(payload + index, chParam.Frequency);
jreiss 12:2e8fda56093f 458 index += 3;
jreiss 12:2e8fda56093f 459 chParam.DrRange.Value = payload[index++];
jreiss 12:2e8fda56093f 460
jreiss 12:2e8fda56093f 461 if (!_radio.CheckRfFrequency(chParam.Frequency)) {
jreiss 12:2e8fda56093f 462 status &= 0xFE; // Channel frequency KO
jreiss 12:2e8fda56093f 463 }
jreiss 12:2e8fda56093f 464
jreiss 12:2e8fda56093f 465 if (chParam.DrRange.Fields.Min < chParam.DrRange.Fields.Max) {
jreiss 12:2e8fda56093f 466 status &= 0xFD; // Datarate range KO
jreiss 12:2e8fda56093f 467 } else if (chParam.DrRange.Fields.Min < _minDatarate || chParam.DrRange.Fields.Min > _maxDatarate) {
jreiss 12:2e8fda56093f 468 status &= 0xFD; // Datarate range KO
jreiss 12:2e8fda56093f 469 } else if (chParam.DrRange.Fields.Max < _minDatarate || chParam.DrRange.Fields.Max > _maxDatarate) {
jreiss 12:2e8fda56093f 470 status &= 0xFD; // Datarate range KO
jreiss 12:2e8fda56093f 471 }
jreiss 12:2e8fda56093f 472
jreiss 12:2e8fda56093f 473 if ((status & 0x03) == 0x03) {
jreiss 12:2e8fda56093f 474 _beaconChannel = chParam;
jreiss 12:2e8fda56093f 475 }
jreiss 11:829f8c2ec1c3 476
jreiss 12:2e8fda56093f 477 if (_beaconChannel.Frequency == 0) {
jreiss 12:2e8fda56093f 478 // TODO: Set to default
jreiss 12:2e8fda56093f 479 }
jreiss 12:2e8fda56093f 480
jreiss 12:2e8fda56093f 481 status = 0x01;
jreiss 12:2e8fda56093f 482
jreiss 12:2e8fda56093f 483 return LORA_OK;
jreiss 12:2e8fda56093f 484 }
jreiss 12:2e8fda56093f 485
jreiss 12:2e8fda56093f 486 bool CustomChannelPlan_KR920::AdrAckReq() {
jreiss 12:2e8fda56093f 487 if (_settings.Network.ADREnabled == false)
jreiss 12:2e8fda56093f 488 return false;
jreiss 12:2e8fda56093f 489
jreiss 12:2e8fda56093f 490 bool ret = false;
jreiss 12:2e8fda56093f 491
jreiss 12:2e8fda56093f 492 if (_settings.Session.TxDatarate == MIN_DATARATE) {
jreiss 12:2e8fda56093f 493 _settings.Session.AdrCounter = 0;
jreiss 12:2e8fda56093f 494 } else {
jreiss 12:2e8fda56093f 495 logDebug("ADR ACK CNT: %d LIMIT: %d DELAY: %d", _settings.Session.AdrCounter, ADR_ACK_LIMIT, ADR_ACK_DELAY);
jreiss 12:2e8fda56093f 496
jreiss 12:2e8fda56093f 497 ret = (_settings.Session.AdrCounter >= ADR_ACK_LIMIT);
jreiss 12:2e8fda56093f 498
jreiss 12:2e8fda56093f 499 if (_settings.Session.AdrCounter >= (ADR_ACK_LIMIT + ADR_ACK_DELAY)) {
jreiss 12:2e8fda56093f 500 if (_settings.Session.AdrCounter - 1 % ADR_ACK_DELAY == 0) {
jreiss 12:2e8fda56093f 501
jreiss 12:2e8fda56093f 502 if (_settings.Session.TxDatarate > MIN_DATARATE) {
jreiss 12:2e8fda56093f 503 _settings.Session.TxDatarate--;
jreiss 12:2e8fda56093f 504 }
jreiss 12:2e8fda56093f 505
jreiss 12:2e8fda56093f 506 if (_settings.Session.TxDatarate == MIN_DATARATE) {
jreiss 12:2e8fda56093f 507 EnableDefaultChannels();
jreiss 12:2e8fda56093f 508 }
jreiss 12:2e8fda56093f 509 }
jreiss 12:2e8fda56093f 510 }
jreiss 12:2e8fda56093f 511
jreiss 12:2e8fda56093f 512 }
jreiss 12:2e8fda56093f 513
jreiss 12:2e8fda56093f 514 return ret;
jreiss 12:2e8fda56093f 515 }
jreiss 12:2e8fda56093f 516
jreiss 12:2e8fda56093f 517 uint8_t CustomChannelPlan_KR920::HandleAdrCommand(const uint8_t* payload, uint8_t index, uint8_t size, uint8_t& status) {
jreiss 12:2e8fda56093f 518
jreiss 12:2e8fda56093f 519 uint8_t power = 0;
jreiss 12:2e8fda56093f 520 uint8_t datarate = 0;
jreiss 12:2e8fda56093f 521 uint16_t mask = 0;
jreiss 12:2e8fda56093f 522 uint8_t ctrl = 0;
jreiss 12:2e8fda56093f 523 uint8_t nbRep = 0;
jreiss 12:2e8fda56093f 524
jreiss 12:2e8fda56093f 525 status = 0x07;
jreiss 12:2e8fda56093f 526 datarate = payload[index++];
jreiss 12:2e8fda56093f 527 power = datarate & 0x0F;
jreiss 12:2e8fda56093f 528 datarate = (datarate >> 4) & 0x0F;
jreiss 12:2e8fda56093f 529
jreiss 12:2e8fda56093f 530 mask = payload[index++];
jreiss 12:2e8fda56093f 531 mask |= payload[index++] << 8;
jreiss 12:2e8fda56093f 532
jreiss 12:2e8fda56093f 533 nbRep = payload[index++];
jreiss 12:2e8fda56093f 534 ctrl = (nbRep >> 4) & 0x07;
jreiss 12:2e8fda56093f 535 nbRep &= 0x0F;
jreiss 12:2e8fda56093f 536
jreiss 12:2e8fda56093f 537 if (nbRep == 0) {
jreiss 12:2e8fda56093f 538 nbRep = 1;
jreiss 12:2e8fda56093f 539 }
jreiss 12:2e8fda56093f 540
jreiss 12:2e8fda56093f 541 if (!(mask == 0 && ctrl == 0)) {
jreiss 12:2e8fda56093f 542 if (ctrl == 5) {
jreiss 12:2e8fda56093f 543 mask = 0;
jreiss 12:2e8fda56093f 544 ctrl = 0;
jreiss 12:2e8fda56093f 545 }
jreiss 12:2e8fda56093f 546 }
jreiss 12:2e8fda56093f 547
jreiss 12:2e8fda56093f 548 if (datarate > _maxDatarate) {
jreiss 12:2e8fda56093f 549 logDebug("ADR Datarate KO");
jreiss 12:2e8fda56093f 550 status &= 0xFD; // Datarate KO
jreiss 12:2e8fda56093f 551 }
jreiss 12:2e8fda56093f 552 //
jreiss 12:2e8fda56093f 553 // Remark MaxTxPower = 0 and MinTxPower = 5
jreiss 12:2e8fda56093f 554 //
jreiss 12:2e8fda56093f 555 if (TX_POWERS[power] < _minTxPower || TX_POWERS[power] > _maxTxPower) {
jreiss 12:2e8fda56093f 556 logDebug("ADR TxPower KO");
jreiss 12:2e8fda56093f 557 status &= 0xFB; // TxPower KO
jreiss 12:2e8fda56093f 558 }
jreiss 12:2e8fda56093f 559
jreiss 12:2e8fda56093f 560 if ((status & 0x07) == 0x07) {
jreiss 12:2e8fda56093f 561 logDebug("ADR settings accepted");
jreiss 12:2e8fda56093f 562
jreiss 12:2e8fda56093f 563 if (_settings.Network.ADREnabled) {
jreiss 12:2e8fda56093f 564 _settings.Session.TxDatarate = datarate;
jreiss 12:2e8fda56093f 565 _settings.Session.TxPower = TX_POWERS[power];
jreiss 12:2e8fda56093f 566 } else {
jreiss 12:2e8fda56093f 567 logInfo("ADR is disabled, DR and Power not changed.");
jreiss 12:2e8fda56093f 568 status &= 0xFB; // TxPower KO
jreiss 12:2e8fda56093f 569 status &= 0xFD; // Datarate KO
jreiss 12:2e8fda56093f 570 }
jreiss 12:2e8fda56093f 571
jreiss 12:2e8fda56093f 572 if (!(mask == 0 && ctrl == 0) || ((mask >= 1 && ctrl <= 5) || ctrl == 7)) {
jreiss 12:2e8fda56093f 573 for (uint8_t i = 0; i < _numChans125k; i++) {
jreiss 12:2e8fda56093f 574 if (ctrl == 6) {
jreiss 12:2e8fda56093f 575 // enable all defined channels
jreiss 12:2e8fda56093f 576 if (_channels[i].Frequency != 0) {
jreiss 12:2e8fda56093f 577 mask |= 1 << i;
jreiss 12:2e8fda56093f 578 }
jreiss 12:2e8fda56093f 579 } else {
jreiss 12:2e8fda56093f 580 if (((mask & (1 << i)) != 0) && (_channels[i].Frequency == 0)) {
jreiss 12:2e8fda56093f 581 // Trying to enable an undefined channel
jreiss 12:2e8fda56093f 582 status &= 0xFE; // Channel mask KO
jreiss 12:2e8fda56093f 583 }
jreiss 12:2e8fda56093f 584 }
jreiss 12:2e8fda56093f 585 }
jreiss 12:2e8fda56093f 586 SetChannelMask(0, mask);
jreiss 12:2e8fda56093f 587 } else {
jreiss 12:2e8fda56093f 588 if (mask == 0 && ctrl == 0) {
jreiss 12:2e8fda56093f 589 logWarning("Rejecting ADR command to disable all channels");
jreiss 12:2e8fda56093f 590 }
jreiss 12:2e8fda56093f 591 status &= 0xFE;
jreiss 12:2e8fda56093f 592 }
jreiss 12:2e8fda56093f 593 _settings.Session.Redundancy = nbRep;
jreiss 12:2e8fda56093f 594 }
jreiss 12:2e8fda56093f 595
jreiss 12:2e8fda56093f 596
jreiss 12:2e8fda56093f 597 logDebug("ADR DR: %u PWR: %u Ctrl: %02x Mask: %04x NbRep: %u Stat: %02x", datarate, power, ctrl, mask, nbRep, status);
jreiss 12:2e8fda56093f 598
jreiss 12:2e8fda56093f 599 return LORA_OK;
jreiss 12:2e8fda56093f 600 }
jreiss 12:2e8fda56093f 601
jreiss 12:2e8fda56093f 602 uint8_t CustomChannelPlan_KR920::HandleAckTimeout() {
jreiss 12:2e8fda56093f 603
jreiss 12:2e8fda56093f 604 if (!_settings.Network.ADREnabled) {
jreiss 12:2e8fda56093f 605 return LORA_ADR_OFF;
jreiss 12:2e8fda56093f 606 }
jreiss 12:2e8fda56093f 607
jreiss 12:2e8fda56093f 608 if ((++_settings.Session.AckCounter % 2) == 0) {
jreiss 12:2e8fda56093f 609 if (_settings.Session.TxPower < _settings.Network.TxPowerMax) {
jreiss 12:2e8fda56093f 610 logTrace("ADR Setting power to maximum");
jreiss 12:2e8fda56093f 611 _settings.Session.TxPower = _settings.Network.TxPowerMax;
jreiss 12:2e8fda56093f 612 } else if (_settings.Session.TxDatarate > 0) {
jreiss 12:2e8fda56093f 613 logTrace("ADR Lowering datarate");
jreiss 12:2e8fda56093f 614 _settings.Session.TxDatarate--;
jreiss 12:2e8fda56093f 615 }
jreiss 12:2e8fda56093f 616 }
jreiss 12:2e8fda56093f 617
jreiss 12:2e8fda56093f 618 return LORA_OK;
jreiss 12:2e8fda56093f 619 }
jreiss 12:2e8fda56093f 620
jreiss 12:2e8fda56093f 621
jreiss 12:2e8fda56093f 622 uint32_t CustomChannelPlan_KR920::GetTimeOffAir()
jreiss 12:2e8fda56093f 623 {
jreiss 12:2e8fda56093f 624 if (_settings.Test.DisableDutyCycle == lora::ON)
jreiss 12:2e8fda56093f 625 return 0;
jreiss 12:2e8fda56093f 626
jreiss 12:2e8fda56093f 627 uint32_t min = 0;
jreiss 12:2e8fda56093f 628 uint32_t now = _dutyCycleTimer.read_ms();
jreiss 12:2e8fda56093f 629
jreiss 12:2e8fda56093f 630
jreiss 12:2e8fda56093f 631 min = UINT_MAX;
jreiss 12:2e8fda56093f 632 int8_t band = 0;
jreiss 12:2e8fda56093f 633
jreiss 12:2e8fda56093f 634 if (P2PEnabled()) {
jreiss 12:2e8fda56093f 635 int8_t band = GetDutyBand(_settings.Network.TxFrequency);
jreiss 12:2e8fda56093f 636 if (_dutyBands[band].TimeOffEnd > now) {
jreiss 12:2e8fda56093f 637 min = _dutyBands[band].TimeOffEnd - now;
jreiss 12:2e8fda56093f 638 } else {
jreiss 12:2e8fda56093f 639 min = 0;
jreiss 12:2e8fda56093f 640 }
jreiss 12:2e8fda56093f 641 } else {
jreiss 12:2e8fda56093f 642 for (size_t i = 0; i < _channels.size(); i++) {
jreiss 12:2e8fda56093f 643 if (IsChannelEnabled(i) && _channels[i].Frequency != 0 &&
jreiss 12:2e8fda56093f 644 !(_settings.Session.TxDatarate < _channels[i].DrRange.Fields.Min ||
jreiss 12:2e8fda56093f 645 _settings.Session.TxDatarate > _channels[i].DrRange.Fields.Max)) {
jreiss 12:2e8fda56093f 646
jreiss 12:2e8fda56093f 647 band = GetDutyBand(_channels[i].Frequency);
jreiss 12:2e8fda56093f 648 if (band != -1) {
jreiss 12:2e8fda56093f 649 // logDebug("band: %d time-off: %d now: %d", band, _dutyBands[band].TimeOffEnd, now);
jreiss 12:2e8fda56093f 650 if (_dutyBands[band].TimeOffEnd > now) {
jreiss 12:2e8fda56093f 651 min = std::min < uint32_t > (min, _dutyBands[band].TimeOffEnd - now);
jreiss 12:2e8fda56093f 652 } else {
jreiss 12:2e8fda56093f 653 min = 0;
jreiss 12:2e8fda56093f 654 break;
jreiss 12:2e8fda56093f 655 }
jreiss 12:2e8fda56093f 656 }
jreiss 12:2e8fda56093f 657 }
jreiss 12:2e8fda56093f 658 }
jreiss 12:2e8fda56093f 659 }
jreiss 12:2e8fda56093f 660
jreiss 12:2e8fda56093f 661
jreiss 12:2e8fda56093f 662 if (_settings.Session.AggregatedTimeOffEnd > 0 && _settings.Session.AggregatedTimeOffEnd > now) {
jreiss 12:2e8fda56093f 663 min = std::max < uint32_t > (min, _settings.Session.AggregatedTimeOffEnd - now);
jreiss 12:2e8fda56093f 664 }
jreiss 12:2e8fda56093f 665
jreiss 12:2e8fda56093f 666 now = time(NULL);
jreiss 12:2e8fda56093f 667 uint32_t join_time = 0;
jreiss 12:2e8fda56093f 668
jreiss 12:2e8fda56093f 669 if (_settings.Session.JoinFirstAttempt != 0 && now < _settings.Session.JoinTimeOffEnd) {
jreiss 12:2e8fda56093f 670 join_time = (_settings.Session.JoinTimeOffEnd - now) * 1000;
jreiss 12:2e8fda56093f 671 }
jreiss 12:2e8fda56093f 672
jreiss 12:2e8fda56093f 673 min = std::max < uint32_t > (join_time, min);
jreiss 12:2e8fda56093f 674
jreiss 12:2e8fda56093f 675 return min;
jreiss 12:2e8fda56093f 676 }
jreiss 12:2e8fda56093f 677
jreiss 12:2e8fda56093f 678
jreiss 12:2e8fda56093f 679 void CustomChannelPlan_KR920::UpdateDutyCycle(uint32_t freq, uint32_t time_on_air_ms) {
jreiss 12:2e8fda56093f 680 if (_settings.Test.DisableDutyCycle == lora::ON) {
jreiss 12:2e8fda56093f 681 _dutyCycleTimer.stop();
jreiss 12:2e8fda56093f 682 for (size_t i = 0; i < _dutyBands.size(); i++) {
jreiss 12:2e8fda56093f 683 _dutyBands[i].TimeOffEnd = 0;
jreiss 12:2e8fda56093f 684 }
jreiss 12:2e8fda56093f 685 return;
jreiss 12:2e8fda56093f 686 }
jreiss 12:2e8fda56093f 687
jreiss 12:2e8fda56093f 688 _dutyCycleTimer.start();
jreiss 12:2e8fda56093f 689
jreiss 12:2e8fda56093f 690 if (_settings.Session.MaxDutyCycle > 0 && _settings.Session.MaxDutyCycle <= 15) {
jreiss 12:2e8fda56093f 691 _settings.Session.AggregatedTimeOffEnd = _dutyCycleTimer.read_ms() + time_on_air_ms * _settings.Session.AggregateDutyCycle;
jreiss 12:2e8fda56093f 692 logDebug("Updated Aggregate DCycle Time-off: %lu DC: %f%%", _settings.Session.AggregatedTimeOffEnd, 1 / float(_settings.Session.AggregateDutyCycle));
jreiss 12:2e8fda56093f 693 } else {
jreiss 12:2e8fda56093f 694 _settings.Session.AggregatedTimeOffEnd = 0;
jreiss 12:2e8fda56093f 695 }
jreiss 12:2e8fda56093f 696
jreiss 12:2e8fda56093f 697
jreiss 12:2e8fda56093f 698 uint32_t time_off_air = 0;
jreiss 12:2e8fda56093f 699 uint32_t now = _dutyCycleTimer.read_ms();
jreiss 12:2e8fda56093f 700
jreiss 12:2e8fda56093f 701 for (size_t i = 0; i < _dutyBands.size(); i++) {
jreiss 12:2e8fda56093f 702 if (_dutyBands[i].TimeOffEnd < now) {
jreiss 12:2e8fda56093f 703 _dutyBands[i].TimeOffEnd = 0;
jreiss 12:2e8fda56093f 704 } else {
jreiss 12:2e8fda56093f 705 _dutyBands[i].TimeOffEnd -= now;
jreiss 12:2e8fda56093f 706 }
jreiss 12:2e8fda56093f 707
jreiss 12:2e8fda56093f 708 if (freq >= _dutyBands[i].FrequencyMin && freq <= _dutyBands[i].FrequencyMax) {
jreiss 12:2e8fda56093f 709 logDebug("update TOE: freq: %d i:%d toa: %d DC:%d", freq, i, time_on_air_ms, _dutyBands[i].DutyCycle);
jreiss 12:2e8fda56093f 710
jreiss 12:2e8fda56093f 711 if (freq > _minFrequency && freq < _maxFrequency && (_settings.Session.TxPower + _settings.Network.AntennaGain) <= 7) {
jreiss 12:2e8fda56093f 712 _dutyBands[i].TimeOffEnd = 0;
jreiss 12:2e8fda56093f 713 } else {
jreiss 12:2e8fda56093f 714 time_off_air = time_on_air_ms * _dutyBands[i].DutyCycle;
jreiss 12:2e8fda56093f 715 _dutyBands[i].TimeOffEnd = time_off_air;
jreiss 12:2e8fda56093f 716 }
jreiss 12:2e8fda56093f 717 }
jreiss 12:2e8fda56093f 718 }
jreiss 12:2e8fda56093f 719
jreiss 12:2e8fda56093f 720
jreiss 12:2e8fda56093f 721 ResetDutyCycleTimer();
jreiss 12:2e8fda56093f 722 }
jreiss 12:2e8fda56093f 723
jreiss 12:2e8fda56093f 724 std::vector<uint32_t> lora::CustomChannelPlan_KR920::GetChannels() {
jreiss 12:2e8fda56093f 725 std::vector < uint32_t > chans;
jreiss 12:2e8fda56093f 726
jreiss 12:2e8fda56093f 727 for (int8_t i = 0; i < (int) _channels.size(); i++) {
jreiss 12:2e8fda56093f 728 chans.push_back(_channels[i].Frequency);
jreiss 12:2e8fda56093f 729 }
jreiss 12:2e8fda56093f 730 chans.push_back(GetRxWindow(2).Frequency);
jreiss 12:2e8fda56093f 731
jreiss 12:2e8fda56093f 732 return chans;
jreiss 12:2e8fda56093f 733 }
jreiss 12:2e8fda56093f 734
jreiss 12:2e8fda56093f 735 std::vector<uint8_t> lora::CustomChannelPlan_KR920::GetChannelRanges() {
jreiss 12:2e8fda56093f 736 std::vector < uint8_t > ranges;
jreiss 12:2e8fda56093f 737
jreiss 12:2e8fda56093f 738 for (int8_t i = 0; i < (int) _channels.size(); i++) {
jreiss 12:2e8fda56093f 739 ranges.push_back(_channels[i].DrRange.Value);
jreiss 12:2e8fda56093f 740 }
jreiss 12:2e8fda56093f 741
jreiss 12:2e8fda56093f 742 ranges.push_back(GetRxWindow(2).DatarateIndex);
jreiss 12:2e8fda56093f 743
jreiss 12:2e8fda56093f 744 return ranges;
jreiss 12:2e8fda56093f 745
jreiss 12:2e8fda56093f 746 }
jreiss 12:2e8fda56093f 747
jreiss 12:2e8fda56093f 748 void lora::CustomChannelPlan_KR920::EnableDefaultChannels() {
jreiss 12:2e8fda56093f 749 _channelMask[0] |= 0x0003;
jreiss 12:2e8fda56093f 750 }
jreiss 12:2e8fda56093f 751
jreiss 12:2e8fda56093f 752 uint8_t CustomChannelPlan_KR920::GetNextChannel()
jreiss 12:2e8fda56093f 753 {
jreiss 12:2e8fda56093f 754 if (_settings.Session.AggregatedTimeOffEnd != 0) {
jreiss 12:2e8fda56093f 755 return LORA_AGGREGATED_DUTY_CYCLE;
jreiss 12:2e8fda56093f 756 }
jreiss 12:2e8fda56093f 757
jreiss 12:2e8fda56093f 758 if (P2PEnabled() || _settings.Network.TxFrequency != 0) {
jreiss 12:2e8fda56093f 759 logDebug("Using frequency %d", _settings.Network.TxFrequency);
jreiss 12:2e8fda56093f 760
jreiss 12:2e8fda56093f 761 if (_settings.Test.DisableDutyCycle != lora::ON) {
jreiss 12:2e8fda56093f 762 int8_t band = GetDutyBand(_settings.Network.TxFrequency);
jreiss 12:2e8fda56093f 763 logDebug("band: %d freq: %d", band, _settings.Network.TxFrequency);
jreiss 12:2e8fda56093f 764 if (band != -1 && _dutyBands[band].TimeOffEnd != 0) {
jreiss 12:2e8fda56093f 765 return LORA_NO_CHANS_ENABLED;
jreiss 12:2e8fda56093f 766 }
jreiss 12:2e8fda56093f 767 }
jreiss 12:2e8fda56093f 768
jreiss 12:2e8fda56093f 769 _radio.SetChannel(_settings.Network.TxFrequency);
jreiss 12:2e8fda56093f 770 return LORA_OK;
jreiss 12:2e8fda56093f 771 }
jreiss 12:2e8fda56093f 772
jreiss 12:2e8fda56093f 773 uint8_t start = 0;
jreiss 12:2e8fda56093f 774 uint8_t maxChannels = _numChans125k;
jreiss 12:2e8fda56093f 775 uint8_t nbEnabledChannels = 0;
jreiss 12:2e8fda56093f 776 uint8_t *enabledChannels = new uint8_t[maxChannels];
jreiss 12:2e8fda56093f 777
jreiss 12:2e8fda56093f 778 if (GetTxDatarate().Bandwidth == BW_500) {
jreiss 12:2e8fda56093f 779 maxChannels = _numChans500k;
jreiss 12:2e8fda56093f 780 start = _numChans125k;
jreiss 12:2e8fda56093f 781 }
jreiss 12:2e8fda56093f 782
jreiss 12:2e8fda56093f 783 // Search how many channels are enabled
jreiss 12:2e8fda56093f 784 DatarateRange range;
jreiss 12:2e8fda56093f 785 uint8_t dr_index = _settings.Session.TxDatarate;
jreiss 12:2e8fda56093f 786 uint32_t now = _dutyCycleTimer.read_ms();
jreiss 12:2e8fda56093f 787
jreiss 12:2e8fda56093f 788 for (size_t i = 0; i < _dutyBands.size(); i++) {
jreiss 12:2e8fda56093f 789 if (_dutyBands[i].TimeOffEnd < now || _settings.Test.DisableDutyCycle == lora::ON) {
jreiss 12:2e8fda56093f 790 _dutyBands[i].TimeOffEnd = 0;
jreiss 12:2e8fda56093f 791 }
jreiss 12:2e8fda56093f 792 }
jreiss 12:2e8fda56093f 793
jreiss 12:2e8fda56093f 794 for (uint8_t i = start; i < start + maxChannels; i++) {
jreiss 12:2e8fda56093f 795 range = GetChannel(i).DrRange;
jreiss 12:2e8fda56093f 796 // logDebug("chan: %d freq: %d range:%02x", i, GetChannel(i).Frequency, range.Value);
jreiss 12:2e8fda56093f 797
jreiss 12:2e8fda56093f 798 if (IsChannelEnabled(i) && (dr_index >= range.Fields.Min && dr_index <= range.Fields.Max)) {
jreiss 12:2e8fda56093f 799 int8_t band = GetDutyBand(GetChannel(i).Frequency);
jreiss 12:2e8fda56093f 800 // logDebug("band: %d freq: %d", band, _channels[i].Frequency);
jreiss 12:2e8fda56093f 801 if (band != -1 && _dutyBands[band].TimeOffEnd == 0) {
jreiss 12:2e8fda56093f 802 enabledChannels[nbEnabledChannels++] = i;
jreiss 12:2e8fda56093f 803 }
jreiss 12:2e8fda56093f 804 }
jreiss 12:2e8fda56093f 805 }
jreiss 12:2e8fda56093f 806
jreiss 12:2e8fda56093f 807 logTrace("Number of available channels: %d", nbEnabledChannels);
jreiss 12:2e8fda56093f 808
jreiss 12:2e8fda56093f 809 uint32_t freq = 0;
jreiss 12:2e8fda56093f 810 uint8_t sf = GetTxDatarate().SpreadingFactor;
jreiss 12:2e8fda56093f 811 uint8_t bw = GetTxDatarate().Bandwidth;
jreiss 12:2e8fda56093f 812 int16_t thres = DEFAULT_FREE_CHAN_RSSI_THRESHOLD;
jreiss 12:2e8fda56093f 813
jreiss 12:2e8fda56093f 814 if (nbEnabledChannels == 0) {
jreiss 12:2e8fda56093f 815 delete [] enabledChannels;
jreiss 12:2e8fda56093f 816 return LORA_NO_CHANS_ENABLED;
jreiss 12:2e8fda56093f 817 }
jreiss 12:2e8fda56093f 818
jreiss 12:2e8fda56093f 819 if (_settings.Network.CADEnabled) {
jreiss 12:2e8fda56093f 820 // Search for free channel with ms timeout
jreiss 12:2e8fda56093f 821 int16_t timeout = 10000;
jreiss 12:2e8fda56093f 822 Timer tmr;
jreiss 12:2e8fda56093f 823 tmr.start();
jreiss 12:2e8fda56093f 824
jreiss 12:2e8fda56093f 825 for (uint8_t j = rand_r(0, nbEnabledChannels - 1); tmr.read_ms() < timeout; j++) {
jreiss 12:2e8fda56093f 826 freq = GetChannel(enabledChannels[j]).Frequency;
jreiss 12:2e8fda56093f 827
jreiss 12:2e8fda56093f 828 if (_radio.IsChannelFree(SxRadio::MODEM_LORA, freq, sf, thres, bw)) {
jreiss 12:2e8fda56093f 829 _txChannel = enabledChannels[j];
jreiss 12:2e8fda56093f 830 break;
jreiss 12:2e8fda56093f 831 }
jreiss 12:2e8fda56093f 832 }
jreiss 12:2e8fda56093f 833 } else {
jreiss 12:2e8fda56093f 834 uint8_t j = rand_r(0, nbEnabledChannels - 1);
jreiss 12:2e8fda56093f 835 _txChannel = enabledChannels[j];
jreiss 12:2e8fda56093f 836 freq = GetChannel(_txChannel).Frequency;
jreiss 12:2e8fda56093f 837 }
jreiss 12:2e8fda56093f 838
jreiss 12:2e8fda56093f 839 assert(freq != 0);
jreiss 12:2e8fda56093f 840
jreiss 12:2e8fda56093f 841 logDebug("Using channel %d : %d", _txChannel, freq);
jreiss 12:2e8fda56093f 842 _radio.SetChannel(freq);
jreiss 12:2e8fda56093f 843
jreiss 12:2e8fda56093f 844 delete [] enabledChannels;
jreiss 12:2e8fda56093f 845 return LORA_OK;
jreiss 12:2e8fda56093f 846 }
jreiss 12:2e8fda56093f 847
jreiss 12:2e8fda56093f 848
jreiss 12:2e8fda56093f 849 uint8_t lora::CustomChannelPlan_KR920::GetJoinDatarate() {
jreiss 12:2e8fda56093f 850 uint8_t dr = _settings.Session.TxDatarate;
jreiss 12:2e8fda56093f 851
jreiss 12:2e8fda56093f 852 // Default join datarate is DR2:SF10BW125
jreiss 12:2e8fda56093f 853 dr = lora::DR_2;
jreiss 12:2e8fda56093f 854
jreiss 12:2e8fda56093f 855 return dr;
jreiss 12:2e8fda56093f 856 }
jreiss 12:2e8fda56093f 857
jreiss 12:2e8fda56093f 858 uint8_t CustomChannelPlan_KR920::CalculateJoinBackoff(uint8_t size) {
jreiss 12:2e8fda56093f 859
jreiss 12:2e8fda56093f 860 time_t now = time(NULL);
jreiss 12:2e8fda56093f 861 uint32_t time_on_max = 0;
jreiss 12:2e8fda56093f 862 static uint32_t time_off_max = 15;
jreiss 12:2e8fda56093f 863 uint32_t rand_time_off = 0;
jreiss 12:2e8fda56093f 864
jreiss 12:2e8fda56093f 865 // TODO: calc time-off-max based on RTC time from JoinFirstAttempt, time-off-max is lost over sleep
jreiss 12:2e8fda56093f 866
jreiss 12:2e8fda56093f 867 if (_settings.Session.JoinTimeOffEnd > now) {
jreiss 12:2e8fda56093f 868 return LORA_JOIN_BACKOFF;
jreiss 12:2e8fda56093f 869 }
jreiss 12:2e8fda56093f 870
jreiss 12:2e8fda56093f 871 uint32_t secs_since_first_attempt = (now - _settings.Session.JoinFirstAttempt);
jreiss 12:2e8fda56093f 872 uint16_t hours_since_first_attempt = secs_since_first_attempt / (60 * 60);
jreiss 12:2e8fda56093f 873
jreiss 12:2e8fda56093f 874 static uint8_t join_cnt = 0;
jreiss 12:2e8fda56093f 875
jreiss 12:2e8fda56093f 876 join_cnt = ++join_cnt % 8;
jreiss 12:2e8fda56093f 877
jreiss 12:2e8fda56093f 878 if (_settings.Session.JoinFirstAttempt == 0) {
jreiss 12:2e8fda56093f 879 /* 1 % duty-cycle for first hour
jreiss 12:2e8fda56093f 880 * 0.1 % next 10 hours
jreiss 12:2e8fda56093f 881 * 0.01 % upto 24 hours */
jreiss 12:2e8fda56093f 882 _settings.Session.JoinFirstAttempt = now;
jreiss 12:2e8fda56093f 883 _settings.Session.JoinTimeOnAir += GetTimeOnAir(size);
jreiss 12:2e8fda56093f 884 _settings.Session.JoinTimeOffEnd = now + (GetTimeOnAir(size) / 10);
jreiss 12:2e8fda56093f 885 } else if (join_cnt == 0) {
jreiss 12:2e8fda56093f 886 if (hours_since_first_attempt < 1) {
jreiss 12:2e8fda56093f 887 time_on_max = 36000;
jreiss 12:2e8fda56093f 888 rand_time_off = rand_r(time_off_max - 1, time_off_max + 1);
jreiss 12:2e8fda56093f 889 // time off max 1 hour
jreiss 12:2e8fda56093f 890 time_off_max = std::min < uint32_t > (time_off_max * 2, 60 * 60);
jreiss 12:2e8fda56093f 891
jreiss 12:2e8fda56093f 892 if (_settings.Session.JoinTimeOnAir < time_on_max) {
jreiss 12:2e8fda56093f 893 _settings.Session.JoinTimeOnAir += GetTimeOnAir(size);
jreiss 12:2e8fda56093f 894 _settings.Session.JoinTimeOffEnd = now + rand_time_off;
jreiss 12:2e8fda56093f 895 } else {
jreiss 12:2e8fda56093f 896 logWarning("Max time-on-air limit met for current join backoff period");
jreiss 12:2e8fda56093f 897 _settings.Session.JoinTimeOffEnd = _settings.Session.JoinFirstAttempt + 60 * 60;
jreiss 12:2e8fda56093f 898 }
jreiss 12:2e8fda56093f 899 } else if (hours_since_first_attempt < 11) {
jreiss 12:2e8fda56093f 900 if (_settings.Session.JoinTimeOnAir < 36000) {
jreiss 12:2e8fda56093f 901 _settings.Session.JoinTimeOnAir = 36000;
jreiss 12:2e8fda56093f 902 }
jreiss 12:2e8fda56093f 903 time_on_max = 72000;
jreiss 12:2e8fda56093f 904 rand_time_off = rand_r(time_off_max - 1, time_off_max + 1);
jreiss 12:2e8fda56093f 905 // time off max 1 hour
jreiss 12:2e8fda56093f 906 time_off_max = std::min < uint32_t > (time_off_max * 2, 60 * 60);
jreiss 12:2e8fda56093f 907
jreiss 12:2e8fda56093f 908 if (_settings.Session.JoinTimeOnAir < time_on_max) {
jreiss 12:2e8fda56093f 909 _settings.Session.JoinTimeOnAir += GetTimeOnAir(size);
jreiss 12:2e8fda56093f 910 _settings.Session.JoinTimeOffEnd = now + rand_time_off;
jreiss 12:2e8fda56093f 911 } else {
jreiss 12:2e8fda56093f 912 logWarning("Max time-on-air limit met for current join backoff period");
jreiss 12:2e8fda56093f 913 _settings.Session.JoinTimeOffEnd = _settings.Session.JoinFirstAttempt + 11 * 60 * 60;
jreiss 12:2e8fda56093f 914 }
jreiss 12:2e8fda56093f 915 } else {
jreiss 12:2e8fda56093f 916 if (_settings.Session.JoinTimeOnAir < 72000) {
jreiss 12:2e8fda56093f 917 _settings.Session.JoinTimeOnAir = 72000;
jreiss 12:2e8fda56093f 918 }
jreiss 12:2e8fda56093f 919 uint32_t join_time = 2500;
jreiss 12:2e8fda56093f 920
jreiss 12:2e8fda56093f 921 time_on_max = 80700;
jreiss 12:2e8fda56093f 922 time_off_max = 1 * 60 * 60; // 1 hour
jreiss 12:2e8fda56093f 923 rand_time_off = rand_r(time_off_max - 1, time_off_max + 1);
jreiss 12:2e8fda56093f 924
jreiss 12:2e8fda56093f 925 if (_settings.Session.JoinTimeOnAir < time_on_max - join_time) {
jreiss 12:2e8fda56093f 926 _settings.Session.JoinTimeOnAir += GetTimeOnAir(size);
jreiss 12:2e8fda56093f 927 _settings.Session.JoinTimeOffEnd = now + rand_time_off;
jreiss 12:2e8fda56093f 928 } else {
jreiss 12:2e8fda56093f 929 logWarning("Max time-on-air limit met for current join backoff period");
jreiss 12:2e8fda56093f 930 // Reset the join time on air and set end of restriction to the next 24 hour period
jreiss 12:2e8fda56093f 931 _settings.Session.JoinTimeOnAir = 72000;
jreiss 12:2e8fda56093f 932 uint16_t days = (now - _settings.Session.JoinFirstAttempt) / (24 * 60 * 60) + 1;
jreiss 12:2e8fda56093f 933 logWarning("days : %d", days);
jreiss 12:2e8fda56093f 934 _settings.Session.JoinTimeOffEnd = _settings.Session.JoinFirstAttempt + ((days * 24) + 11) * 60 * 60;
jreiss 12:2e8fda56093f 935 }
jreiss 12:2e8fda56093f 936 }
jreiss 12:2e8fda56093f 937
jreiss 12:2e8fda56093f 938 logWarning("JoinBackoff: %lu seconds Time On Air: %lu / %lu", _settings.Session.JoinTimeOffEnd - now, _settings.Session.JoinTimeOnAir, time_on_max);
jreiss 12:2e8fda56093f 939 } else {
jreiss 12:2e8fda56093f 940 _settings.Session.JoinTimeOnAir += GetTimeOnAir(size);
jreiss 12:2e8fda56093f 941 _settings.Session.JoinTimeOffEnd = now + (GetTimeOnAir(size) / 10);
jreiss 12:2e8fda56093f 942 }
jreiss 12:2e8fda56093f 943
jreiss 12:2e8fda56093f 944 return LORA_OK;
jreiss 12:2e8fda56093f 945 }
jreiss 12:2e8fda56093f 946
jreiss 12:2e8fda56093f 947
jreiss 12:2e8fda56093f 948 uint8_t CustomChannelPlan_KR920::HandleMacCommands(uint8_t* payload, uint8_t index, uint8_t end_index) {
jreiss 12:2e8fda56093f 949
jreiss 12:2e8fda56093f 950 return LORA_OK;
jreiss 12:2e8fda56093f 951 }
jreiss 12:2e8fda56093f 952