/**
 * @file main.c
 *
 * @brief  Analyser of IEEE 802.15.4 Radio Channels
 *
 *
 * @author    Bc. Jiri Pokorny
 * @author    Brno University of Technology 
 */
/*
 * Copyright (c) 2012, Brno University of Technology 
 * Copyright (c) 2009, Atmel Corporation All rights reserved.
 *
 * Licensed under Atmel's Limited License Agreement --> EULA.txt
 */

/* === INCLUDES ============================================================ */

#include <stddef.h>
#include <stdint.h>
#include <stdlib.h>
#include <stdbool.h>
#include <inttypes.h>
#include "pal.h"
#include "tal.h"
#include "mac.h"
#include "mac_api.h"
#include "app_config.h"
#include "ieee_const.h"
#include "sio_handler.h"
#include <math.h>
#include "lcd.h"
#include <avr/pgmspace.h>
#include <util/delay.h>

/* === TYPES =============================================================== */

typedef struct prom_mode_payload_tag
{
    uint16_t fcf;
    uint8_t frame_type;
    uint8_t frame_length;
    uint8_t sequence_number;
    uint8_t dest_addr_mode;
    uint16_t dest_panid;
    address_field_t dest_addr;
    uint8_t src_addr_mode;
    uint16_t src_panid;
    address_field_t src_addr;
    uint8_t ppduLinkQuality;
	uint8_t ppduEdLevel;
    uint32_t timestamp;
    uint8_t payload_length;
    uint8_t *payload_data;
} prom_mode_payload_t;


typedef enum
{
  STATE_INITIAL,
  STATE_MENU,
  STATE_SCAN_MENU,
  STATE_SCAN_TIME_SET,
  STATE_GRAPHICAL_SCAN_SET,
  STATE_SCANNING,
  STATE_SCAN_DONE,
  STATE_TEXT_SCAN_DONE,
  STATE_SELECT_CHANNEL,
  STATE_ANALYZING, 
  STATE_ANALYZER_MENU,        
} State_t;

/* === MACROS ============================================================== */


/** Defines the default channel. */
#if (TAL_TYPE == AT86RF212)
    #ifdef CHINESE_BAND
        #define DEFAULT_CHANNEL                 (0)
        #define DEFAULT_CHANNEL_PAGE            (5)
    #else
        #define DEFAULT_CHANNEL                 (1)
        #define DEFAULT_CHANNEL_PAGE            (0)
    #endif  /* #ifdef CHINESE_BAND */
#else
#define DEFAULT_CHANNEL                 (20)
#define DEFAULT_CHANNEL_PAGE            (0)
#endif  /* #if (TAL_TYPE == AT86RF212) */
/* Constant define for the ED scaling: register value at -35dBm */
//#define CLIP_VALUE_REG                  (56)
#define LED_PERIOD                   (1000000)
#define LED_PERIOD_ANALYZE           (200000)
#define MAX_NODES					 (100) // maximum possible nodes when analyzing
/* === GLOBALS ============================================================= */

static uint32_t rx_frame_cnt;
static prom_mode_payload_t app_parse_data;
static uint8_t current_page;
static State_t state = STATE_INITIAL;
static uint8_t channel = 11;
static uint8_t menuitem;
//static bool analyze = false;
static bool key1pressed = false;
static bool key2pressed = false;
static bool key3pressed = false;
static bool key4pressed = false;
//static bool first_frame_patch;
//static uint8_t row;
static int8_t max_rssi;
static uint8_t scan_type;
static int8_t channels[16][2];
static uint8_t pos;
static uint8_t resolution = 20; //default graph resolution
static int16_t node[MAX_NODES][4];
static uint8_t node_size;
static bool onetime;
static uint16_t scan_channel_duration[11] = {1,2,5,10,30,60,120,300,600,1800,3600}; // default scan duration times per channel in seconds
static uint16_t	scan_duration;	
static uint16_t time;
static int8_t max_ed; // maximum ED energy
static bool valid;
static bool led_timer;

/* === PROTOTYPES ========================================================== */

static void process_frame(uint8_t *ptr_to_msdu, uint8_t len_msdu);
static void print_start_menu(void);
static void print_scan_menu(void);
#ifdef HIGH_DATA_RATE_SUPPORT
static uint8_t get_page(void);
#endif  /* HIGH_DATA_RATE_SUPPORT */
static void start_scan(uint8_t type);
static void graph(uint8_t channel, int8_t rssi);
static void select_channel(void);
static void channel_up(void);
static void channel_down(void);
static void channels_scan_cb(void);
static void led_blink_cb(void);
static void led_blink_an_cb(void);
static void go_to_next_channel(void);
static void print_list(void);
static void graphical_scan_set(void);
static void print_nodes(void);
static void print_analyzer_menu(void);
static void start_analyzer(void);
static void button_up(void);
static void button_down(void);
static void button_ok(void);
static void button_backlight(void);
static void rx_start(void);
static void scan_channel_duration_set(void);
static void scan_channel_duration_change(void);
static void time_cb(void);
static void get_ed_level(void);

/* === IMPLEMENTATION ====================================================== */


/**
 * @brief Main function
 */
int main(void)
{
   
    /* Initialize the MAC layer and its underlying layers, like PAL, TAL, BMM. */
    if (wpan_init() != MAC_SUCCESS)
    {
       
        pal_alert();
    }

    /* Initialize LEDs. */
    pal_led_init();

    /*
     * The stack is initialized above, hence the global interrupts are enabled
     * here.
     */
    pal_global_irq_enable();

    /* Initialize the serial interface used for communication with terminal program */
    if (pal_sio_init(SIO_CHANNEL) != MAC_SUCCESS)
    {
        // something went wrong during initialization
        pal_alert();
    }
	
	//LEDs 
	LED_PORT |= (1 << LED_PIN_0); //green on
    LED_PORT |= (0 << LED_PIN_1); //yellow off
    LED_PORT |= (1 << LED_PIN_2); //red
	
	/* Inicializace tlačítek */
	PORTE|=(1<<PE7);     // T1, pull up rezistor, log 1
	PORTE|=(1<<PE4);     // T2, pull up rezistor, log 1
	PORTB|=(1<<PB5);     // T3, pull up rezistor, log 1
	PORTE|=(1<<PE2);     // T4, pull up rezistor, log 1
	DDRE|=(1<<PE3);      // výstup na podsvětlení	
	PORTE|=(0<<PE3);     // podsvetleni vypnuto
	
	/* LCD init */
		
	lcd_init(LCD_DISP_ON);
	lcd_clrscr();
	lcd_gotoxy(0,1);
	lcd_puts("Channel Analyser");
	lcd_gotoxy(0,2);
	lcd_puts("is starting ...");
	_delay_ms(2000); //2s
	
	
	
    // custom LCD characters - vlastni znaky
	// full black box
	lcd_command(0x40); //set CGRAM address 
    lcd_data(0x00); //set the bits 
	lcd_data(0x00); 
	lcd_data(0x00); 
	lcd_data(0x00); 
	lcd_data(0x00); 
	lcd_data(0x00); 
	lcd_data(0x00); 
	lcd_data(0x1F); 
	
	// up arrow
    lcd_data(0x00); //set the bits 
	lcd_data(0x04); 
	lcd_data(0x0E); 
	lcd_data(0x15); 
	lcd_data(0x04); 
	lcd_data(0x04); 
	lcd_data(0x04); 
	lcd_data(0x00); 
	
	// down arrow
	lcd_data(0x00); //set the bits 
	lcd_data(0x04); 
	lcd_data(0x04); 
	lcd_data(0x04); 
	lcd_data(0x15); 
	lcd_data(0x0E); 
	lcd_data(0x04); 
	lcd_data(0x00); 
	
	// signal strength is larger than size of graph area
	lcd_data(0x4); //set the bits 
	lcd_data(0xe); 
	lcd_data(0x15); 
	lcd_data(0x4); 
	lcd_data(0x1f); 
	lcd_data(0x1f); 
	lcd_data(0x1f); 
	lcd_data(0x1f); 
	
	lcd_command(0x80); // back to DDRAM
	
	
	
	
#if ((!defined __ICCAVR__) && (!defined __ICCARM__) && (!defined __GNUARM__) && \
     (!defined __ICCAVR32__) && (!defined __GNUAVR32__))
    fdevopen(_sio_putchar, _sio_getchar);
#endif


    print_start_menu();


    /* Main loop */
    while (1)
    {
        //////////////////////////////////////////////////
	    // BUTTON 1 - UP
		//////////////////////////////////////////////////
		if(PINE & (1<<PE7))
        {
            //tlacitko neaktivni log1
			key1pressed = false;
        } 
		else  {
			_delay_ms(10); // de-bouncing, osetreni zakmitu
			if(PINE & (1<<PE7)){
				
			}
			else button_up();
		
		}
		//////////////////////////////////////////////////
		// BUTTON 2 - DOWN
		//////////////////////////////////////////////////
		if(PINE & (1<<PE4))
        {
            //tlacitko neaktivni log1
			key2pressed = false;
        } 
		else  {
			_delay_ms(10); // de-bouncing, osetreni zakmitu
			if(PINE & (1<<PE4)){
				
			}
			else button_down();
			
		
		}
		//////////////////////////////////////////////////
		// BUTTON 3 - OK
		//////////////////////////////////////////////////
		if(PINB & (1<<PB5))
        {
            //tlacitko neaktivni log1
			key3pressed = false;
        } 
		else  {
			_delay_ms(10); // de-bouncing, osetreni zakmitu
			if(PINB & (1<<PB5)){}
		    else button_ok();
			
		}
		//////////////////////////////////////////////////
		// BUTTON 4 - ZAPNUTI/VYPNUTI PODSVETLENI
		//////////////////////////////////////////////////
		if(PINE & (1<<PE2))
        {
            //tlacitko neaktivni log1
			key4pressed = false;
        } 
		else  {
			_delay_ms(10); // de-bouncing, osetreni zakmitu
			if(PINE & (1<<PE2)){}
			else button_backlight();
			
		}
		
	if(state==STATE_SCANNING) get_ed_level();		
		wpan_task();
		//pal_task();
        
    }
}

void button_up(void){
	if(!key1pressed){
				//tlacitko bylo zmacknuto log0
				if(state==STATE_SELECT_CHANNEL){
					channel_up();
				} else if(state == STATE_SCAN_TIME_SET){
					if(pos<10)pos++; // 10 is number of elements of scan_channel_duration -1
					else pos=0;
					scan_channel_duration_change();
				} else if(state==STATE_MENU || state==STATE_SCAN_MENU){
					if(menuitem==0){
						lcd_gotoxy(0,1);
						lcd_puts(" ");
						lcd_gotoxy(0,2);
						lcd_puts(">");
						menuitem=1;
					}
					else {
						lcd_gotoxy(0,1);
						lcd_puts(">");
						lcd_gotoxy(0,2);
						lcd_puts(" ");
						menuitem=0;
					}
				} else if(state == STATE_TEXT_SCAN_DONE){
					if(pos != 0) pos--;
					print_list();
				} else if(state == STATE_GRAPHICAL_SCAN_SET){
					if(resolution != 20) resolution=resolution+5;
					graphical_scan_set();
				} else if(state == STATE_ANALYZING){
					if(pos != 0 && !onetime) {
						pos--;
						print_nodes();
					}

					
				} else if(state == STATE_ANALYZER_MENU){
					if(menuitem==0){
						lcd_gotoxy(0,1);
						lcd_puts(" ");
						lcd_gotoxy(0,3);
						lcd_puts(">");
						menuitem=2;
					}
					else if(menuitem==1){
						lcd_gotoxy(0,1);
						lcd_puts(">");
						lcd_gotoxy(0,2);
						lcd_puts(" ");
						menuitem=0;
					} else {
						lcd_gotoxy(0,2);
						lcd_puts(">");
						lcd_gotoxy(0,3);
						lcd_puts(" ");
						menuitem=1;
					}
				}					 					 	
				key1pressed = true;
			}
			
}

void button_down(void){
if(!key2pressed){
				//tlacitko bylo zmacknuto log0
				if(state==STATE_SELECT_CHANNEL){
					channel_down();
				} else  if(state==STATE_MENU  || state==STATE_SCAN_MENU){
					if(menuitem==0){
						lcd_gotoxy(0,1);
						lcd_puts(" ");
						lcd_gotoxy(0,2);
						lcd_puts(">");
						menuitem=1;
					}
					else {
						lcd_gotoxy(0,1);
						lcd_puts(">");
						lcd_gotoxy(0,2);
						lcd_puts(" ");
						menuitem=0;
					}
				} else if(state == STATE_SCAN_TIME_SET){
					if(pos>0) pos--;
					else pos=10; // 11 is number of elements of scan_channel_duration
					scan_channel_duration_change();
				} else if(state == STATE_TEXT_SCAN_DONE){
					if(pos != 12) pos++;
					print_list();
					
				} else if(state == STATE_GRAPHICAL_SCAN_SET){
					if(resolution != 5) resolution=resolution-5;
					graphical_scan_set();
				} else if(state == STATE_ANALYZING){
					if(node_size > 3 && pos != node_size-3 && !onetime) {
						pos++;
						print_nodes();
					}
					
					
				} else if(state == STATE_ANALYZER_MENU){
					if(menuitem==0){
						lcd_gotoxy(0,1);
						lcd_puts(" ");
						lcd_gotoxy(0,2);
						lcd_puts(">");
						menuitem=1;
					}
					else if(menuitem==1){
						lcd_gotoxy(0,2);
						lcd_puts(" ");
						lcd_gotoxy(0,3);
						lcd_puts(">");
						menuitem=2;
					} else {
						lcd_gotoxy(0,3);
						lcd_puts(" ");
						lcd_gotoxy(0,1);
						lcd_puts(">");
						menuitem=0;
					}
				}							
				key2pressed = true;
			}
}

void button_ok(void){
if(!key3pressed){
				//tlacitko bylo zmacknuto log0
				if(state == STATE_MENU){
					
					if(menuitem==0){
						scan_channel_duration_set();
					}
					else {
						
						select_channel();
					}
					
			
				} else if(state== STATE_SCAN_TIME_SET){
					print_scan_menu();
				} else if(state == STATE_SCAN_MENU){
					if(menuitem==0){
						graphical_scan_set(); // menu for set graph resolution 
					}
					else {
						start_scan(1); // text scan
					}
					
				} else if(state == STATE_SELECT_CHANNEL){
					
					print_analyzer_menu();
					
				} else if(state == STATE_ANALYZER_MENU){
					
						start_analyzer();
					
				} else if(state==STATE_ANALYZING){
					print_start_menu();
				} else if(state == STATE_SCAN_DONE || state == STATE_TEXT_SCAN_DONE){
					print_start_menu();
				} else if(state == STATE_SCANNING){
					print_start_menu();
					pal_timer_stop(TIMER_SCAN); //stop timer
				} else if(state == STATE_GRAPHICAL_SCAN_SET){
					start_scan(0);
				}	
				
				key3pressed = true;
			}
}

void button_backlight(void){
if(!key4pressed){
				//tlacitko bylo zmacknuto log0
				PORTE^=(1<<PE3);
				key4pressed = true;
			}
}

//callback pro request scan
void usr_mlme_scan_conf(uint8_t status,
                        uint8_t ScanType,
                        uint8_t ChannelPage,
                        uint32_t UnscannedChannels,
                        uint8_t ResultListSize,
                        void *ResultList)
{
									


}

// callback for LED blink during scanning channels
void led_blink_cb(void){
	LED_PORT ^= (1 << LED_PIN_1); // yellow LED
	if(state==STATE_SCANNING)
	pal_timer_start(TIMER_LED_OFF,LED_PERIOD,TIMEOUT_RELATIVE,(FUNC_PTR)led_blink_cb,NULL); //start timer
	else LED_PORT &= ~(1 << LED_PIN_1);
}

// callback for LED blink if frame is received
void led_blink_an_cb(void){
	if(led_timer) {
		LED_PORT |= (1 << LED_PIN_1);
		led_timer=false;
		pal_timer_start(TIMER_LED_OFF,LED_PERIOD_ANALYZE,TIMEOUT_RELATIVE,(FUNC_PTR)led_blink_an_cb,NULL); //start timer
	} 
	else {
		LED_PORT &= ~(1 << LED_PIN_1);
		led_timer=true;
	}
		
}
	
	

void time_cb(void){
	if(time<65535)
	time++;
	else {
		time=0;
		for(uint8_t i = 0;i<node_size;i++){
		
			node[i][3] = 0;
		}
	}
	if(state==STATE_ANALYZING && onetime==false) {
		pal_timer_start(TIMER_SCAN,1000000,TIMEOUT_RELATIVE,(FUNC_PTR)time_cb,NULL); //start timer
		print_nodes();
	}		
	
}

void start_scan(uint8_t type)  { 
	
	for(uint8_t i=0;i<16;i++){
		channels[i][1]=0; // reset validity of channels
	}
	channel = 11;
	rx_start();
	max_rssi = -92;
	max_ed = -92;
	valid=false;
	state = STATE_SCANNING; // change current state
	
	
	pal_timer_start(TIMER_SCAN,scan_duration*1000000,TIMEOUT_RELATIVE,(FUNC_PTR)channels_scan_cb,NULL); //start timer
	pal_timer_start(TIMER_LED_OFF,LED_PERIOD,TIMEOUT_RELATIVE,(FUNC_PTR)led_blink_cb,NULL); //start timer
	scan_type = type; // 0 = graphical, 1 = text list
	pos = 0; // default start position for channel list
    lcd_clrscr();
	
	
} 

// callback function for scan channels
void channels_scan_cb(void){
	if(scan_type == 0){ // graphical scan
		graph(channel,max_rssi); // draws graph for current channel
		go_to_next_channel();
	}
	else { // text list scan
		
		uint8_t index = channel-11;
		if(valid)	{
			channels[index][0] = max_rssi;
			channels[index][1] = 1; // MAC valid frame
		}
		else channels[index][0] = max_ed;
		if(channel>=15) pos++;
		print_list(); // print results of one channel scan
		go_to_next_channel();
	}	
}

// function for increment channel number in state SCAN
void go_to_next_channel(void){
	
	channel++; // increment of channel
	if(channel<=26){
		while(!wpan_mlme_set_req(phyCurrentChannel, &channel)) wpan_mlme_set_req(phyCurrentChannel, &channel); //set new channel
		max_ed = -92; // reset value
		max_rssi = -92; // reset value
		valid=false; // reset validity of 802.15.4
		pal_timer_start(TIMER_SCAN,scan_duration*1000000,TIMEOUT_RELATIVE,(FUNC_PTR)channels_scan_cb,NULL); //start timer
		
	}
	else {
		channel = 26;
		if(scan_type == 0) {state = STATE_SCAN_DONE;} // end of scan
		else {state = STATE_TEXT_SCAN_DONE;} // end of scan
		bool promiscuous_mode = false;
		wpan_mlme_set_req(macPromiscuousMode, &promiscuous_mode);
		bool rx_on_when_idle = false;
		wpan_mlme_set_req(macRxOnWhenIdle, &rx_on_when_idle);	
	}
}

void graphical_scan_set(void){
	
	state = STATE_GRAPHICAL_SCAN_SET;
	lcd_clrscr();
	lcd_puts("Set resolution");
	lcd_gotoxy(0,1);
	lcd_putnum(resolution);
	lcd_gotoxy(3,1);
	lcd_puts("dBm/div");
	lcd_gotoxy(0,3);
	lcd_puts("Press ");
	lcd_gotoxy(6,3);
	lcd_data(0x01);
	lcd_puts(" or ");
	lcd_data(0x02);
	lcd_puts(", OK");
	
}

// function for plot graph
void graph(uint8_t channel, int8_t rssi){
	uint8_t xpos = channel-11;
	lcd_gotoxy(xpos,3);
	if(rssi==-92) {lcd_data(0x00);} // signal below -91, print _
	else if(rssi<-91+resolution){ // print 1 segment
		
		lcd_data(0xFF);
	}
	else if(rssi<-91+2*resolution){ // print 2 segments
		
		lcd_data(0xFF);
		lcd_gotoxy(xpos,2);
		lcd_data(0xFF);
	}
	else if(rssi<-91+3*resolution){ // print 3 segments
		
		lcd_data(0xFF);
		lcd_gotoxy(xpos,2);
		lcd_data(0xFF);
		lcd_gotoxy(xpos,1);
		lcd_data(0xFF);
	}
	else if(rssi<-91+4*resolution){ // print 4 segments
		
		lcd_data(0xFF);
		lcd_gotoxy(xpos,2);
		lcd_data(0xFF);
		lcd_gotoxy(xpos,1);
		lcd_data(0xFF);
		lcd_gotoxy(xpos,0);
		lcd_data(0xFF);
	}
	else {						// signal is higher than size of display
		lcd_data(0xFF);
		lcd_gotoxy(xpos,2);
		lcd_data(0xFF);
		lcd_gotoxy(xpos,1);
		lcd_data(0xFF);
		lcd_gotoxy(xpos,0);
		lcd_data(0x03);
	}
	
}

void print_list(void){ // print list of scanned channels
	uint8_t r = 0;
	if(pos == 0 && state != STATE_TEXT_SCAN_DONE){
		r = channel-11;
		lcd_gotoxy(0,r);
		lcd_puts("Ch");
		lcd_putnum(channel);
		lcd_gotoxy(5,r);
		lcd_puts("           ");
		lcd_gotoxy(5,r);
		if(channels[r][1] == 1) {
			lcd_putnum(channels[r][0]);
			lcd_puts("dBm");
			lcd_puts(" 15.4");
		} else if(channels[r][0]!=-92){	
			lcd_putnum(channels[r][0]);
			lcd_puts("dBm");
		} else {
			lcd_puts("<-91dBm");}
		
	} else for(uint8_t i = pos;i<pos+4;i++){
		lcd_gotoxy(0,r);
		lcd_puts("Ch");
		lcd_putnum(i+11);
		lcd_gotoxy(5,r);
		lcd_puts("           ");
		lcd_gotoxy(5,r);
		if(channels[i][1] == 1) {
			lcd_putnum(channels[i][0]);
			lcd_puts("dBm");
			lcd_puts(" 15.4");
		}
		else if(channels[i][0]!=-92){	
			lcd_putnum(channels[i][0]);
			lcd_puts("dBm");
		} else {
			lcd_puts("<-91dBm");}
		r++;	
		}
		
}
	
	


void print_nodes(void){
	
	if(onetime){
		lcd_clrscr();
		// prints header Addr, RSSI, LQI
		lcd_puts("Addr");
		lcd_gotoxy(8,0);
		lcd_puts("RSSI");
		lcd_gotoxy(13,0);
		lcd_puts("LQI");
		onetime=false;
		pal_timer_start(TIMER_SCAN,1000000,TIMEOUT_RELATIVE,(FUNC_PTR)time_cb,NULL); //start timer for counting time in seconds
	}
	uint8_t r = 1;
	
	
	if(node_size<3){
		for(uint8_t i = 0;i<node_size;i++){
			lcd_gotoxy(0,r);
			lcd_put16hex(node[i][0]); // addr
			if(node[i][3]+60<=time){  // node has got old time stamp, print X
				lcd_gotoxy(7,r);
				lcd_puts("X");
			} else {
				lcd_gotoxy(7,r);
				lcd_puts(" ");
			}
			if(node[i][1]>-10){
				lcd_gotoxy(9,r);
				lcd_puts("   ");
			}
			
			lcd_gotoxy(9,r);
			lcd_putnum(node[i][1]); // rssi
			lcd_gotoxy(13,r);
			if(node[i][2]<10){
				lcd_puts("  ");
			} else if(node[i][2]<100) {
				lcd_puts(" ");
			} 
			lcd_putnum(node[i][2]); // lqi
			r++;
		}
	} else {
		
		for(uint8_t i = pos;i<pos+3;i++){
			lcd_gotoxy(0,r);
			lcd_put16hex(node[i][0]);
			if(node[i][3]+60<=time){ // node has got old time stamp, print X
				lcd_gotoxy(7,r);
				lcd_puts("X");
			} else {
				lcd_gotoxy(7,r);
				lcd_puts(" ");
			}
			if(node[i][1]>-10){
				lcd_gotoxy(9,r);
				lcd_puts("   ");
			}
			lcd_gotoxy(9,r);
			lcd_putnum(node[i][1]); // RSSI
			lcd_gotoxy(13,r);
			if(node[i][2]<10){  // LQI
				lcd_puts("  ");
			} else if(node[i][2]<100) {
				lcd_puts(" ");
			} 
			lcd_putnum(node[i][2]); // lqi
			r++;
		}
		
		
	}
	
}

// function for print channel select menu
void select_channel(void){
	state = STATE_SELECT_CHANNEL;
	channel = 11;
	lcd_clrscr();
	lcd_puts("Select channel:");
	lcd_gotoxy(0,1);
	lcd_putnum(channel);
	lcd_gotoxy(0,3);
	lcd_puts("Press ");
	lcd_gotoxy(6,3);
	lcd_data(0x01);
	lcd_puts(" or ");
	lcd_data(0x02);
	lcd_puts(", OK");
}

// function for change channel up in state channel_select
void channel_up(void){
	if(channel<26) channel++;
	else channel = 11;
	lcd_gotoxy(0,1);
	lcd_puts("  ");
	lcd_gotoxy(0,1);
	lcd_putnum(channel);
}
// function for change channel down in state channel_select
void channel_down(void){
	if(channel>11) channel--;
	else channel = 26;
	lcd_gotoxy(0,1);
	lcd_puts("  ");
	lcd_gotoxy(0,1);
	lcd_putnum(channel);
}
/**
 * @brief Callback function usr_mlme_reset_conf
 *
 * @param status Result of the reset procedure
 */
void usr_mlme_reset_conf(uint8_t status)
{
    if (status == MAC_SUCCESS)
    {
        /* Get Current Channel Page. */
        wpan_mlme_get_req(phyCurrentPage);
    }
    else
    {
        // something went wrong; restart
        wpan_mlme_reset_req(true);
    }
}





/**
 * @brief Callback function usr_mlme_set_conf
 *
 * @param status        Result of requested PIB attribute set operation
 * @param PIBAttribute  Updated PIB attribute
 */
void usr_mlme_set_conf(uint8_t status, uint8_t PIBAttribute)
{
    if ((status == MAC_SUCCESS) && (PIBAttribute == phyCurrentPage))
    {
        
        /* Get Current Channel. */
        wpan_mlme_get_req(phyCurrentChannel);

    }
    else if ((status == MAC_SUCCESS) && (PIBAttribute == phyCurrentChannel))
    {

        /*
         * Set RX on when idle to enable the receiver in promiscuous mode.
         * Use: bool wpan_mlme_set_req(uint8_t PIBAttribute,
         *                             void *PIBAttributeValue);
         *
         * This request leads to a set confirm message -> usr_mlme_set_conf
         */
		 
         bool rx_on_when_idle = true;

         wpan_mlme_set_req(macRxOnWhenIdle, &rx_on_when_idle);
		  	 
    }
    else if ((status == MAC_SUCCESS) && (PIBAttribute == macRxOnWhenIdle))
    {
        /*
         * Set promiscuous mode.
         * Use: bool wpan_mlme_set_req(uint8_t PIBAttribute,
         *                             void *PIBAttributeValue);
         *
         * This request leads to a set confirm message -> usr_mlme_set_conf
       / */
         if(state!=STATE_MENU && state!=STATE_TEXT_SCAN_DONE && state!=STATE_SCAN_DONE){
		 bool promiscuous_mode = true;
         wpan_mlme_set_req(macPromiscuousMode, &promiscuous_mode);
		 }
		 
    } 
    
}



/**
 * @brief Callback function usr_mlme_get_conf
 *
 * @param status            Result of requested PIB attribute set operation
 * @param PIBAttribute      Updated PIB attribute
 * @param PIBAttributeValue Pointer to data containing retrieved PIB attribute
 */
void usr_mlme_get_conf(uint8_t status,
                       uint8_t PIBAttribute,
                       void *PIBAttributeValue)
{
    if ((status == MAC_SUCCESS) && (PIBAttribute == phyCurrentPage))
    {

        current_page = DEFAULT_CHANNEL_PAGE;


        wpan_mlme_set_req(phyCurrentPage, &current_page);
    }
    else if ((status == MAC_SUCCESS) && (PIBAttribute == phyCurrentChannel))
    {

        wpan_mlme_set_req(phyCurrentChannel, &channel);
    }
	
}



/**
 * @brief Callback function usr_mcps_data_ind
 *
 * @param SrcAddrSpec      Pointer to source address specification
 * @param DstAddrSpec      Pointer to destination address specification
 * @param msduLength       Number of octets contained in MSDU
 * @param msdu             Pointer to MSDU
 * @param mpduLinkQuality  LQI measured during reception of the MPDU
 * @param mpduEdLevel      ED level
 * @param DSN              DSN of the received data frame.
 * @param Timestamp        The time, in symbols, at which the data were received.
 *                         (only if timestamping is enabled).
 */
void usr_mcps_data_ind(wpan_addr_spec_t *SrcAddrSpec,
                       wpan_addr_spec_t *DstAddrSpec,
                       uint8_t msduLength,
                       uint8_t *msdu,
                       uint8_t mpduLinkQuality,
					   uint8_t mpduEdLevel, //pocky
#ifdef ENABLE_TSTAMP
                       uint8_t DSN,
                       uint32_t Timestamp)
#else
                       uint8_t DSN)
#endif  /* ENABLE_TSTAMP */
{
    uint8_t *payload_ptr = msdu;

    /* Update rx counter. */
    rx_frame_cnt++;

    /*
     * The relevant information in promiscuous mode is in the payload (*msdu)
     * of this callback. This payload contains the MHR of the original frame
     * and the payload of the original frame.
     * See IEEE 802.15.4-2006 section 7.5.6.5 Promiscuous mode.
     */

    /*
     * Now start parsing the received payload to get the elements of the
     * MHR of the frame.
     */
    app_parse_data.fcf = convert_byte_array_to_16_bit(payload_ptr);

    /*
     * MAC is maintained in Little endian stack and in order to get the fcf
     * we need to convert the endianess for further check
     */
    app_parse_data.fcf = CPU_ENDIAN_TO_LE16(app_parse_data.fcf);

    app_parse_data.frame_type = FCF_GET_FRAMETYPE(app_parse_data.fcf);
    app_parse_data.frame_length = msduLength;

    app_parse_data.dest_addr_mode = FCF_GET_DEST_ADDR_MODE(app_parse_data.fcf);
    app_parse_data.src_addr_mode = FCF_GET_SOURCE_ADDR_MODE(app_parse_data.fcf);

    payload_ptr += 3;// zacatek AddressingFields, sizeof(app_parse_data.fcf);
    
    if (app_parse_data.dest_addr_mode != 0)
    {
        app_parse_data.dest_panid = convert_byte_array_to_16_bit(payload_ptr);
        payload_ptr += sizeof(app_parse_data.dest_panid);

        if (FCF_SHORT_ADDR == app_parse_data.dest_addr_mode)
        {
            app_parse_data.dest_addr.short_address = convert_byte_array_to_16_bit(payload_ptr);
            payload_ptr += 2;   // Length of short address
        }
        else if (FCF_LONG_ADDR == app_parse_data.dest_addr_mode)
        {
            app_parse_data.dest_addr.long_address = convert_byte_array_to_64_bit(payload_ptr);
            payload_ptr += 8;   // Length of extended address
        }
    }

    if (app_parse_data.src_addr_mode != 0)
    {
        if (!(app_parse_data.fcf & FCF_PAN_ID_COMPRESSION))
        {
            /*
             * Source PAN ID is present in the frame only if the intra-PAN bit
             * is zero and src_address_mode is non zero.
             */
            app_parse_data.src_panid = convert_byte_array_to_16_bit(payload_ptr);
            payload_ptr += sizeof(app_parse_data.src_panid);
        }
        else
        {
            /*
             * The received frame does not contain a source PAN ID, hence
             * source PAN ID of the frame_info_t is updated with the
             * destination PAN ID.
             */
            app_parse_data.src_panid = app_parse_data.dest_panid;
        }

        /* The frame_info_t structure is updated with the source address. */
        if (FCF_SHORT_ADDR == app_parse_data.src_addr_mode)
        {
            app_parse_data.src_addr.short_address = convert_byte_array_to_16_bit(payload_ptr);
            payload_ptr += 2;   // Length of short address
        }
        else if (FCF_LONG_ADDR == app_parse_data.src_addr_mode)
        {
            app_parse_data.src_addr.long_address = convert_byte_array_to_64_bit(payload_ptr);
            payload_ptr += 8;   // Length of extended address
        }
    } else return; // non valid node address
	
	
    app_parse_data.sequence_number = DSN;

    app_parse_data.payload_length = msduLength - (payload_ptr - msdu);

    app_parse_data.ppduLinkQuality = mpduLinkQuality;
	app_parse_data.ppduEdLevel = mpduEdLevel;
#ifdef ENABLE_TSTAMP
    app_parse_data.timestamp = Timestamp;
#endif
    app_parse_data.payload_data = payload_ptr;

	
     if(FCF_FRAMETYPE_ACK != app_parse_data.frame_type){
	   
		// process valid frame only in one of these states
		if(state==STATE_SCANNING || state==STATE_ANALYZING) process_frame(msdu, msduLength);
	 
	 
	}
   
   

    /* Keep compiler happy. */
    SrcAddrSpec = SrcAddrSpec;
    DstAddrSpec = DstAddrSpec;
}




/**
 * @brief Parse the received MCPS-DATA.indication message for the contained frame
 */
static void process_frame(uint8_t *ptr_to_msdu, uint8_t len_msdu)
{
	
	
	valid = true; // valid frame
	int8_t an_rssi =  -91+app_parse_data.ppduEdLevel; //get RSSI value
	
	if(state == STATE_SCANNING){ // channels scan
		if(an_rssi>max_rssi) max_rssi = an_rssi; // store strongest RSSI value
		
	}
	else { // channel analyzing
	
	if(led_timer){
		pal_timer_start(TIMER_LED_OFF,LED_PERIOD_ANALYZE,TIMEOUT_RELATIVE,(FUNC_PTR)led_blink_an_cb,NULL); //start timer
	}
		
	
		//uint16_t an_panid = app_parse_data.src_panid; 
	uint16_t an_srcaddr = app_parse_data.src_addr.short_address;
	uint8_t an_lqi = app_parse_data.ppduLinkQuality;
	
	bool existing_node = false;
	uint8_t i;
	uint8_t j;
	uint16_t tmp1;
	int8_t tmp2;
	uint8_t tmp3;
	uint16_t tmp4;
	
	// node is already node array - update data
	for(i = 0;i<node_size;i++){
		if(node[i][0]==an_srcaddr) {
			existing_node = true;
			node[i][1] = an_rssi;
			node[i][2] = an_lqi;
			node[i][3] = time;
			break;
		}
	}
	// new node
	if(existing_node == false && node_size<MAX_NODES){
		node[node_size][0] = an_srcaddr;
		node[node_size][1] = an_rssi;
		node[node_size][2] = an_lqi;
		node[node_size][3] = time;
		node_size++;
	}
	
	if(menuitem==1){
		
	// insertion sort ascending ADDR
	for (i = 0; i < node_size - 1; i++) {
		j = i + 1;
		tmp1 = node[j][0]; // addr
		tmp2 = node[j][1]; // rssi
		tmp3 = node[j][2]; // lqi
		tmp4 = node[j][3]; // time
		while (j > 0 && node[j-1][0] > tmp1) {
			node[j][0] = node[j-1][0];
			node[j][1] = node[j-1][1];
			node[j][2] = node[j-1][2];
			node[j][3] = node[j-1][3];
			j--;
		}
		node[j][0] = tmp1;
		node[j][1] = tmp2;
		node[j][2] = tmp3;
		node[j][3] = tmp4;
	}
	} else if(menuitem==2){
		
	// insertion sort descending RSSI
	for (i = 0; i < node_size - 1; i++) {
		j = i + 1;
		tmp1 = node[j][0]; // addr
		tmp2 = node[j][1]; // rssi
		tmp3 = node[j][2]; // lqi
		tmp4 = node[j][3]; // time
		while (j > 0 && tmp2 > node[j-1][1]) {
			node[j][0] = node[j-1][0];
			node[j][1] = node[j-1][1];
			node[j][2] = node[j-1][2];
			node[j][3] = node[j-1][3];
			j--;
		}
		node[j][0] = tmp1;
		node[j][1] = tmp2;
		node[j][2] = tmp3;
		node[j][3] = tmp4;
	}
	}

	print_nodes();
	
	}
	

	                   
}



/**
 * @brief Print start menu
 */
static void print_start_menu(void)
{
	
	// SLEEP MODE
	if(state==STATE_ANALYZING || state==STATE_SCANNING){
		bool promiscuous_mode = false;
		wpan_mlme_set_req(macPromiscuousMode, &promiscuous_mode);
		bool rx_on_when_idle = false;
		wpan_mlme_set_req(macRxOnWhenIdle, &rx_on_when_idle);	
	}
	
	state = STATE_MENU;
	
	LED_PORT &= ~(1 << LED_PIN_1); // yellow LED off
	
	node_size=0; // reset node table
	menuitem = 0; // default menu position 
	lcd_clrscr();
    lcd_puts("Main menu");
	lcd_gotoxy(0,1);
	lcd_puts(">Channels scan");
	lcd_gotoxy(0,2);
	lcd_puts(" Channel select");
	lcd_gotoxy(0,3);
	lcd_puts("Press ");
	lcd_gotoxy(6,3);
	lcd_data(0x01);
	lcd_puts(" or ");
	lcd_data(0x02);
	lcd_puts(", OK");
	

}

void print_scan_menu(void)
{
   
	state = STATE_SCAN_MENU;   
    menuitem = 0;
	lcd_clrscr();
    lcd_puts("Scan menu");
	lcd_gotoxy(0,1);
	lcd_puts(">Graphical");
	lcd_gotoxy(0,2);
	lcd_puts(" Text list");
	lcd_gotoxy(0,3);
	lcd_puts("Press ");
	lcd_gotoxy(6,3);
	lcd_data(0x01);
	lcd_puts(" or ");
	lcd_data(0x02);
	lcd_puts(", OK");
	
	
}

void print_analyzer_menu(void)
{
    state = STATE_ANALYZER_MENU;
	menuitem = 0;
	lcd_clrscr();
    lcd_puts("Sort settings");
	lcd_gotoxy(0,1);
	lcd_puts(">None");
	lcd_gotoxy(0,2);
	lcd_puts(" Node address");
	lcd_gotoxy(0,3);
	lcd_puts(" RSSI");
	
	
	
}

void start_analyzer(void){
	
	
	time = 0; // actual time set to 0s
	pos=0;
	onetime = true; // clear screen once
	led_timer=true;
	rx_start(); // wakeup
	state = STATE_ANALYZING;
	
	lcd_clrscr();
	lcd_gotoxy(0,1);
	lcd_puts("Searching for");
	lcd_gotoxy(0,2);
	lcd_puts("valid frames..");
}

// function for wakeup
void rx_start(void){
	
	/*
     * Reset the MAC layer to the default values
     * This request will cause a mlme reset confirm message ->
     * usr_mlme_reset_conf
     */
    wpan_mlme_reset_req(true);
    //while(!wpan_mlme_set_req(phyCurrentChannel, &channel)) wpan_mlme_set_req(phyCurrentChannel, &channel); // start RX
	
	
}

// function to set scan duration time of channel
void scan_channel_duration_set(void){
	state=STATE_SCAN_TIME_SET;
	pos=1; // second position of scan_channel_duration[] = 2s
	lcd_clrscr();
	lcd_puts("Scan time per");
	lcd_gotoxy(0,1);
	lcd_puts("channel: ");
	if(scan_channel_duration[pos]<60){ // time in seconds
		lcd_putnum(scan_channel_duration[pos]);
	    lcd_puts(" s");
	} else { // time in minutes
		lcd_putnum(scan_channel_duration[pos]/60);
	    lcd_puts(" min");
	}
	lcd_gotoxy(0,3);
	lcd_puts("Press ");
	lcd_gotoxy(6,3);
	lcd_data(0x01);
	lcd_puts(" or ");
	lcd_data(0x02);
	lcd_puts(", OK");
    scan_duration = scan_channel_duration[pos];
}

// function to change scan duration time of channel
void scan_channel_duration_change(void){
	lcd_gotoxy(9,1);
	lcd_puts("      "); // clear old values
	lcd_gotoxy(9,1);
	if(scan_channel_duration[pos]<60){ // time in seconds
		lcd_putnum(scan_channel_duration[pos]);
	    lcd_puts(" s");
	} else { // time in minutes
		lcd_putnum(scan_channel_duration[pos]/60);
	    lcd_puts(" min");
	}
	scan_duration = scan_channel_duration[pos];
}

// function to get max energy of current channel
void get_ed_level(void){
    
    int8_t ed_val = pal_trx_bit_read(RG_PHY_RSSI,0x1F,0); // read actual RSSI from register 
    ed_val = -91 + 3*(ed_val-1); // convert value to dBm
	if(ed_val!=-1) { // bits 4:0 of PHY_RSSI are sometimes 11111 .... nonsense, because max value is 28 dec, not 31
    
		if (ed_val > max_ed) // store highest value during measurement of one channel
		{
			max_ed = ed_val;
		}	
	}		
}	


/* EOF */
