/**
 * Copyright (c) 2009 - 2017, Nordic Semiconductor ASA
 * 
 * All rights reserved.
 * 
 * Redistribution and use in source and binary forms, with or without modification,
 * are permitted provided that the following conditions are met:
 * 
 * 1. Redistributions of source code must retain the above copyright notice, this
 *    list of conditions and the following disclaimer.
 * 
 * 2. Redistributions in binary form, except as embedded into a Nordic
 *    Semiconductor ASA integrated circuit in a product or a software update for
 *    such product, must reproduce the above copyright notice, this list of
 *    conditions and the following disclaimer in the documentation and/or other
 *    materials provided with the distribution.
 * 
 * 3. Neither the name of Nordic Semiconductor ASA nor the names of its
 *    contributors may be used to endorse or promote products derived from this
 *    software without specific prior written permission.
 * 
 * 4. This software, with or without modification, must only be used with a
 *    Nordic Semiconductor ASA integrated circuit.
 * 
 * 5. Any software provided in binary form under this license must not be reverse
 *    engineered, decompiled, modified and/or disassembled.
 * 
 * THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS
 * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 * OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE
 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
 * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 * 
 */
/** @file
* @addtogroup nrf_radio_test_example_main
* @{
*/

#include "radio_test.h"
#include <stdbool.h>
#include "nrf.h"

static uint8_t packet[256];

static uint8_t mode_;
static uint8_t txpower_;
static uint8_t channel_start_;
static uint8_t channel_end_;
static uint8_t channel_;
static bool    sweep_tx_;

/**
 * @brief Function for initializing Timer 0 in 24 bit timer mode with 1 us resolution.
*/
static void timer0_init(uint8_t delayms)
{
    NRF_TIMER0->TASKS_STOP = 1;

    // Create an Event-Task shortcut to clear Timer 1 on COMPARE[0] event.
    NRF_TIMER0->SHORTS     = (TIMER_SHORTS_COMPARE0_CLEAR_Enabled << TIMER_SHORTS_COMPARE0_CLEAR_Pos);
    NRF_TIMER0->MODE       = TIMER_MODE_MODE_Timer;
    NRF_TIMER0->BITMODE    = (TIMER_BITMODE_BITMODE_24Bit << TIMER_BITMODE_BITMODE_Pos);
    NRF_TIMER0->PRESCALER  = 4;  // 1us resolution
    NRF_TIMER0->INTENSET   = (TIMER_INTENSET_COMPARE0_Set << TIMER_INTENSET_COMPARE0_Pos);

    // Sample update needs to happen as soon as possible. The earliest possible moment is MAX_SAMPLE_LEVELS
    // ticks before changing the output duty cycle.
    NRF_TIMER0->CC[0]       = (uint32_t)delayms * 1000;
    NRF_TIMER0->TASKS_START = 1;
}


/**
 * @brief Function for generating an 8 bit random number using the internal random generator.
*/
static uint32_t rnd8(void)
{
    NRF_RNG->EVENTS_VALRDY = 0;
    while (NRF_RNG->EVENTS_VALRDY == 0)
    {
        // Do nothing.
    }
    return NRF_RNG->VALUE;
}


/**
 * @brief Function for generating a 32 bit random number using the internal random generator.
*/
static uint32_t rnd32(void)
{
    uint8_t  i;
    uint32_t val = 0;

    for (i=0; i<4; i++)
    {
        val <<= 8;
        val  |= rnd8();
    }
    return val;
}


/**
 * @brief Function for configuring the radio to use a random address and a 254 byte random payload.
 * The S0 and S1 fields are not used.
*/
static void generate_modulated_rf_packet(void)
{
    uint8_t i;

    NRF_RADIO->PREFIX0 = rnd8();
    NRF_RADIO->BASE0   = rnd32();

    // Packet configuration:
    // S1 size = 0 bits, S0 size = 0 bytes, payload length size = 8 bits
    NRF_RADIO->PCNF0  = (0UL << RADIO_PCNF0_S1LEN_Pos) |
                        (0UL << RADIO_PCNF0_S0LEN_Pos) |
                        (8UL << RADIO_PCNF0_LFLEN_Pos);
    // Packet configuration:
    // Bit 25: 1 Whitening enabled
    // Bit 24: 1 Big endian,
    // 4 byte base address length (5 byte full address length),
    // 0 byte static length, max 255 byte payload .
    NRF_RADIO->PCNF1  = (RADIO_PCNF1_WHITEEN_Enabled << RADIO_PCNF1_WHITEEN_Pos) |
                        (RADIO_PCNF1_ENDIAN_Big << RADIO_PCNF1_ENDIAN_Pos) |
                        (4UL << RADIO_PCNF1_BALEN_Pos) |
                        (0UL << RADIO_PCNF1_STATLEN_Pos) |
                        (255UL << RADIO_PCNF1_MAXLEN_Pos);
    NRF_RADIO->CRCCNF = (RADIO_CRCCNF_LEN_Disabled << RADIO_CRCCNF_LEN_Pos);
    packet[0]         = 254;    // 254 byte payload.

    // Fill payload with random data.
    for (i = 0; i < 254; i++)
    {
        packet[i + 1] = rnd8();
    }
    NRF_RADIO->PACKETPTR = (uint32_t)packet;
}


static void radio_disable(void)
{
    NRF_RADIO->SHORTS          = 0;
    NRF_RADIO->EVENTS_DISABLED = 0;
#ifdef NRF51
    NRF_RADIO->TEST            = 0;
#endif
    NRF_RADIO->TASKS_DISABLE   = 1;
    while (NRF_RADIO->EVENTS_DISABLED == 0)
    {
        // Do nothing.
    }
    NRF_RADIO->EVENTS_DISABLED = 0;
}


/**
 * @brief Function for stopping Timer 0.
*/
void radio_sweep_end(void)
{
    NRF_TIMER0->TASKS_STOP = 1;
    radio_disable();
}


/**
 * @brief Function for turning on the TX carrier test mode.
*/
void radio_tx_carrier(uint8_t txpower, uint8_t mode, uint8_t channel)
{
    radio_disable();
    NRF_RADIO->SHORTS     = RADIO_SHORTS_READY_START_Msk;
    NRF_RADIO->TXPOWER    = (txpower << RADIO_TXPOWER_TXPOWER_Pos);
    NRF_RADIO->MODE       = (mode << RADIO_MODE_MODE_Pos);
    NRF_RADIO->FREQUENCY  = channel;
#ifdef NRF51
    NRF_RADIO->TEST       = (RADIO_TEST_CONST_CARRIER_Enabled << RADIO_TEST_CONST_CARRIER_Pos) \
                            | (RADIO_TEST_PLL_LOCK_Enabled << RADIO_TEST_PLL_LOCK_Pos);
#endif
    NRF_RADIO->TASKS_TXEN = 1;
}


/**
 * @brief Function for starting modulated TX carrier by repeatedly sending a packet with random address and
 * random payload.
*/
void radio_modulated_tx_carrier(uint8_t txpower, uint8_t mode, uint8_t channel)
{
    radio_disable();
    generate_modulated_rf_packet();
    NRF_RADIO->SHORTS     = RADIO_SHORTS_END_DISABLE_Msk | RADIO_SHORTS_READY_START_Msk | \
                            RADIO_SHORTS_DISABLED_TXEN_Msk;;
    NRF_RADIO->TXPOWER    = (txpower << RADIO_TXPOWER_TXPOWER_Pos);
    NRF_RADIO->MODE       = (mode << RADIO_MODE_MODE_Pos);
    NRF_RADIO->FREQUENCY  = channel;
    NRF_RADIO->TASKS_TXEN = 1;
}


/**
 * @brief Function for turning on RX carrier.
*/
void radio_rx_carrier(uint8_t mode, uint8_t channel)
{
    radio_disable();
    NRF_RADIO->SHORTS     = RADIO_SHORTS_READY_START_Msk;
    NRF_RADIO->FREQUENCY  = channel;
    NRF_RADIO->TASKS_RXEN = 1;
}


/**
 * @brief Function for turning on TX carrier sweep. This test uses Timer 0 to restart the TX carrier at different channels.
*/
void radio_tx_sweep_start(uint8_t txpower, uint8_t mode, uint8_t channel_start, uint8_t channel_end, uint8_t delayms)
{
    txpower_       = txpower;
    mode_          = mode;
    channel_start_ = channel_start;
    channel_       = channel_start;
    channel_end_   = channel_end;
    sweep_tx_      = true;
    timer0_init(delayms);
}


/**
 * @brief Function for turning on RX carrier sweep. This test uses Timer 0 to restart the RX carrier at different channels.
*/
void radio_rx_sweep_start(uint8_t mode, uint8_t channel_start, uint8_t channel_end, uint8_t delayms)
{
    mode_          = mode;
    channel_start_ = channel_start;
    channel_       = channel_start;
    channel_end_   = channel_end;
    sweep_tx_      = false;
    timer0_init(delayms);
}


/**
 * @brief Function for handling the Timer 0 interrupt used for TX/RX sweep. The carrier is started with the new channel,
 * and the channel is incremented for the next interrupt.
*/
void TIMER0_IRQHandler(void)
{
    if (sweep_tx_)
    {
        radio_tx_carrier(txpower_, mode_, channel_);
    }
    else
    {
        radio_rx_carrier(mode_, channel_);
    }
    channel_++;
    if (channel_ > channel_end_)
    {
        channel_ = channel_start_;
    }
    NRF_TIMER0->EVENTS_COMPARE[0] = 0;
}
/**
 * @}
 */
