/*
 This project is free software: you can redistribute it and/or modify
 it under the terms of the GNU General Public License as published by
 the Free Software Foundation, either version 3 of the License, or
 (at your option) any later version.

 Multiprotocol is distributed in the hope that it will be useful,
 but WITHOUT ANY WARRANTY; without even the implied warranty of
 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 GNU General Public License for more details.

 You should have received a copy of the GNU General Public License
 along with Multiprotocol.  If not, see <http://www.gnu.org/licenses/>.
 */

#if defined(FRSKY_RX_CC2500_INO)

#include "iface_cc2500.h" 

 #define FRSKY_RX_D16FCC_LENGTH	0x1D+1
 #define FRSKY_RX_D16LBT_LENGTH	0x20+1
 #define FRSKY_RX_D16v2_LENGTH	0x1D+1
 #define FRSKY_RX_D8_LENGTH		0x11+1
 #define FRSKY_RX_FORMATS		5

enum
{
	FRSKY_RX_D8			=0,
	FRSKY_RX_D16FCC		=1,
	FRSKY_RX_D16LBT		=2,
	FRSKY_RX_D16v2FCC	=3,
	FRSKY_RX_D16v2LBT	=4,
};

enum {
	FRSKY_RX_TUNE_START,
	FRSKY_RX_TUNE_LOW,
	FRSKY_RX_TUNE_HIGH,
	FRSKY_RX_BIND,
	FRSKY_RX_DATA,
};

const PROGMEM uint8_t FRSKY_RX_common_reg[][2] = {
	{CC2500_02_IOCFG0, 0x01},
	{CC2500_18_MCSM0, 0x18},
	{CC2500_07_PKTCTRL1, 0x05},
	{CC2500_3E_PATABLE, 0xFF},
	{CC2500_0C_FSCTRL0, 0},
	{CC2500_0D_FREQ2, 0x5C},
	{CC2500_13_MDMCFG1, 0x23},
	{CC2500_14_MDMCFG0, 0x7A},
	{CC2500_19_FOCCFG, 0x16},
	{CC2500_1A_BSCFG, 0x6C},
	{CC2500_1B_AGCCTRL2, 0x03},
	{CC2500_1C_AGCCTRL1, 0x40},
	{CC2500_1D_AGCCTRL0, 0x91},
	{CC2500_21_FREND1, 0x56},
	{CC2500_22_FREND0, 0x10},
	{CC2500_23_FSCAL3, 0xA9},
	{CC2500_24_FSCAL2, 0x0A},
	{CC2500_25_FSCAL1, 0x00},
	{CC2500_26_FSCAL0, 0x11},
	{CC2500_29_FSTEST, 0x59},
	{CC2500_2C_TEST2, 0x88},
	{CC2500_2D_TEST1, 0x31},
	{CC2500_2E_TEST0, 0x0B},
	{CC2500_03_FIFOTHR, 0x07},
	{CC2500_09_ADDR, 0x03},
};

const PROGMEM uint8_t FRSKY_RX_d16fcc_reg[][2] = {
	{CC2500_17_MCSM1, 0x0C},
	{CC2500_0E_FREQ1, 0x76},
	{CC2500_0F_FREQ0, 0x27},
	{CC2500_06_PKTLEN, 0x1E},
	{CC2500_08_PKTCTRL0, 0x01},
	{CC2500_0B_FSCTRL1, 0x0A},
	{CC2500_10_MDMCFG4, 0x7B},
	{CC2500_11_MDMCFG3, 0x61},
	{CC2500_12_MDMCFG2, 0x13},
	{CC2500_15_DEVIATN, 0x51},
};

const PROGMEM uint8_t FRSKY_RX_d16lbt_reg[][2] = {
	{CC2500_17_MCSM1, 0x0E},
	{CC2500_0E_FREQ1, 0x80},
	{CC2500_0F_FREQ0, 0x00},
	{CC2500_06_PKTLEN, 0x23},
	{CC2500_08_PKTCTRL0, 0x01},
	{CC2500_0B_FSCTRL1, 0x08},
	{CC2500_10_MDMCFG4, 0x7B},
	{CC2500_11_MDMCFG3, 0xF8},
	{CC2500_12_MDMCFG2, 0x03},
	{CC2500_15_DEVIATN, 0x53},
};

const PROGMEM uint8_t FRSKY_RX_d8_reg[][2] = {
	{CC2500_17_MCSM1,    0x0C},
	{CC2500_0E_FREQ1,    0x76},
	{CC2500_0F_FREQ0,    0x27},
	{CC2500_06_PKTLEN,   0x19},
	{CC2500_08_PKTCTRL0, 0x05},
	{CC2500_0B_FSCTRL1,  0x08},
	{CC2500_10_MDMCFG4,  0xAA},
	{CC2500_11_MDMCFG3,  0x39},
	{CC2500_12_MDMCFG2,  0x11},
	{CC2500_15_DEVIATN,  0x42},
};

static uint8_t FRSKY_RX_chanskip;
static int8_t  FRSKY_RX_finetune;
static uint8_t FRSKY_RX_format;

static void __attribute__((unused)) FRSKY_RX_strobe_rx()
{
	 CC2500_Strobe(CC2500_SIDLE);
	 CC2500_Strobe(CC2500_SFRX);
	 CC2500_Strobe(CC2500_SRX);
}

static void __attribute__((unused)) FRSKY_RX_initialise_cc2500() {
	const uint8_t FRSKY_RX_length[] = { FRSKY_RX_D8_LENGTH, FRSKY_RX_D16FCC_LENGTH, FRSKY_RX_D16LBT_LENGTH, FRSKY_RX_D16v2_LENGTH, FRSKY_RX_D16v2_LENGTH };
	packet_length = FRSKY_RX_length[FRSKY_RX_format];
	CC2500_Reset();
	CC2500_Strobe(CC2500_SIDLE);
	for (uint8_t i = 0; i < sizeof(FRSKY_RX_common_reg) / 2; i++)
		CC2500_WriteReg(pgm_read_byte_near(&FRSKY_RX_common_reg[i][0]), pgm_read_byte_near(&FRSKY_RX_common_reg[i][1]));

	switch (FRSKY_RX_format)
	{
		case FRSKY_RX_D16v2FCC:
		case FRSKY_RX_D16FCC:
			for (uint8_t i = 0; i < sizeof(FRSKY_RX_d16fcc_reg) / 2; i++)
				CC2500_WriteReg(pgm_read_byte_near(&FRSKY_RX_d16fcc_reg[i][0]), pgm_read_byte_near(&FRSKY_RX_d16fcc_reg[i][1]));
			if(FRSKY_RX_format==FRSKY_RX_D16v2FCC)
			{
				CC2500_WriteReg(CC2500_08_PKTCTRL0, 0x05);	// Enable CRC
				CC2500_WriteReg(CC2500_17_MCSM1, 0x0E);		// Go/Stay in RX mode
				CC2500_WriteReg(CC2500_11_MDMCFG3, 0x84);	// bitrate 70K->77K
			}
			break;
		case FRSKY_RX_D16v2LBT:
		case FRSKY_RX_D16LBT:
			for (uint8_t i = 0; i < sizeof(FRSKY_RX_d16lbt_reg) / 2; i++)
				CC2500_WriteReg(pgm_read_byte_near(&FRSKY_RX_d16lbt_reg[i][0]), pgm_read_byte_near(&FRSKY_RX_d16lbt_reg[i][1]));
			if(FRSKY_RX_format==FRSKY_RX_D16v2LBT)
				CC2500_WriteReg(CC2500_08_PKTCTRL0, 0x05);	// Enable CRC
			break;
		case FRSKY_RX_D8:
			for (uint8_t i = 0; i < sizeof(FRSKY_RX_d8_reg) / 2; i++)
				CC2500_WriteReg(pgm_read_byte_near(&FRSKY_RX_d8_reg[i][0]), pgm_read_byte_near(&FRSKY_RX_d8_reg[i][1]));
			CC2500_WriteReg(CC2500_23_FSCAL3, 0x89);
			break;
	}
	CC2500_WriteReg(CC2500_0A_CHANNR, 0);  // bind channel
	rx_disable_lna = IS_POWER_FLAG_on;
	CC2500_SetTxRxMode(rx_disable_lna ? TXRX_OFF : RX_EN); // lna disable / enable
	FRSKY_RX_strobe_rx();
	delayMicroseconds(1000); // wait for RX to activate
}

static void __attribute__((unused)) FRSKY_RX_set_channel(uint8_t channel)
{
	CC2500_WriteReg(CC2500_0A_CHANNR, hopping_frequency[channel]);
	if(FRSKY_RX_format == FRSKY_RX_D8)
		CC2500_WriteReg(CC2500_23_FSCAL3, 0x89);
	CC2500_WriteReg(CC2500_25_FSCAL1, calData[channel]);
	FRSKY_RX_strobe_rx();
}

static void __attribute__((unused)) FRSKY_RX_calibrate()
{
	FRSKY_RX_strobe_rx();
	for (unsigned c = 0; c < 47; c++)
	{
		CC2500_Strobe(CC2500_SIDLE);
		CC2500_WriteReg(CC2500_0A_CHANNR, hopping_frequency[c]);
		CC2500_Strobe(CC2500_SCAL);
		delayMicroseconds(900);
		calData[c] = CC2500_ReadReg(CC2500_25_FSCAL1);
	}
}

static uint8_t __attribute__((unused)) frskyx_rx_check_crc_id(bool bind,bool init)
{
	/*debugln("RX");
	for(uint8_t i=0; i<packet_length;i++)
		debug(" %02X",packet[i]);
	debugln("");*/
	
	if(bind && packet[0]!=packet_length-1 && packet[1] !=0x03 && packet[2] != 0x01)
		return false;
	uint8_t offset=bind?3:1;
	
	// Check D8 checksum
	if (FRSKY_RX_format == FRSKY_RX_D8)
	{
		if((packet[packet_length+1] & 0x80) != 0x80)	// Check CRC_OK flag in status byte 2
			return false; 								// Bad CRC
		if(init)
		{//Save TXID
			rx_tx_addr[3] = packet[3];
			rx_tx_addr[2] = packet[4];
			rx_tx_addr[1] = packet[17];
		}
		else
			if(rx_tx_addr[3] != packet[offset] || rx_tx_addr[2] != packet[offset+1] || rx_tx_addr[1] != packet[bind?17:5])
				return false;							// Bad address
		return true;									// Full match
	}

	// Check D16v2 checksum
	if (FRSKY_RX_format == FRSKY_RX_D16v2LBT || FRSKY_RX_format == FRSKY_RX_D16v2FCC)
		if((packet[packet_length+1] & 0x80) != 0x80)	// Check CRC_OK flag in status byte 2
			return false;
	//debugln("HW Checksum ok");

	// Check D16 checksum
	uint16_t lcrc = FrSkyX_crc(&packet[3], packet_length - 5);		// Compute crc
	uint16_t rcrc = (packet[packet_length-2] << 8) | (packet[packet_length-1] & 0xff);	// Received crc
	if(lcrc != rcrc)
		return false; 									// Bad CRC
	//debugln("Checksum ok");

	if (bind && (FRSKY_RX_format == FRSKY_RX_D16v2LBT || FRSKY_RX_format == FRSKY_RX_D16v2FCC))
		for(uint8_t i=3; i<packet_length-2; i++)		//unXOR bind packet
			packet[i] ^= 0xA7;
	
	uint8_t offset2=0;
	if (bind && (FRSKY_RX_format == FRSKY_RX_D16LBT || FRSKY_RX_format == FRSKY_RX_D16FCC))
		offset2=6;
	if(init)
	{//Save TXID
		rx_tx_addr[3] = packet[3];
		rx_tx_addr[2] = packet[4];
		rx_tx_addr[1] = packet[5+offset2];
		rx_tx_addr[0] = packet[6+offset2];				// RXnum
	}
	else
		if(rx_tx_addr[3] != packet[offset] || rx_tx_addr[2] != packet[offset+1] || rx_tx_addr[1] != packet[offset+2+offset2])
			return false;								// Bad address
	//debugln("Address ok");
	
	if(!bind && rx_tx_addr[0] != packet[6])
		return false;									// Bad RX num
	
	//debugln("Match");
	return true;										// Full match
}

static void __attribute__((unused)) FRSKY_RX_build_telemetry_packet()
{
	uint16_t raw_channel[8];
	uint32_t bits = 0;
	uint8_t bitsavailable = 0;
	uint8_t idx = 0;
	uint8_t i;

	if (FRSKY_RX_format == FRSKY_RX_D8)
	{// decode D8 channels
		raw_channel[0] = ((packet[10] & 0x0F) << 8 | packet[6]);
		raw_channel[1] = ((packet[10] & 0xF0) << 4 | packet[7]);
		raw_channel[2] = ((packet[11] & 0x0F) << 8 | packet[8]);
		raw_channel[3] = ((packet[11] & 0xF0) << 4 | packet[9]);
		raw_channel[4] = ((packet[16] & 0x0F) << 8 | packet[12]);
		raw_channel[5] = ((packet[16] & 0xF0) << 4 | packet[13]);
		raw_channel[6] = ((packet[17] & 0x0F) << 8 | packet[14]);
		raw_channel[7] = ((packet[17] & 0xF0) << 4 | packet[15]);
		for (i = 0; i < 8; i++) {
			if (raw_channel[i] < 1290)
				raw_channel[i] = 1290;
			rx_rc_chan[i] = min(((raw_channel[i] - 1290) << 4) / 15, 2047);
		}
	}
	else
	{// decode D16 channels
		raw_channel[0] = ((packet[10] << 8) & 0xF00) | packet[9];
		raw_channel[1] = ((packet[11] << 4) & 0xFF0) | (packet[10] >> 4);
		raw_channel[2] = ((packet[13] << 8) & 0xF00) | packet[12];
		raw_channel[3] = ((packet[14] << 4) & 0xFF0) | (packet[13] >> 4);
		raw_channel[4] = ((packet[16] << 8) & 0xF00) | packet[15];
		raw_channel[5] = ((packet[17] << 4) & 0xFF0) | (packet[16] >> 4);
		raw_channel[6] = ((packet[19] << 8) & 0xF00) | packet[18];
		raw_channel[7] = ((packet[20] << 4) & 0xFF0) | (packet[19] >> 4);	
		for (i = 0; i < 8; i++) {
			// ignore failsafe channels
			if(packet[7] != 0x10+(i<<1)) {
				uint8_t shifted = (raw_channel[i] & 0x800)>0;
				uint16_t channel_value = raw_channel[i] & 0x7FF;
				if (channel_value < 64)
					rx_rc_chan[shifted ? i + 8 : i] = 0;
				else
					rx_rc_chan[shifted ? i + 8 : i] = min(((channel_value - 64) << 4) / 15, 2047);
			}
		}
	}

	// buid telemetry packet
	packet_in[idx++] = RX_LQI;
	packet_in[idx++] = RX_RSSI;
	packet_in[idx++] = 0;  // start channel
	packet_in[idx++] = FRSKY_RX_format == FRSKY_RX_D8 ? 8 : 16; // number of channels in packet

	// pack channels
	for (i = 0; i < packet_in[3]; i++) {
		bits |= ((uint32_t)rx_rc_chan[i]) << bitsavailable;
		bitsavailable += 11;
		while (bitsavailable >= 8) {
			packet_in[idx++] = bits & 0xff;
			bits >>= 8;
			bitsavailable -= 8;
		}
	}
}

static void __attribute__((unused)) FRSKY_RX_data()
{
	uint16_t temp = FRSKY_RX_EEPROM_OFFSET;
	FRSKY_RX_format = eeprom_read_byte((EE_ADDR)temp++) % FRSKY_RX_FORMATS;
	rx_tx_addr[3] = eeprom_read_byte((EE_ADDR)temp++);
	rx_tx_addr[2] = eeprom_read_byte((EE_ADDR)temp++);
	rx_tx_addr[1] = eeprom_read_byte((EE_ADDR)temp++);
	rx_tx_addr[0] = RX_num;
	FRSKY_RX_finetune = eeprom_read_byte((EE_ADDR)temp++);
	debug("format=%d, ", FRSKY_RX_format);
	debug("addr[3]=%02X, ", rx_tx_addr[3]);
	debug("addr[2]=%02X, ", rx_tx_addr[2]);
	debug("addr[1]=%02X, ", rx_tx_addr[1]);
	debug("rx_num=%02X, ",  rx_tx_addr[0]);
	debugln("tune=%d", (int8_t)FRSKY_RX_finetune);
	if(FRSKY_RX_format != FRSKY_RX_D16v2LBT && FRSKY_RX_format != FRSKY_RX_D16v2FCC)
	{//D8 & D16v1
		for (uint8_t ch = 0; ch < 47; ch++)
			hopping_frequency[ch] = eeprom_read_byte((EE_ADDR)temp++);
	}
	else
	{
		FrSkyFormat=FRSKY_RX_format == FRSKY_RX_D16v2FCC?0:2;
		FrSkyX2_init_hop();
	}
	debug("ch:");
	for (uint8_t ch = 0; ch < 47; ch++)
		debug(" %02X", hopping_frequency[ch]);
	debugln("");

	FRSKY_RX_initialise_cc2500();
	FRSKY_RX_calibrate();
	CC2500_WriteReg(CC2500_18_MCSM0, 0x08); // FS_AUTOCAL = manual
	CC2500_WriteReg(CC2500_09_ADDR, rx_tx_addr[3]); // set address
	CC2500_WriteReg(CC2500_07_PKTCTRL1, 0x05); // check address
	if (option == 0)
		CC2500_WriteReg(CC2500_0C_FSCTRL0, FRSKY_RX_finetune);
	else
		CC2500_WriteReg(CC2500_0C_FSCTRL0, option);
	FRSKY_RX_set_channel(hopping_frequency_no);
	phase = FRSKY_RX_DATA;
}

void FRSKY_RX_init()
{
	if(sub_protocol == FRSKY_ERASE)
	{
		if(IS_BIND_IN_PROGRESS)
		{// Clear all cloned addresses
			uint16_t addr[]={ FRSKYD_CLONE_EEPROM_OFFSET+1, FRSKYX_CLONE_EEPROM_OFFSET+1, FRSKYX2_CLONE_EEPROM_OFFSET+1 };
			for(uint8_t i=0; i<3;i++)
				for(uint8_t j=0; j<3;j++)
					eeprom_write_byte((EE_ADDR)(addr[i]+j), 0xFF);
			packet_count = 100;
		}			
	}
	else
	{
		FRSKY_RX_chanskip = 1;
		hopping_frequency_no = 0;
		rx_data_started = false;
		FRSKY_RX_finetune = 0;
		telemetry_link = 0;
		packet_count = 0;
		if (IS_BIND_IN_PROGRESS)
		{
			FRSKY_RX_format = FRSKY_RX_D8;
			FRSKY_RX_initialise_cc2500();
			phase = FRSKY_RX_TUNE_START;
			debugln("FRSKY_RX_TUNE_START");
		}
		else
			FRSKY_RX_data();
	}
}

uint16_t FRSKY_RX_callback()
{
	static int8_t read_retry = 0;
	static int8_t tune_low, tune_high;
	uint8_t len, ch;

	if(sub_protocol == FRSKY_ERASE)
	{
		if(packet_count)
			packet_count--;
		else
			BIND_DONE;
		return 10000;	//  Nothing to do...
	}

	if(IS_BIND_DONE && phase != FRSKY_RX_DATA)
		FRSKY_RX_init();	// Abort bind

	if ((prev_option != option) && (phase >= FRSKY_RX_DATA))
	{
		if (option == 0)
			CC2500_WriteReg(CC2500_0C_FSCTRL0, FRSKY_RX_finetune);
		else
			CC2500_WriteReg(CC2500_0C_FSCTRL0, option);
		prev_option = option;
	}

	if (rx_disable_lna != IS_POWER_FLAG_on)
	{
		rx_disable_lna = IS_POWER_FLAG_on;
		CC2500_SetTxRxMode(rx_disable_lna ? TXRX_OFF : RX_EN);
	}

	len = CC2500_ReadReg(CC2500_3B_RXBYTES | CC2500_READ_BURST) & 0x7F;
	switch(phase)
	{
		case FRSKY_RX_TUNE_START:
			if (len == packet_length + 2) //+2=RSSI+LQI+CRC
			{
				CC2500_ReadData(packet, len);
				if(frskyx_rx_check_crc_id(true,true))
				{
					FRSKY_RX_finetune = -127;
					CC2500_WriteReg(CC2500_0C_FSCTRL0, FRSKY_RX_finetune);
					phase = FRSKY_RX_TUNE_LOW;
					debugln("FRSKY_RX_TUNE_LOW");
					FRSKY_RX_strobe_rx();
					state = 0;
					return 1000;
				}
			}
			FRSKY_RX_format = (FRSKY_RX_format + 1) % FRSKY_RX_FORMATS; // switch to next format (D8, D16FCC, D16LBT, D16v2FCC, D16v2LBT)
			FRSKY_RX_initialise_cc2500();
			FRSKY_RX_finetune += 10;
			CC2500_WriteReg(CC2500_0C_FSCTRL0, FRSKY_RX_finetune);
			FRSKY_RX_strobe_rx();
			return 18000;

		case FRSKY_RX_TUNE_LOW:
			if (len == packet_length + 2) //+2=RSSI+LQI+CRC
			{
				CC2500_ReadData(packet, len);
				if(frskyx_rx_check_crc_id(true,false)) {
					tune_low = FRSKY_RX_finetune;
					FRSKY_RX_finetune = 127;
					CC2500_WriteReg(CC2500_0C_FSCTRL0, FRSKY_RX_finetune);
					phase = FRSKY_RX_TUNE_HIGH;
					debugln("FRSKY_RX_TUNE_HIGH");
					FRSKY_RX_strobe_rx();
					return 1000;
				}
			}
			FRSKY_RX_finetune += 1;
			CC2500_WriteReg(CC2500_0C_FSCTRL0, FRSKY_RX_finetune);
			FRSKY_RX_strobe_rx();
			return 18000;

		case FRSKY_RX_TUNE_HIGH:
			if (len == packet_length + 2) //+2=RSSI+LQI+CRC
			{
				CC2500_ReadData(packet, len);
				if(frskyx_rx_check_crc_id(true,false)) {
					tune_high = FRSKY_RX_finetune;
					FRSKY_RX_finetune = (tune_low + tune_high) / 2;
					CC2500_WriteReg(CC2500_0C_FSCTRL0, (int8_t)FRSKY_RX_finetune);
					if(tune_low < tune_high)
					{
						phase = FRSKY_RX_BIND;
						debugln("FRSKY_RX_TUNE_HIGH");
					}
					else
					{
						phase = FRSKY_RX_TUNE_START;
						debugln("FRSKY_RX_TUNE_START");
					}
					FRSKY_RX_strobe_rx();
					return 1000;
				}
			}
			FRSKY_RX_finetune -= 1;
			CC2500_WriteReg(CC2500_0C_FSCTRL0, FRSKY_RX_finetune);
			FRSKY_RX_strobe_rx();
			return 18000;

		case FRSKY_RX_BIND:
			if (len == packet_length + 2) //+2=RSSI+LQI+CRC
			{
				CC2500_ReadData(packet, len);
				if(frskyx_rx_check_crc_id(true,false)) {
					if(FRSKY_RX_format != FRSKY_RX_D16v2LBT && FRSKY_RX_format != FRSKY_RX_D16v2FCC)
					{// D8 & D16v1
						if(packet[5] <= 0x2D)
						{
							for (ch = 0; ch < 5; ch++)
								hopping_frequency[packet[5]+ch] = packet[6+ch];
							state |= 1 << (packet[5] / 5);
						}
					}
					else
						state = 0x3FF; //No hop table for D16v2
					if (state == 0x3FF)
					{
						debugln("Bind complete");
						BIND_DONE;
						// store format, finetune setting, txid, channel list
						uint16_t temp = FRSKY_RX_EEPROM_OFFSET;
						if(sub_protocol==FRSKY_CLONE)
						{
							if(FRSKY_RX_format==FRSKY_RX_D8)
								temp=FRSKYD_CLONE_EEPROM_OFFSET;
							else if(FRSKY_RX_format == FRSKY_RX_D16FCC || FRSKY_RX_format == FRSKY_RX_D16LBT)
								temp=FRSKYX_CLONE_EEPROM_OFFSET;
							else
								temp=FRSKYX2_CLONE_EEPROM_OFFSET;
						}
						eeprom_write_byte((EE_ADDR)temp++, FRSKY_RX_format);
						eeprom_write_byte((EE_ADDR)temp++, rx_tx_addr[3]);
						eeprom_write_byte((EE_ADDR)temp++, rx_tx_addr[2]);
						eeprom_write_byte((EE_ADDR)temp++, rx_tx_addr[1]);
						if(sub_protocol == FRSKY_RX || sub_protocol == FRSKY_CPPM)	// FRSKY_RX, FRSKY_CPPM
							eeprom_write_byte((EE_ADDR)temp++, FRSKY_RX_finetune);
						if(FRSKY_RX_format != FRSKY_RX_D16v2FCC && FRSKY_RX_format != FRSKY_RX_D16v2LBT)
							for (ch = 0; ch < 47; ch++)
								eeprom_write_byte((EE_ADDR)temp++, hopping_frequency[ch]);
						FRSKY_RX_data();
						debugln("FRSKY_RX_DATA");
					}
				}
				FRSKY_RX_strobe_rx();
			}
			return 1000;

		case FRSKY_RX_DATA:
			if (len == packet_length + 2) //+2=RSSI+LQI+CRC
			{
				CC2500_ReadData(packet, len);
				if(frskyx_rx_check_crc_id(false,false))
				{
					RX_RSSI = packet[len-2];
					if(RX_RSSI >= 128)
						RX_RSSI -= 128;
					else
						RX_RSSI += 128;
					bool chanskip_valid=true;
					// hop to next channel
					if (FRSKY_RX_format != FRSKY_RX_D8)
					{//D16v1 & D16v2
						if(rx_data_started)
						{
							if(FRSKY_RX_chanskip != (((packet[4] & 0xC0) >> 6) | ((packet[5] & 0x3F) << 2)))
							{
								chanskip_valid=false;	// chanskip value has changed which surely indicates a bad frame
								packet_count++;
								if(packet_count>5)		// the TX must have changed chanskip...
									FRSKY_RX_chanskip = ((packet[4] & 0xC0) >> 6) | ((packet[5] & 0x3F) << 2);	// chanskip init
							}
							else
								packet_count=0;
						}
						else
							FRSKY_RX_chanskip = ((packet[4] & 0xC0) >> 6) | ((packet[5] & 0x3F) << 2);	// chanskip init
					}
					hopping_frequency_no = (hopping_frequency_no + FRSKY_RX_chanskip) % 47;
					FRSKY_RX_set_channel(hopping_frequency_no);
					if(chanskip_valid)
					{
						if ((telemetry_link & 0x7F) == 0)
						{ // send channels to TX
							FRSKY_RX_build_telemetry_packet();
							telemetry_link = 1;
							#ifdef SEND_CPPM
								if(sub_protocol == FRSKY_CPPM)
									telemetry_link |= 0x80;		// Disable telemetry output
							#endif
						}
						pps_counter++;
					}
					rx_data_started = true;
					read_retry = 0;
				}
			}
			
			// packets per second
			if (millis() - pps_timer >= 1000) {
				pps_timer = millis();
				debugln("%d pps", pps_counter);
				RX_LQI = pps_counter;
				if(pps_counter==0)	// no packets for 1 sec or more...
				{// restart the search
					rx_data_started=false;
					packet_count=0;
				}
				pps_counter = 0;
			}

			// skip channel if no packet received in time
			if (read_retry++ >= 9) {
				hopping_frequency_no = (hopping_frequency_no + FRSKY_RX_chanskip) % 47;
				FRSKY_RX_set_channel(hopping_frequency_no);
				if(rx_data_started)
					read_retry = 0;
				else
					read_retry = -50; // retry longer until first packet is catched
			}
			break;
	}
	return 1000;
}

#endif
