Bleeding edge development version of the xDot library for mbed 5. This version of the library is not guaranteed to be stable or well tested and should not be used in production or deployment scenarios.

Dependents:   Dot-Examples Dot-AT-Firmware Dot-Examples TEST_FF1705 ... more

Embed: (wiki syntax)

« Back to documentation index

Show/hide line numbers ChannelPlan_IN865.cpp Source File

ChannelPlan_IN865.cpp

00001 /**********************************************************************
00002 * COPYRIGHT 2016 MULTI-TECH SYSTEMS, INC.
00003 *
00004 * ALL RIGHTS RESERVED BY AND FOR THE EXCLUSIVE BENEFIT OF
00005 * MULTI-TECH SYSTEMS, INC.
00006 *
00007 * MULTI-TECH SYSTEMS, INC. - CONFIDENTIAL AND PROPRIETARY
00008 * INFORMATION AND/OR TRADE SECRET.
00009 *
00010 * NOTICE: ALL CODE, PROGRAM, INFORMATION, SCRIPT, INSTRUCTION,
00011 * DATA, AND COMMENT HEREIN IS AND SHALL REMAIN THE CONFIDENTIAL
00012 * INFORMATION AND PROPERTY OF MULTI-TECH SYSTEMS, INC.
00013 * USE AND DISCLOSURE THEREOF, EXCEPT AS STRICTLY AUTHORIZED IN A
00014 * WRITTEN AGREEMENT SIGNED BY MULTI-TECH SYSTEMS, INC. IS PROHIBITED.
00015 *
00016 ***********************************************************************/
00017 
00018 #include "ChannelPlan_IN865.h"
00019 #include "limits.h"
00020 
00021 using namespace lora;
00022 
00023 const uint8_t ChannelPlan_IN865::IN865_TX_POWERS[] = { 30, 28, 26, 24, 22, 20, 18, 16, 14, 12, 10 };
00024 const uint8_t ChannelPlan_IN865::IN865_RADIO_POWERS[] = { 3, 3, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 18, 19, 20 };
00025 const uint8_t ChannelPlan_IN865::IN865_MAX_PAYLOAD_SIZE[] = { 51, 51, 51, 115, 242, 242, 242, 242, 0, 0, 0, 0, 0, 0, 0, 0 };
00026 const uint8_t ChannelPlan_IN865::IN865_MAX_PAYLOAD_SIZE_REPEATER[] = { 51, 51, 51, 115, 222, 222, 222, 222, 0, 0, 0, 0, 0, 0, 0, 0 };
00027 
00028 ChannelPlan_IN865::ChannelPlan_IN865()
00029 :
00030     ChannelPlan(NULL, NULL)
00031 {
00032 
00033 }
00034 
00035 ChannelPlan_IN865::ChannelPlan_IN865(Settings* settings)
00036 :
00037     ChannelPlan(NULL, settings)
00038 {
00039 
00040 }
00041 
00042 ChannelPlan_IN865::ChannelPlan_IN865(SxRadio* radio, Settings* settings)
00043 :
00044     ChannelPlan(radio, settings)
00045 {
00046 
00047 }
00048 
00049 ChannelPlan_IN865::~ChannelPlan_IN865() {
00050 
00051 }
00052 
00053 void ChannelPlan_IN865::Init() {
00054 
00055     _datarates.clear();
00056     _channels.clear();
00057     _dutyBands.clear();
00058 
00059     DutyBand band;
00060 
00061     band.Index = 0;
00062     band.DutyCycle = 0;
00063 
00064     Datarate dr;
00065 
00066     _plan = IN865;
00067     _planName = "IN865";
00068     _maxTxPower = 30;
00069     _minTxPower = 0;
00070 
00071     _minFrequency = 865000000;
00072     _maxFrequency = 867000000;
00073 
00074     TX_POWERS = IN865_TX_POWERS;
00075     RADIO_POWERS = IN865_RADIO_POWERS;
00076     MAX_PAYLOAD_SIZE = IN865_MAX_PAYLOAD_SIZE;
00077     MAX_PAYLOAD_SIZE_REPEATER = IN865_MAX_PAYLOAD_SIZE_REPEATER;
00078 
00079     _minDatarate = 0;
00080     _maxDatarate = 7;
00081 
00082     _minRx2Datarate = DR_0;
00083     _maxRx2Datarate = DR_7;
00084 
00085     _minDatarateOffset = 0;
00086     _maxDatarateOffset = 7;
00087 
00088     _numChans125k = 16;
00089     _numChans500k = 0;
00090     _numDefaultChans = IN865_DEFAULT_NUM_CHANS; 
00091 
00092     GetSettings()->Session.Rx2Frequency = 866550000;
00093     GetSettings()->Session.Rx2DatarateIndex = DR_2;
00094 
00095     GetSettings()->Session.BeaconFrequency = IN865_BEACON_FREQ;
00096     GetSettings()->Session.BeaconFreqHop = false;
00097     GetSettings()->Session.PingSlotFrequency = IN865_BEACON_FREQ;
00098     GetSettings()->Session.PingSlotDatarateIndex = IN865_BEACON_DR;
00099     GetSettings()->Session.PingSlotFreqHop = false;
00100 
00101     logInfo("Initialize datarates...");
00102 
00103     dr.SpreadingFactor = SF_12;
00104 
00105     // Add DR0-5
00106     while (dr.SpreadingFactor >= SF_7) {
00107         AddDatarate(-1, dr);
00108         dr.SpreadingFactor--;
00109         dr.Index++;
00110     }
00111 
00112     // Skip DR6
00113     AddDatarate(-1, dr);
00114     dr.Index++;
00115 
00116     // Add DR7
00117     dr.SpreadingFactor = SF_FSK;
00118     dr.Bandwidth = BW_FSK;
00119     dr.PreambleLength = 10;
00120     dr.Coderate = 0;
00121     AddDatarate(-1, dr);
00122     dr.Index++;
00123 
00124     _maxDatarate = DR_7;
00125 
00126     // Skip DR8-15 RFU
00127     dr.SpreadingFactor = SF_INVALID;
00128     while (dr.Index++ <= DR_15) {
00129         AddDatarate(-1, dr);
00130     }
00131 
00132     GetSettings()->Session.TxDatarate = 0;
00133 
00134     logInfo("Initialize channels...");
00135 
00136     Channel chan;
00137     chan.DrRange.Fields.Min = DR_0;
00138     chan.DrRange.Fields.Max = DR_5;
00139     chan.Index = 0;
00140     chan.Frequency = 865062500;
00141     SetNumberOfChannels(16);
00142 
00143     uint8_t numDefaultChannels = IN865_DEFAULT_NUM_CHANS;
00144 
00145     AddChannel(0, chan);
00146 
00147     chan.Index++;
00148     chan.Frequency = 865402500;
00149     AddChannel(1, chan);
00150 
00151     chan.Index++;
00152     chan.Frequency = 865985000;
00153     AddChannel(2, chan);
00154 
00155 
00156     chan.DrRange.Value = 0;
00157     chan.Frequency = 0;
00158 
00159     for (uint8_t i = numDefaultChannels; i < 16; i++) {
00160         AddChannel(i, chan);
00161         chan.Index++;
00162     }
00163 
00164     // Add downlink channel defaults
00165     chan.Index = 0;
00166     _dlChannels.resize(16);
00167     for (uint8_t i = 0; i < 16; i++) {
00168         AddDownlinkChannel(i, chan);
00169         chan.Index++;
00170     }
00171 
00172     SetChannelMask(0, 0x07);
00173 
00174     band.Index = 0;
00175     band.FrequencyMin = _minFrequency;
00176     band.FrequencyMax = _maxFrequency;
00177     band.PowerMax = 30;
00178     band.TimeOffEnd = 0;
00179 
00180     // Disable duty-cycle limits
00181     band.DutyCycle = 0;
00182 
00183     AddDutyBand(-1, band);
00184 
00185     GetSettings()->Session.TxPower = GetSettings()->Network.TxPower;
00186 }
00187 
00188 uint8_t ChannelPlan_IN865::AddChannel(int8_t index, Channel channel) {
00189     logTrace("Add Channel %d : %lu : %02x %d", index, channel.Frequency, channel.DrRange.Value, _channels.size());
00190 
00191     assert(index < (int) _channels.size());
00192 
00193     if (index >= 0) {
00194         _channels[index] = channel;
00195     } else {
00196         _channels.push_back(channel);
00197     }
00198 
00199     return LORA_OK;
00200 }
00201 
00202 uint8_t ChannelPlan_IN865::HandleJoinAccept(const uint8_t* buffer, uint8_t size) {
00203 
00204     if (size == 33) {
00205         Channel ch;
00206         int index = 3;
00207         for (int i = 13; i < size - 5; i += 3) {
00208 
00209             ch.Frequency = ((buffer[i]) | (buffer[i + 1] << 8) | (buffer[i + 2] << 16)) * 100u;
00210 
00211             if (ch.Frequency > 0) {
00212                 ch.Index = index;
00213                 ch.DrRange.Fields.Min = static_cast<int8_t>(DR_0);
00214                 ch.DrRange.Fields.Max = static_cast<int8_t>(DR_5);
00215                 AddChannel(index, ch);
00216 
00217                 if (GetDutyBand(ch.Frequency) > -1)
00218                     _channelMask[0] |= (1 << index);
00219                 else
00220                     _channelMask[0] |= ~(1 << index);
00221 
00222                 index += 1;
00223             }
00224         }
00225     }
00226 
00227     return LORA_OK;
00228 }
00229 
00230 uint8_t ChannelPlan_IN865::SetTxConfig() {
00231 
00232     logInfo("Configure radio for TX");
00233 
00234     uint8_t band = GetDutyBand(GetChannel(_txChannel).Frequency);
00235     Datarate txDr = GetDatarate(GetSettings()->Session.TxDatarate);
00236     int8_t max_pwr = _dutyBands[band].PowerMax;
00237 
00238     int8_t pwr = 0;
00239 
00240     pwr = std::min < int8_t > (GetSettings()->Session.TxPower, max_pwr);
00241     pwr -= GetSettings()->Network.AntennaGain;
00242 
00243     for (int i = 20; i >= 0; i--) {
00244         if (RADIO_POWERS[i] <= pwr) {
00245             pwr = i;
00246             break;
00247         }
00248         if (i == 0) {
00249             pwr = i;
00250         }
00251     }
00252 
00253     logDebug("Session pwr: %d ant: %d max: %d", GetSettings()->Session.TxPower, GetSettings()->Network.AntennaGain, max_pwr);
00254     logDebug("Radio Power index: %d output: %d total: %d", pwr, RADIO_POWERS[pwr], RADIO_POWERS[pwr] + GetSettings()->Network.AntennaGain);
00255 
00256     uint32_t bw = txDr.Bandwidth;
00257     uint32_t sf = txDr.SpreadingFactor;
00258     uint8_t cr = txDr.Coderate;
00259     uint8_t pl = txDr.PreambleLength;
00260     uint16_t fdev = 0;
00261     bool crc = txDr.Crc;
00262     bool iq = txDr.TxIQ;
00263 
00264     if (GetSettings()->Network.DisableCRC == true)
00265         crc = false;
00266 
00267     SxRadio::RadioModems_t  modem = SxRadio::MODEM_LORA;
00268 
00269     if (sf == SF_FSK) {
00270         modem = SxRadio::MODEM_FSK;
00271         sf = 50e3;
00272         fdev = 25e3;
00273         bw = 0;
00274     }
00275 
00276     GetRadio()->SetTxConfig(modem, pwr, fdev, bw, sf, cr, pl, false, crc, false, 0, iq, 3e3);
00277 
00278     logDebug("TX PWR: %u DR: %u SF: %u BW: %u CR: %u PL: %u CRC: %d IQ: %d", pwr, txDr.Index, sf, bw, cr, pl, crc, iq);
00279 
00280     return LORA_OK;
00281 }
00282 
00283 
00284 uint8_t ChannelPlan_IN865::SetRxConfig(uint8_t window, bool continuous, uint16_t wnd_growth) {
00285 
00286     RxWindow rxw = GetRxWindow(window);
00287 
00288     if (_dlChannels[_txChannel].Frequency != 0  && window == 1)
00289         GetRadio()->SetChannel(_dlChannels[_txChannel].Frequency);
00290     else
00291         GetRadio()->SetChannel(rxw.Frequency);
00292 
00293     Datarate rxDr = GetDatarate(rxw.DatarateIndex);
00294     uint32_t bw = rxDr.Bandwidth;
00295     uint32_t sf = rxDr.SpreadingFactor;
00296     uint8_t cr = rxDr.Coderate;
00297     uint8_t pl = rxDr.PreambleLength;
00298     uint16_t sto = rxDr.SymbolTimeout() * wnd_growth;
00299     uint32_t afc = 0;
00300     bool fixLen = false;
00301     uint8_t payloadLen = 0U;
00302     bool crc = false; // downlink does not use CRC according to LORAWAN
00303 
00304     if (GetSettings()->Network.DisableCRC == true)
00305         crc = false;
00306 
00307     Datarate txDr = GetDatarate(GetSettings()->Session.TxDatarate);
00308     bool iq = txDr.RxIQ;
00309 
00310     if (P2PEnabled()) {
00311         iq = txDr.TxIQ;
00312     }
00313 
00314     // Beacon modifications - no I/Q inversion, fixed length rx, preamble
00315     if (window == RX_BEACON) {
00316         iq = txDr.TxIQ;
00317         fixLen = true;
00318         payloadLen = sizeof(BCNPayload);
00319         pl = BEACON_PREAMBLE_LENGTH;
00320     }
00321 
00322     SxRadio::RadioModems_t  modem = SxRadio::MODEM_LORA;
00323 
00324     if (sf == SF_FSK) {
00325         modem = SxRadio::MODEM_FSK;
00326         sf = 50e3;
00327         cr = 0;
00328         bw = 50e3;
00329         afc = 83333;
00330         iq = false;
00331         crc = true;  // FSK must use CRC
00332     }
00333 
00334     // Disable printf's to actually receive packets, printing to debug may mess up the timing
00335     // logTrace("Configure radio for RX%d on freq: %lu", window, rxw.Frequency);
00336     // logTrace("RX SF: %u BW: %u CR: %u PL: %u STO: %u CRC: %d IQ: %d", sf, bw, cr, pl, sto, crc, iq);
00337 
00338     GetRadio()->SetRxConfig(modem, bw, sf, cr, afc, pl, sto, fixLen, payloadLen, crc, false, 0, iq, continuous);
00339 
00340     return LORA_OK;
00341 }
00342 
00343 Channel ChannelPlan_IN865::GetChannel(int8_t index) {
00344     Channel chan;
00345     memset(&chan, 0, sizeof(Channel));
00346 
00347     chan = _channels[index];
00348 
00349     return chan;
00350 }
00351 
00352 uint8_t ChannelPlan_IN865::SetFrequencySubBand(uint8_t sub_band) {
00353     return LORA_OK;
00354 }
00355 
00356 void ChannelPlan_IN865::LogRxWindow(uint8_t wnd) {
00357 
00358     RxWindow rxw = GetRxWindow(wnd);
00359     Datarate rxDr = GetDatarate(rxw.DatarateIndex);
00360     uint8_t bw = rxDr.Bandwidth;
00361     uint8_t sf = rxDr.SpreadingFactor;
00362     uint8_t cr = rxDr.Coderate;
00363     uint8_t pl = rxDr.PreambleLength;
00364     uint16_t sto = rxDr.SymbolTimeout();
00365     bool crc = false; // downlink does not use CRC according to LORAWAN
00366     bool iq = GetTxDatarate().RxIQ;
00367     uint32_t freq = rxw.Frequency;
00368 
00369     if (wnd == 1 && _dlChannels[_txChannel].Frequency != 0)
00370         freq = _dlChannels[_txChannel].Frequency;
00371 
00372     logTrace("RX%d on freq: %lu", wnd, freq);
00373     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);
00374 }
00375 
00376 RxWindow ChannelPlan_IN865::GetRxWindow(uint8_t window) {
00377     RxWindow rxw;
00378     int index = 0;
00379 
00380     if (P2PEnabled()) {
00381         rxw.Frequency = GetSettings()->Network.TxFrequency;
00382         index = GetSettings()->Session.TxDatarate;
00383     } else {
00384         switch (window) {
00385         case RX_1:
00386             // Use same frequency as TX
00387             rxw.Frequency = _channels[_txChannel].Frequency;
00388 
00389             if (GetSettings()->Session.Rx1DatarateOffset >= 6) {
00390                 index =  GetSettings()->Session.TxDatarate + (GetSettings()->Session.Rx1DatarateOffset == 6 ? 1 : 2);
00391                 index = std::min<int>(index, _maxDatarate);
00392             } else if (GetSettings()->Session.TxDatarate > GetSettings()->Session.Rx1DatarateOffset) {
00393                 index = GetSettings()->Session.TxDatarate - GetSettings()->Session.Rx1DatarateOffset;
00394             } else {
00395                 index = 0;
00396             }
00397 
00398             break;
00399 
00400         case RX_BEACON:
00401             rxw.Frequency = GetSettings()->Session.BeaconFrequency;
00402             index = IN865_BEACON_DR;
00403             break;
00404 
00405         case RX_SLOT:
00406             rxw.Frequency = GetSettings()->Session.PingSlotFrequency;
00407             index = GetSettings()->Session.PingSlotDatarateIndex;
00408             break;
00409 
00410         // RX2, RXC, RX_TEST, etc..
00411         default:
00412             rxw.Frequency = GetSettings()->Session.Rx2Frequency;
00413             index = GetSettings()->Session.Rx2DatarateIndex;
00414         }
00415     }
00416 
00417     if (index == DR_6)
00418         index = DR_5;
00419 
00420     rxw.DatarateIndex = index;
00421 
00422     return rxw;
00423 }
00424 
00425 uint8_t ChannelPlan_IN865::HandleRxParamSetup(const uint8_t* payload, uint8_t index, uint8_t size, uint8_t& status) {
00426     status = 0x07;
00427     int8_t datarate = 0;
00428     int8_t drOffset = 0;
00429     uint32_t freq = 0;
00430 
00431     drOffset = payload[index++];
00432     datarate = drOffset & 0x0F;
00433     drOffset = (drOffset >> 4) & 0x07;
00434 
00435     freq = payload[index++];
00436     freq |= payload[index++] << 8;
00437     freq |= payload[index++] << 16;
00438     freq *= 100;
00439 
00440     if (!CheckRfFrequency(freq)) {
00441         logInfo("Freq KO");
00442         status &= 0xFE; // Channel frequency KO
00443     }
00444 
00445     if (datarate < _minRx2Datarate || datarate > _maxRx2Datarate || datarate == DR_6) {
00446         logInfo("DR KO");
00447         status &= 0xFD; // Datarate KO
00448     }
00449 
00450     if (drOffset < 0 || drOffset > _maxDatarateOffset) {
00451         logInfo("DR Offset KO");
00452         status &= 0xFB; // Rx1DrOffset range KO
00453     }
00454 
00455     if ((status & 0x07) == 0x07) {
00456         logInfo("RxParamSetup accepted Rx2DR: %d Rx2Freq: %d Rx1Offset: %d", datarate, freq, drOffset);
00457         SetRx2DatarateIndex(datarate);
00458         SetRx2Frequency(freq);
00459         SetRx1Offset(drOffset);
00460     } else {
00461         logInfo("RxParamSetup rejected Rx2DR: %d Rx2Freq: %d Rx1Offset: %d", datarate, freq, drOffset);
00462     }
00463 
00464     return LORA_OK;
00465 }
00466 
00467 uint8_t ChannelPlan_IN865::HandleNewChannel(const uint8_t* payload, uint8_t index, uint8_t size, uint8_t& status) {
00468 
00469     status = 0x03;
00470     uint8_t channelIndex = 0;
00471     Channel chParam;
00472 
00473     channelIndex = payload[index++];
00474     lora::CopyFreqtoInt(payload + index, chParam.Frequency);
00475     index += 3;
00476     chParam.DrRange.Value = payload[index++];
00477 
00478     if (channelIndex < 3 || channelIndex > _channels.size() - 1) {
00479         logError("New Channel index KO");
00480         status &= 0xFE; // Channel index KO
00481     }
00482 
00483     if (chParam.Frequency == 0) {
00484         chParam.DrRange.Value = 0;
00485     } else if (chParam.Frequency < _minFrequency || chParam.Frequency > _maxFrequency) {
00486         logError("New Channel frequency KO");
00487         status &= 0xFE; // Channel frequency KO
00488     }
00489 
00490     if (chParam.DrRange.Fields.Min > chParam.DrRange.Fields.Max && chParam.Frequency != 0) {
00491         logError("New Channel datarate min/max KO");
00492         status &= 0xFD; // Datarate range KO
00493     } else if ((chParam.DrRange.Fields.Min < _minDatarate || chParam.DrRange.Fields.Min > _maxDatarate) &&
00494                chParam.Frequency != 0) {
00495         logError("New Channel datarate min KO");
00496         status &= 0xFD; // Datarate range KO
00497     } else if ((chParam.DrRange.Fields.Max < _minDatarate || chParam.DrRange.Fields.Max > _maxDatarate) &&
00498                chParam.Frequency != 0) {
00499         logError("New Channel datarate max KO");
00500         status &= 0xFD; // Datarate range KO
00501     }
00502 
00503     if ((status & 0x03) == 0x03) {
00504         logInfo("New Channel accepted index: %d freq: %lu drRange: %02x", channelIndex, chParam.Frequency, chParam.DrRange.Value);
00505         AddChannel(channelIndex, chParam);
00506         SetChannelMask(0, _channelMask[0] | 1 << (channelIndex));
00507     }
00508 
00509     return LORA_OK;
00510 }
00511 
00512 uint8_t ChannelPlan_IN865::HandlePingSlotChannelReq(const uint8_t* payload, uint8_t index, uint8_t size, uint8_t& status) {
00513     uint8_t datarate = 0;
00514     uint32_t freq = 0;
00515 
00516     status = 0x03;
00517 
00518     freq = payload[index++];
00519     freq |= payload[index++] << 8;
00520     freq |= payload[index++] << 16;
00521     freq *= 100;
00522 
00523     datarate = payload[index] & 0x0F;
00524 
00525     if (freq == 0U) {
00526         logInfo("Received request to reset ping slot frequency to default");
00527         freq = IN865_BEACON_FREQ;
00528     } else if (!CheckRfFrequency(freq)) {
00529         logInfo("Freq KO");
00530         status &= 0xFE; // Channel frequency KO
00531     }
00532 
00533     if (datarate < _minRx2Datarate || datarate > _maxRx2Datarate) {
00534         logInfo("DR KO");
00535         status &= 0xFD; // Datarate KO
00536     }
00537 
00538     if ((status & 0x03) == 0x03) {
00539         logInfo("PingSlotChannelReq accepted DR: %d Freq: %d", datarate, freq);
00540         GetSettings()->Session.PingSlotFrequency = freq;
00541         GetSettings()->Session.PingSlotDatarateIndex = datarate;
00542     } else {
00543         logInfo("PingSlotChannelReq rejected DR: %d Freq: %d", datarate, freq);
00544     }
00545 
00546     return LORA_OK;
00547 }
00548 
00549 uint8_t ChannelPlan_IN865::HandleBeaconFrequencyReq(const uint8_t* payload, uint8_t index, uint8_t size, uint8_t& status) {
00550     uint32_t freq = 0;
00551 
00552     status = 0x01;
00553 
00554     freq = payload[index++];
00555     freq |= payload[index++] << 8;
00556     freq |= payload[index] << 16;
00557     freq *= 100;
00558 
00559     if (freq == 0U) {
00560         logInfo("Received request to reset beacon frequency to default");
00561         freq = IN865_BEACON_FREQ;
00562     } else if (!CheckRfFrequency(freq)) {
00563         logInfo("Freq KO");
00564         status &= 0xFE; // Channel frequency KO
00565     }
00566 
00567     if (status & 0x01) {
00568         logInfo("BeaconFrequencyReq accepted Freq: %d", freq);
00569         GetSettings()->Session.BeaconFrequency = freq;
00570     } else {
00571         logInfo("BeaconFrequencyReq rejected Freq: %d", freq);
00572     }
00573 
00574     return LORA_OK;
00575 }
00576 
00577 uint8_t ChannelPlan_IN865::HandleAdrCommand(const uint8_t* payload, uint8_t index, uint8_t size, uint8_t& status) {
00578 
00579     uint8_t power = 0;
00580     uint8_t datarate = 0;
00581     uint16_t mask = 0;
00582     uint16_t new_mask = 0;
00583     uint8_t ctrl = 0;
00584     uint8_t nbRep = 0;
00585 
00586     status = 0x07;
00587     datarate = payload[index++];
00588     power = datarate & 0x0F;
00589     datarate = (datarate >> 4) & 0x0F;
00590 
00591     mask = payload[index++];
00592     mask |= payload[index++] << 8;
00593 
00594     nbRep = payload[index++];
00595     ctrl = (nbRep >> 4) & 0x07;
00596     nbRep &= 0x0F;
00597 
00598     if (nbRep == 0) {
00599         nbRep = 1;
00600     }
00601 
00602     if (datarate > _maxDatarate || datarate == DR_6) {
00603         status &= 0xFD; // Datarate KO
00604     }
00605 
00606     //
00607     // Remark MaxTxPower = 0 and MinTxPower = 10
00608     //
00609     if (power > 10) {
00610         status &= 0xFB; // TxPower KO
00611     }
00612 
00613     switch (ctrl) {
00614         case 0:
00615             SetChannelMask(0, mask);
00616             break;
00617 
00618         case 6:
00619             // enable all currently defined channels
00620             // set bits 0 - N of a number by (2<<N)-1
00621             new_mask = (1 << _channels.size()) - 1;
00622             SetChannelMask(0, new_mask);
00623             break;
00624 
00625         default:
00626             logWarning("rejecting RFU or unknown control value %d", ctrl);
00627             status &= 0xFE; // ChannelMask KO
00628             return LORA_ERROR;
00629     }
00630 
00631     if (GetSettings()->Network.ADREnabled) {
00632         if (status == 0x07) {
00633             GetSettings()->Session.TxDatarate = datarate;
00634             GetSettings()->Session.TxPower = TX_POWERS[power];
00635             GetSettings()->Session.Redundancy = nbRep;
00636         }
00637     } else {
00638         logDebug("ADR is disabled, DR and Power not changed.");
00639         status &= 0xFB; // TxPower KO
00640         status &= 0xFD; // Datarate KO
00641     }
00642 
00643     logDebug("ADR DR: %u PWR: %u Ctrl: %02x Mask: %04x NbRep: %u Stat: %02x", datarate, power, ctrl, mask, nbRep, status);
00644 
00645     return LORA_OK;
00646 }
00647 
00648 uint8_t ChannelPlan_IN865::ValidateAdrConfiguration() {
00649     uint8_t status = 0x07;
00650     uint8_t datarate = GetSettings()->Session.TxDatarate;
00651     uint8_t power = GetSettings()->Session.TxPower;
00652 
00653     if (!GetSettings()->Network.ADREnabled) {
00654         logDebug("ADR disabled - no applied changes to validate");
00655         return status;
00656     }
00657 
00658     if (datarate > _maxDatarate || datarate == DR_6) {
00659         logWarning("ADR Datarate KO - outside allowed range");
00660         status &= 0xFD; // Datarate KO
00661     }
00662     if (power < _minTxPower || power > _maxTxPower) {
00663         logWarning("ADR TX Power KO - outside allowed range");
00664         status &= 0xFB; // TxPower KO
00665     }
00666 
00667     // mask must not contain any undefined channels
00668     for (int i = 3; i < 16; i++) {
00669         if ((_channelMask[0] & (1 << i)) && (_channels[i].Frequency == 0)) {
00670             logWarning("ADR Channel Mask KO - cannot enable undefined channel");
00671             status &= 0xFE; // ChannelMask KO
00672             break;
00673         }
00674     }
00675 
00676     return status;
00677 }
00678 
00679 uint8_t ChannelPlan_IN865::HandleAckTimeout() {
00680 
00681     if (!GetSettings()->Network.ADREnabled) {
00682         return LORA_ADR_OFF;
00683     }
00684 
00685     if ((++(GetSettings()->Session.AckCounter) % 2) == 0) {
00686         if (GetSettings()->Session.TxPower < GetSettings()->Network.TxPowerMax) {
00687             logTrace("ADR Setting power to maximum");
00688             GetSettings()->Session.TxPower = GetSettings()->Network.TxPowerMax;
00689         } else if (GetSettings()->Session.TxDatarate > 0) {
00690             logTrace("ADR Lowering datarate");
00691             DecrementDatarate();
00692         }
00693     }
00694 
00695     return LORA_OK;
00696 }
00697 
00698 
00699 uint32_t ChannelPlan_IN865::GetTimeOffAir()
00700 {
00701     if (GetSettings()->Test.DisableDutyCycle == lora::ON)
00702         return 0;
00703 
00704     uint32_t min = 0;
00705     uint32_t now = _dutyCycleTimer.read_ms();
00706 
00707 
00708     min = UINT_MAX;
00709     int8_t band = 0;
00710 
00711     if (P2PEnabled()) {
00712         int8_t band = GetDutyBand(GetSettings()->Network.TxFrequency);
00713         if (_dutyBands[band].TimeOffEnd > now) {
00714             min = _dutyBands[band].TimeOffEnd - now;
00715         } else {
00716             min = 0;
00717         }
00718     } else {
00719         for (size_t i = 0; i < _channels.size(); i++) {
00720             if (IsChannelEnabled(i) && GetChannel(i).Frequency != 0 &&
00721                 !(GetSettings()->Session.TxDatarate < GetChannel(i).DrRange.Fields.Min ||
00722                   GetSettings()->Session.TxDatarate > GetChannel(i).DrRange.Fields.Max)) {
00723 
00724                 band = GetDutyBand(GetChannel(i).Frequency);
00725                 if (band != -1) {
00726                     // logDebug("band: %d time-off: %d now: %d", band, _dutyBands[band].TimeOffEnd, now);
00727                     if (_dutyBands[band].TimeOffEnd > now) {
00728                         min = std::min < uint32_t > (min, _dutyBands[band].TimeOffEnd - now);
00729                     } else {
00730                         min = 0;
00731                         break;
00732                     }
00733                 }
00734             }
00735         }
00736     }
00737 
00738 
00739     if (GetSettings()->Session.AggregatedTimeOffEnd > 0 && GetSettings()->Session.AggregatedTimeOffEnd > now) {
00740         min = std::max < uint32_t > (min, GetSettings()->Session.AggregatedTimeOffEnd - now);
00741     }
00742 
00743     now = time(NULL);
00744     uint32_t join_time = 0;
00745 
00746     if (GetSettings()->Session.JoinFirstAttempt != 0 && now < GetSettings()->Session.JoinTimeOffEnd) {
00747         join_time = (GetSettings()->Session.JoinTimeOffEnd - now) * 1000;
00748     }
00749 
00750     min = std::max < uint32_t > (join_time, min);
00751 
00752     return min;
00753 }
00754 
00755 
00756 void ChannelPlan_IN865::UpdateDutyCycle(uint32_t freq, uint32_t time_on_air_ms) {
00757     if (GetSettings()->Test.DisableDutyCycle == lora::ON) {
00758         _dutyCycleTimer.stop();
00759         for (size_t i = 0; i < _dutyBands.size(); i++) {
00760             _dutyBands[i].TimeOffEnd = 0;
00761         }
00762         return;
00763     }
00764 
00765     _dutyCycleTimer.start();
00766 
00767     if (GetSettings()->Session.MaxDutyCycle > 0 && GetSettings()->Session.MaxDutyCycle <= 15) {
00768         GetSettings()->Session.AggregatedTimeOffEnd = _dutyCycleTimer.read_ms() + time_on_air_ms * GetSettings()->Session.AggregateDutyCycle;
00769         logDebug("Updated Aggregate DCycle Time-off: %lu DC: %f%%", GetSettings()->Session.AggregatedTimeOffEnd, 1 / float(GetSettings()->Session.AggregateDutyCycle));
00770     } else {
00771         GetSettings()->Session.AggregatedTimeOffEnd = 0;
00772     }
00773 
00774 
00775     uint32_t time_off_air = 0;
00776     uint32_t now = _dutyCycleTimer.read_ms();
00777 
00778     for (size_t i = 0; i < _dutyBands.size(); i++) {
00779         if (_dutyBands[i].TimeOffEnd < now) {
00780             _dutyBands[i].TimeOffEnd = 0;
00781         } else {
00782             _dutyBands[i].TimeOffEnd -= now;
00783         }
00784 
00785         if (freq >= _dutyBands[i].FrequencyMin && freq <= _dutyBands[i].FrequencyMax) {
00786             logDebug("update TOE: freq: %d i:%d toa: %d DC:%d", freq, i, time_on_air_ms, _dutyBands[i].DutyCycle);
00787 
00788             if (freq > _minFrequency && freq < _maxFrequency && (GetSettings()->Session.TxPower + GetSettings()->Network.AntennaGain) <= 7) {
00789                 _dutyBands[i].TimeOffEnd = 0;
00790             } else {
00791                 time_off_air = time_on_air_ms * _dutyBands[i].DutyCycle;
00792                 _dutyBands[i].TimeOffEnd = time_off_air;
00793             }
00794         }
00795     }
00796 
00797 
00798     ResetDutyCycleTimer();
00799 }
00800 
00801 std::vector<uint32_t> lora::ChannelPlan_IN865::GetChannels() {
00802     std::vector < uint32_t > chans;
00803 
00804     for (int8_t i = 0; i < (int) _channels.size(); i++) {
00805         chans.push_back(_channels[i].Frequency);
00806     }
00807     chans.push_back(GetRxWindow(2).Frequency);
00808 
00809     return chans;
00810 }
00811 
00812 std::vector<uint8_t> lora::ChannelPlan_IN865::GetChannelRanges() {
00813     std::vector < uint8_t > ranges;
00814 
00815     for (int8_t i = 0; i < (int) _channels.size(); i++) {
00816         ranges.push_back(_channels[i].DrRange.Value);
00817     }
00818 
00819     ranges.push_back(GetRxWindow(2).DatarateIndex);
00820 
00821     return ranges;
00822 
00823 }
00824 
00825 void lora::ChannelPlan_IN865::EnableDefaultChannels() {
00826     _channelMask[0] |= 0x0003;
00827 }
00828 
00829 uint8_t ChannelPlan_IN865::GetNextChannel()
00830 {
00831     if (GetSettings()->Session.AggregatedTimeOffEnd != 0) {
00832         return LORA_AGGREGATED_DUTY_CYCLE;
00833     }
00834 
00835     if (P2PEnabled() || GetSettings()->Network.TxFrequency != 0) {
00836         logDebug("Using frequency %d", GetSettings()->Network.TxFrequency);
00837 
00838         if (GetSettings()->Test.DisableDutyCycle != lora::ON) {
00839             int8_t band = GetDutyBand(GetSettings()->Network.TxFrequency);
00840             logDebug("band: %d freq: %d", band, GetSettings()->Network.TxFrequency);
00841             if (band != -1 && _dutyBands[band].TimeOffEnd != 0) {
00842                 return LORA_NO_CHANS_ENABLED;
00843             }
00844         }
00845 
00846         GetRadio()->SetChannel(GetSettings()->Network.TxFrequency);
00847         return LORA_OK;
00848     }
00849 
00850     uint8_t start = 0;
00851     uint8_t maxChannels = _numChans125k;
00852     uint8_t nbEnabledChannels = 0;
00853     uint8_t *enabledChannels = new uint8_t[maxChannels];
00854 
00855     if (GetTxDatarate().Bandwidth == BW_500) {
00856         maxChannels = _numChans500k;
00857         start = _numChans125k;
00858     }
00859 
00860 // Search how many channels are enabled
00861     DatarateRange range;
00862     uint8_t dr_index = GetSettings()->Session.TxDatarate;
00863     uint32_t now = _dutyCycleTimer.read_ms();
00864 
00865     for (size_t i = 0; i < _dutyBands.size(); i++) {
00866         if (_dutyBands[i].TimeOffEnd < now || GetSettings()->Test.DisableDutyCycle == lora::ON) {
00867             _dutyBands[i].TimeOffEnd = 0;
00868         }
00869     }
00870 
00871     for (uint8_t i = start; i < start + maxChannels; i++) {
00872         range = GetChannel(i).DrRange;
00873         // logDebug("chan: %d freq: %d range:%02x", i, GetChannel(i).Frequency, range.Value);
00874 
00875         if (IsChannelEnabled(i) && (dr_index >= range.Fields.Min && dr_index <= range.Fields.Max)) {
00876             int8_t band = GetDutyBand(GetChannel(i).Frequency);
00877             // logDebug("band: %d freq: %d", band, _channels[i].Frequency);
00878             if (band != -1 && _dutyBands[band].TimeOffEnd == 0) {
00879                 enabledChannels[nbEnabledChannels++] = i;
00880             }
00881         }
00882     }
00883 
00884     logTrace("Number of available channels: %d", nbEnabledChannels);
00885 
00886     uint32_t freq = 0;
00887     uint8_t sf = GetTxDatarate().SpreadingFactor;
00888     uint8_t bw = GetTxDatarate().Bandwidth;
00889     int16_t thres = DEFAULT_FREE_CHAN_RSSI_THRESHOLD;
00890 
00891     if (nbEnabledChannels == 0) {
00892         delete [] enabledChannels;
00893         return LORA_NO_CHANS_ENABLED;
00894     }
00895 
00896     if (GetSettings()->Network.CADEnabled) {
00897         // Search for free channel with ms timeout
00898         int16_t timeout = 10000;
00899         Timer tmr;
00900         tmr.start();
00901 
00902         for (uint8_t j = rand_r(0, nbEnabledChannels - 1); tmr.read_ms() < timeout; j++) {
00903             freq = GetChannel(enabledChannels[j]).Frequency;
00904 
00905             if (GetRadio()->IsChannelFree(SxRadio::MODEM_LORA, freq, sf, thres, bw)) {
00906                 _txChannel = enabledChannels[j];
00907                 break;
00908             }
00909         }
00910     } else {
00911         uint8_t j = rand_r(0, nbEnabledChannels - 1);
00912         _txChannel = enabledChannels[j];
00913         freq = GetChannel(_txChannel).Frequency;
00914     }
00915 
00916     assert(freq != 0);
00917 
00918     logDebug("Using channel %d : %d", _txChannel, freq);
00919     GetRadio()->SetChannel(freq);
00920 
00921     delete [] enabledChannels;
00922     return LORA_OK;
00923 }
00924 
00925 
00926 uint8_t lora::ChannelPlan_IN865::GetJoinDatarate() {
00927     uint8_t dr = GetSettings()->Session.TxDatarate;
00928     static uint8_t cnt = 0;
00929 
00930     if (GetSettings()->Test.DisableRandomJoinDatarate == lora::OFF) {
00931         if ((cnt++ % 20) == 0) {
00932             dr = lora::DR_0;
00933         } else if ((cnt % 16) == 0) {
00934             dr = lora::DR_1;
00935         } else if ((cnt % 12) == 0) {
00936             dr = lora::DR_2;
00937         } else if ((cnt % 8) == 0) {
00938             dr = lora::DR_3;
00939         } else if ((cnt % 4) == 0) {
00940             dr = lora::DR_4;
00941         } else {
00942             dr = lora::DR_5;
00943         }
00944     }
00945 
00946     return dr;
00947 }
00948 
00949 uint8_t ChannelPlan_IN865::CalculateJoinBackoff(uint8_t size) {
00950 
00951     time_t now = time(NULL);
00952     uint32_t time_on_max = 0;
00953     static uint32_t time_off_max = 15;
00954     uint32_t rand_time_off = 0;
00955 
00956     // TODO: calc time-off-max based on RTC time from JoinFirstAttempt, time-off-max is lost over sleep
00957 
00958     if ((time_t)GetSettings()->Session.JoinTimeOffEnd > now) {
00959         return LORA_JOIN_BACKOFF;
00960     }
00961 
00962     uint32_t secs_since_first_attempt = (now - GetSettings()->Session.JoinFirstAttempt);
00963     uint16_t hours_since_first_attempt = secs_since_first_attempt / (60 * 60);
00964 
00965     static uint8_t join_cnt = 0;
00966 
00967     join_cnt = (join_cnt+1) % 8;
00968 
00969     if (GetSettings()->Session.JoinFirstAttempt == 0) {
00970         /* 1 % duty-cycle for first hour
00971          * 0.1 % next 10 hours
00972          * 0.01 % upto 24 hours         */
00973         GetSettings()->Session.JoinFirstAttempt = now;
00974         GetSettings()->Session.JoinTimeOnAir += GetTimeOnAir(size);
00975         GetSettings()->Session.JoinTimeOffEnd = now + (GetTimeOnAir(size) / 10);
00976     } else if (join_cnt == 0) {
00977         if (hours_since_first_attempt < 1) {
00978             time_on_max = 36000;
00979             rand_time_off = rand_r(time_off_max - 1, time_off_max + 1);
00980             // time off max 1 hour
00981             time_off_max = std::min < uint32_t > (time_off_max * 2, 60 * 60);
00982 
00983             if (GetSettings()->Session.JoinTimeOnAir < time_on_max) {
00984                 GetSettings()->Session.JoinTimeOnAir += GetTimeOnAir(size);
00985                 GetSettings()->Session.JoinTimeOffEnd = now + rand_time_off;
00986             } else {
00987                 logWarning("Max time-on-air limit met for current join backoff period");
00988                 GetSettings()->Session.JoinTimeOffEnd = GetSettings()->Session.JoinFirstAttempt + 60 * 60;
00989             }
00990         } else if (hours_since_first_attempt < 11) {
00991             if (GetSettings()->Session.JoinTimeOnAir < 36000) {
00992                 GetSettings()->Session.JoinTimeOnAir = 36000;
00993             }
00994             time_on_max = 72000;
00995             rand_time_off = rand_r(time_off_max - 1, time_off_max + 1);
00996             // time off max 1 hour
00997             time_off_max = std::min < uint32_t > (time_off_max * 2, 60 * 60);
00998 
00999             if (GetSettings()->Session.JoinTimeOnAir < time_on_max) {
01000                 GetSettings()->Session.JoinTimeOnAir += GetTimeOnAir(size);
01001                 GetSettings()->Session.JoinTimeOffEnd = now + rand_time_off;
01002             } else {
01003                 logWarning("Max time-on-air limit met for current join backoff period");
01004                 GetSettings()->Session.JoinTimeOffEnd = GetSettings()->Session.JoinFirstAttempt + 11 * 60 * 60;
01005             }
01006         } else {
01007             if (GetSettings()->Session.JoinTimeOnAir < 72000) {
01008                 GetSettings()->Session.JoinTimeOnAir = 72000;
01009             }
01010             uint32_t join_time = 2500;
01011 
01012             time_on_max = 80700;
01013             time_off_max = 1 * 60 * 60; // 1 hour
01014             rand_time_off = rand_r(time_off_max - 1, time_off_max + 1);
01015 
01016             if (GetSettings()->Session.JoinTimeOnAir < time_on_max - join_time) {
01017                 GetSettings()->Session.JoinTimeOnAir += GetTimeOnAir(size);
01018                 GetSettings()->Session.JoinTimeOffEnd = now + rand_time_off;
01019             } else {
01020                 logWarning("Max time-on-air limit met for current join backoff period");
01021                 // Reset the join time on air and set end of restriction to the next 24 hour period
01022                 GetSettings()->Session.JoinTimeOnAir = 72000;
01023                 uint16_t days = (now - GetSettings()->Session.JoinFirstAttempt) / (24 * 60 * 60) + 1;
01024                 logWarning("days : %d", days);
01025                 GetSettings()->Session.JoinTimeOffEnd = GetSettings()->Session.JoinFirstAttempt + ((days * 24) + 11) * 60 * 60;
01026             }
01027         }
01028 
01029         logWarning("JoinBackoff: %lu seconds  Time On Air: %lu / %lu", GetSettings()->Session.JoinTimeOffEnd - now, GetSettings()->Session.JoinTimeOnAir, time_on_max);
01030     } else {
01031         GetSettings()->Session.JoinTimeOnAir += GetTimeOnAir(size);
01032         GetSettings()->Session.JoinTimeOffEnd = now + (GetTimeOnAir(size) / 10);
01033     }
01034 
01035     return LORA_OK;
01036 }
01037 
01038 uint8_t ChannelPlan_IN865::HandleMacCommand(uint8_t* payload, uint8_t& index) {
01039     return LORA_ERROR;
01040 }
01041 
01042 //in865 skips dr6 rfu
01043 
01044 void ChannelPlan_IN865::IncrementDatarate() {
01045     uint8_t dr = GetSettings()->Session.TxDatarate;
01046     if (dr < _maxDatarate)
01047         dr++;
01048     if(dr == DR_6)
01049         dr= DR_7;
01050     GetSettings()->Session.TxDatarate = dr;
01051 
01052 }
01053 
01054 void ChannelPlan_IN865::DecrementDatarate() {
01055     uint8_t dr = GetSettings()->Session.TxDatarate;
01056     if (dr > _minDatarate)
01057         dr--;
01058     if(dr == DR_6)
01059         dr= DR_5;
01060     GetSettings()->Session.TxDatarate = dr;
01061 }
01062 
01063 bool ChannelPlan_IN865::DecodeBeacon(const uint8_t* payload, size_t size, BeaconData_t& data) {
01064     uint16_t crc1, crc1_rx, crc2, crc2_rx;
01065     const BCNPayload* beacon = (const BCNPayload*)payload;
01066 
01067     // First check the size of the packet
01068     if (size != sizeof(BCNPayload))
01069         return false;
01070 
01071     // Next we verify the CRCs are correct
01072     crc1 = CRC16(beacon->RFU1, sizeof(beacon->RFU1) + sizeof(beacon->Time));
01073     memcpy((uint8_t*)&crc1_rx, beacon->CRC1, sizeof(uint16_t));
01074 
01075     if (crc1 != crc1_rx)
01076         return false;
01077 
01078     crc2 = CRC16(beacon->GwSpecific, sizeof(beacon->GwSpecific) + sizeof(beacon->RFU2));
01079     memcpy((uint8_t*)&crc2_rx, beacon->CRC2, sizeof(uint16_t));
01080 
01081     if (crc2 != crc2_rx)
01082         return false;
01083 
01084     // Now that we have confirmed this packet is a beacon, parse and complete the output struct
01085     memcpy(&data.Time, beacon->Time, sizeof(beacon->Time));
01086     data.InfoDesc = beacon->GwSpecific[0];
01087 
01088     // Update the GPS fields if we have a gps info descriptor
01089     if (data.InfoDesc == GPS_FIRST_ANTENNA ||
01090         data.InfoDesc == GPS_SECOND_ANTENNA ||
01091         data.InfoDesc == GPS_THIRD_ANTENNA) {
01092         // Latitude and Longitude 3 bytes in length
01093         memcpy(&data.Latitude, &beacon->GwSpecific[1], 3);
01094         memcpy(&data.Longitude, &beacon->GwSpecific[4], 3);
01095     }
01096 
01097     return true;
01098 }