static void OnRxWindow2TimerEvent( TimerHandle_t xTimer ) { uint16_t symbTimeout = 5; // DR_2, DR_1, DR_0 uint32_t bandwidth = 0; // LoRa 125 kHz TimerStop(&RxWindow2Timer); phyFlags.Bits.RxSlot = 2; if ( pLoRaDevice->ctrlFlags.Bits.ackRequested == 1 ) { // TimerSetValue(&AckTimeoutTimer, // ACK_TIMEOUT + randr(-ACK_TIMEOUT_RND, ACK_TIMEOUT_RND)); // TimerStart (&AckTimeoutTimer); } // For higher datarates, we increase the number of symbols generating a Rx Timeout if ( Rx2Dr >= DR_3 ) { // DR_6, DR_5, DR_4, DR_3 symbTimeout = 8; } if ( Rx2Dr == DR_6 ) { // LoRa 250 kHz bandwidth = 1; } if ( pLoRaDevice->devClass != CLASS_C ) { LOG_TRACE("Open single Rx window 2 at %u ms (Channel : %u / DR: %u).", (uint32_t)(TimerGetCurrentTime() * portTICK_PERIOD_MS), Rx2ChannelFrequency, Rx2Dr); OpenReceptionWindow(Rx2ChannelFrequency, Rx2Dr, bandwidth, symbTimeout, false); } else { LOG_TRACE("Open continuous Rx window 2 at %u ms (Channel : %u / DR: %u).", (uint32_t)(TimerGetCurrentTime() * portTICK_PERIOD_MS), Rx2ChannelFrequency, Rx2Dr); OpenReceptionWindow(Rx2ChannelFrequency, Rx2Dr, bandwidth, symbTimeout, true); } }
static void OnRxWindow1TimerEvent( TimerHandle_t xTimer ) { uint16_t symbTimeout = 5; // DR_2, DR_1, DR_0 int8_t datarate = 0; uint32_t bandwidth = 0; // LoRa 125 kHz TimerStop(&RxWindow1Timer); phyFlags.Bits.RxSlot = 1; datarate = pLoRaDevice->currDataRateIndex - Rx1DrOffset; if ( datarate < 0 ) { datarate = DR_0; } // For higher datarates, we increase the number of symbols generating a Rx Timeout if ( datarate >= DR_3 ) { // DR_6, DR_5, DR_4, DR_3 symbTimeout = 8; } if ( datarate == DR_6 ) { // LoRa 250 kHz bandwidth = 1; } LOG_TRACE("Open single Rx window 1 at %u ms (Channel : %u / DR: %u).", (uint32_t)(TimerGetCurrentTime() * portTICK_PERIOD_MS), Channels[pLoRaDevice->currChannelIndex].Frequency, datarate); OpenReceptionWindow(Channels[pLoRaDevice->currChannelIndex].Frequency, datarate, bandwidth, symbTimeout, false); }
static void OnRadioRxTimeout( void ) { #if defined(LOG_LEVEL_TRACE) if ( phyFlags.Bits.RxSlot == 1 ) { LOG_TRACE("Receive window 1 timeout occurred at %u ms.", (uint32_t)(TimerGetCurrentTime() * portTICK_PERIOD_MS)); } else if ( phyFlags.Bits.RxSlot == 2 ) { LOG_TRACE("Receive window 2 timeout occurred at %u ms.", (uint32_t)(TimerGetCurrentTime() * portTICK_PERIOD_MS)); } else if ( phyFlags.Bits.RxSlot == 3 ) { LOG_TRACE("Time synchronized reception window timeout occurred."); } #endif phyStatus = PHY_TIMEOUT; phyFlags.Bits.RxDone = 0; }
static void OnCadDone( bool channelActivityDetected ) { #if defined(LOG_LEVEL_TRACE) uint64_t curTime = TimerGetCurrentTime(); #endif Radio.Sleep(); if ( !channelActivityDetected ) { LOG_TRACE("Channel clear. Send packet now (%d%d)", *(((int*) (&curTime)) + 1), curTime); // Radio.Send(LoRaMacBuffer, LoRaMacBufferPktLen); } }
bool RegionCN470ATxConfig( TxConfigParams_t *txConfig, int8_t *txPower, TimerTime_t *txTimeOnAir ) { RadioModems_t modem; uint32_t frequency; uint16_t preambleLen; bool iqInverted; TimerTime_t curTime = TimerGetCurrentTime( ); int8_t phyDr = DataratesCN470A[txConfig->Datarate]; int8_t txPowerLimited = LimitTxPower( txConfig->TxPower, Bands[TxChannels[txConfig->Channel].Band].TxMaxPower, txConfig->Datarate, ChannelsMask ); uint32_t bandwidth = GetBandwidth( txConfig->Datarate ); int8_t phyTxPower = 0; // Calculate physical TX power phyTxPower = RegionCommonComputeTxPower( txPowerLimited, txConfig->MaxEirp, txConfig->AntennaGain ); if (txConfig->NodeWorkMode == NODE_MODE_REPEATER) { frequency = txConfig->RepeaterFrequency; preambleLen = PreambleLenthCN470A[txConfig->Datarate]; iqInverted = true; } else { frequency = TxChannels[txConfig->Channel].Frequency; preambleLen = 8; iqInverted = false; } // Setup the radio frequency Radio.SetChannel( frequency); if ( txConfig->Datarate == DR_7 ) { // High Speed FSK channel modem = MODEM_FSK; Radio.SetTxConfig( modem, phyTxPower, 25e3, bandwidth, phyDr * 1e3, 0, 5, false, true, 0, 0, false, 3e3 ); } else { modem = MODEM_LORA; Radio.SetTxConfig( modem, phyTxPower, 0, bandwidth, phyDr, 1, preambleLen, false, true, 0, 0, iqInverted, 3e3 ); } // Setup maximum payload lenght of the radio driver Radio.SetMaxPayloadLength( modem, txConfig->PktLen ); // Get the time-on-air of the next tx frame *txTimeOnAir = Radio.TimeOnAir( modem, txConfig->PktLen ); *txPower = txConfig->TxPower; DBG_LINKWAN("Tx, Band %d, Freq: %d,DR: %d, len: %d, duration %d, at %d\r\n", TxFreqBandNum, frequency, txConfig->Datarate, txConfig->PktLen, *txTimeOnAir, curTime); return true; }
static void OnRadioTxDone( void ) { TimerTime_t curTime = TimerGetCurrentTime(); LOG_TRACE("Transmitted successfully (%u ms).", (uint32_t)(curTime * portTICK_PERIOD_MS)); // Update Band Time OFF Bands[Channels[pLoRaDevice->currChannelIndex].Band].LastTxDoneTime = curTime; if ( pLoRaDevice->dbgFlags.Bits.dutyCycleCtrlOff == 0 ) { Bands[Channels[pLoRaDevice->currChannelIndex].Band].TimeOff = TxTimeOnAir * Bands[Channels[pLoRaDevice->currChannelIndex].Band].DCycle - TxTimeOnAir; } else { Bands[Channels[pLoRaDevice->currChannelIndex].Band].TimeOff = 0; } // Update Agregated Time OFF AggregatedLastTxDoneTime = curTime; AggregatedTimeOff = AggregatedTimeOff + (TxTimeOnAir * AggregatedDCycle - TxTimeOnAir); if ( phyFlags.Bits.TxType == LORAPHY_TXTYPE_ADVERTISING ) { /* Open advertising beacon reception window */ } else if ( phyFlags.Bits.TxType == LORAPHY_TXTYPE_REGULAR && pLoRaDevice->dbgFlags.Bits.rxWindowsDisabled != 1 ) { TimerSetValue(&RxWindow1Timer, pLoRaDevice->rxWindow1Delay); TimerStart(&RxWindow1Timer); TimerSetValue(&RxWindow2Timer, pLoRaDevice->rxWindow2Delay); TimerStart(&RxWindow2Timer); } else { phyFlags.Bits.TxDone = 1; } /* Uplink message repetition is only valid for unconfirmed messages */ if ( pLoRaDevice->ctrlFlags.Bits.ackPending == 0 ) { pLoRaDevice->nbRepCounter++; } }
bool RegionCN470ARxConfig( RxConfigParams_t *rxConfig, int8_t *datarate ) { RadioModems_t modem; int8_t dr = rxConfig->Datarate; uint8_t maxPayload = 0; int8_t phyDr = 0; uint32_t frequency = rxConfig->Frequency; TimerTime_t curTime = TimerGetCurrentTime( ); bool iqInverted; if ( Radio.GetStatus( ) != RF_IDLE ) { return false; } if ( rxConfig->Window == 0 ) { // Apply window 1 frequency frequency = Channels[rxConfig->Channel].Frequency; // Apply the alternative RX 1 window frequency, if it is available if ( Channels[rxConfig->Channel].Rx1Frequency != 0 ) { frequency = Channels[rxConfig->Channel].Rx1Frequency; } } if (rxConfig->Window == 1) { if (get_lora_freq_type() == FREQ_TYPE_INTER) { frequency = 470300000 + (InterFreqRx2Chan[TxFreqBandNum]) * 200000; } else { frequency = 470300000 + (IntraFreqRx2Chan[TxFreqBandNum]) * 200000; } } // Read the physical datarate from the datarates table phyDr = DataratesCN470A[dr]; if (rxConfig->NodeWorkMode == NODE_MODE_REPEATER) { frequency = rxConfig->RepeaterFrequency; iqInverted = false; } else { iqInverted = true; } Radio.SetChannel( frequency ); // Radio configuration if ( dr == DR_7 ) { modem = MODEM_FSK; Radio.SetRxConfig( modem, 50e3, phyDr * 1e3, 0, 83.333e3, 5, rxConfig->WindowTimeout, false, 0, true, 0, 0, false, rxConfig->RxContinuous ); } else { modem = MODEM_LORA; Radio.SetRxConfig( modem, rxConfig->Bandwidth, phyDr, 1, 0, 8, rxConfig->WindowTimeout, false, 0, false, 0, 0, iqInverted, rxConfig->RxContinuous ); } if ( rxConfig->RepeaterSupport == true ) { maxPayload = MaxPayloadOfDatarateRepeaterCN470A[dr]; } else { maxPayload = MaxPayloadOfDatarateCN470A[dr]; } Radio.SetMaxPayloadLength( modem, maxPayload + LORA_MAC_FRMPAYLOAD_OVERHEAD ); *datarate = (uint8_t) dr; rxConfig->Frequency = frequency; return true; }
/* * Set next transmission channel according to duty cycle boundries * unless it is a scheduled transmission on a specific channel (Class D) * * \return ERR_OK if a channel was set successfully */ static uint8_t SetNextChannel( void ) { uint8_t i = 0; uint8_t j = 0; uint8_t k = 0; uint8_t nbEnabledChannels = 0; uint8_t enabledChannels[LORA_MAX_NB_CHANNELS]; TimerTime_t curTime = TimerGetCurrentTime(); memset1(enabledChannels, 0, LORA_MAX_NB_CHANNELS); // Update Aggregated duty cycle if ( AggregatedTimeOff < (curTime - AggregatedLastTxDoneTime) ) { AggregatedTimeOff = 0; } // Update bands Time OFF TimerTime_t minTime = (TimerTime_t)(-1); for ( i = 0; i < LORA_MAX_NB_BANDS; i++ ) { if ( pLoRaDevice->dbgFlags.Bits.dutyCycleCtrlOff == 0 ) { if ( Bands[i].TimeOff < (curTime - Bands[i].LastTxDoneTime) ) { Bands[i].TimeOff = 0; } if ( Bands[i].TimeOff != 0 ) { minTime = MIN(Bands[i].TimeOff, minTime); } } else { minTime = 0; Bands[i].TimeOff = 0; } } // Search how many channels are enabled for ( i = 0, k = 0; i < LORA_MAX_NB_CHANNELS; i += 16, k++ ) { for ( j = 0; j < 16; j++ ) { if ( (pLoRaDevice->channelsMask[k] & (1 << j)) != 0 ) { if ( Channels[i + j].Frequency == 0 ) { // Check if the channel is enabled continue; } if ( ((Channels[i + j].DrRange.Fields.Min <= pLoRaDevice->currChannelIndex) && (pLoRaDevice->currDataRateIndex <= Channels[i + j].DrRange.Fields.Max)) == false ) { // Check if the current channel selection supports the given datarate continue; } if ( Bands[Channels[i + j].Band].TimeOff > 0 ) { // Check if the band is available for transmission continue; } if ( AggregatedTimeOff > 0 ) { // Check if there is time available for transmission continue; } enabledChannels[nbEnabledChannels++] = i + j; } } } if ( nbEnabledChannels > 0 ) { pLoRaDevice->currChannelIndex = enabledChannels[randr(0, nbEnabledChannels - 1)]; return 0; } return ERR_OK; }
/*! * Check tx message queue to see if any messages are pending. * * \retvalue ERR_OK Transmission started successfully. * ERR_NOTAVAIL No channel available. * ERR_VALUE Invalid tx type selected. * ERR_DISABLED Device was remotely disable (MaxDCycle setting). * ERR_RXEMPTY Message queue is empty. */ static uint8_t CheckTx( void ) { LoRaPhy_ChannelParams_t channel; uint8_t flags; uint8_t TxDataBuffer[LORAPHY_BUFFER_SIZE]; if ( GetTxMsg(TxDataBuffer, sizeof(TxDataBuffer)) == ERR_OK ) { #if 0 if ( SetNextChannel() != ERR_OK ) { return ERR_NOTAVAIL; } #endif flags = LORAPHY_BUF_FLAGS(TxDataBuffer); channel = Channels[pLoRaDevice->currChannelIndex]; if ( flags & LORAPHY_PACKET_FLAGS_JOIN_REQ ) { pLoRaDevice->rxWindow1Delay = JoinAcceptDelay1 - RADIO_WAKEUP_TIME; pLoRaDevice->rxWindow2Delay = JoinAcceptDelay2 - RADIO_WAKEUP_TIME; } else { pLoRaDevice->rxWindow1Delay = ReceiveDelay1 - RADIO_WAKEUP_TIME; pLoRaDevice->rxWindow2Delay = ReceiveDelay2 - RADIO_WAKEUP_TIME; } Radio.SetChannel(channel.Frequency); Radio.SetMaxPayloadLength(MODEM_LORA, LORAPHY_BUF_SIZE(TxDataBuffer)); if ( pLoRaDevice->currDataRateIndex == DR_7 ) { // High Speed FSK channel Radio.SetTxConfig(MODEM_FSK, TxPowers[pLoRaDevice->currTxPowerIndex], 25e3, 0, Datarates[pLoRaDevice->currDataRateIndex] * 1e3, 0, 5, false, true, 0, 0, false, TX_TIMEOUT); TxTimeOnAir = Radio.TimeOnAir(MODEM_FSK, LORAPHY_BUF_SIZE(TxDataBuffer)); } else if ( pLoRaDevice->currDataRateIndex == DR_6 ) { // High speed LoRa channel Radio.SetTxConfig(MODEM_LORA, TxPowers[pLoRaDevice->currTxPowerIndex], 0, 1, Datarates[pLoRaDevice->currDataRateIndex], 1, 8, false, true, 0, 0, false, TX_TIMEOUT); TxTimeOnAir = Radio.TimeOnAir(MODEM_LORA, LORAPHY_BUF_SIZE(TxDataBuffer)); } else { // Normal LoRa channel Radio.SetTxConfig(MODEM_LORA, TxPowers[pLoRaDevice->currTxPowerIndex], 0, 0, Datarates[pLoRaDevice->currDataRateIndex], 1, 8, false, true, 0, 0, false, TX_TIMEOUT); TxTimeOnAir = Radio.TimeOnAir(MODEM_LORA, LORAPHY_BUF_SIZE(TxDataBuffer)); } if ( MaxDCycle == 255 ) { return ERR_DISABLED; } if ( MaxDCycle == 0 ) { AggregatedTimeOff = 0; } if ( MAX(Bands[channel.Band].TimeOff, AggregatedTimeOff) > (TimerGetCurrentTime()) ) { // Schedule transmission LOG_TRACE("Send in %d ticks on channel %d (DR: %u).", MAX(Bands[channel.Band].TimeOff, AggregatedTimeOff), channel.Frequency, pLoRaDevice->currDataRateIndex); vTaskDelay( MAX(Bands[channel.Band].TimeOff, AggregatedTimeOff) / MAX(Bands[channel.Band].TimeOff, AggregatedTimeOff)); } else { // Send now LOG_TRACE("Sending at %u ms on channel %d (DR: %u).", (uint32_t)(TimerGetCurrentTime() * portTICK_PERIOD_MS), channel.Frequency, pLoRaDevice->currDataRateIndex); Radio.Send(LORAPHY_BUF_PAYLOAD_START(TxDataBuffer), LORAPHY_BUF_SIZE(TxDataBuffer)); // LOG_DEBUG("Send data on channel with frequency %u Hz", channel.Frequency); } if ( (flags & LORAPHY_PACKET_FLAGS_FRM_MASK) == LORAPHY_PACKET_FLAGS_FRM_ADVERTISING ) { phyFlags.Bits.TxType = LORAPHY_TXTYPE_ADVERTISING; } else if ( (flags & LORAPHY_PACKET_FLAGS_FRM_MASK) == LORAPHY_PACKET_FLAGS_FRM_REGULAR ) { phyFlags.Bits.TxType = LORAPHY_TXTYPE_REGULAR; } else if ( (flags & LORAPHY_PACKET_FLAGS_FRM_MASK) == LORAPHY_PACKET_FLAGS_FRM_MULTICAST ) { phyFlags.Bits.TxType = LORAPHY_TXTYPE_MULTICAST; } else { return ERR_VALUE; } return ERR_OK; } return ERR_NOTAVAIL; /* no data to send? */ }