/*******************************************************************************
 * THE FOLLOWING EXAMPLE CODE IS INTENDED FOR LIMITED CIRCULATION ONLY.
 * 
 * Please forward all questions regarding this code to ANT Technical Support.
 * 
 * Dynastream Innovations Inc.
 * 
 * (P) +1 403 932 9292
 * (F) +1 403 932 4196
 * (E) support@thisisant.com
 * 
 * www.thisisant.com
 *
 * Reference Design Disclaimer
 *
 * The references designs and codes provided may be used with ANT devices only and remain the copyrighted property of 
 * Dynastream Innovations Inc. The reference designs and codes are being provided on an "as-is" basis and as an accommodation, 
 * and therefore all warranties, representations, or guarantees of any kind (whether express, implied or statutory) including, 
 * without limitation, warranties of merchantability, non-infringement,
 * or fitness for a particular purpose, are specifically disclaimed.
 *
 * �2012 Dynastream Innovations Inc. All Rights Reserved
 * This software may not be reproduced by
 * any means without express written approval of Dynastream
 * Innovations Inc.
 *
 *******************************************************************************/
 


#include "StdAfx.h"
#include "BikeSpdCadDisplay.h"

/**************************************************************************
 * BikeSpdCadDisplay::ANT_eventNotification
 * 
 * Process ANT channel event
 *
 * ucEventCode_: code of ANT channel event
 * pucEventBuffer_: pointer to buffer containing data received from ANT,
 *		or a pointer to the transmit buffer in the case of an EVENT_TX
 * 
 * returns: N/A
 *
 **************************************************************************/
void BikeSpdCadDisplay::ANT_eventNotification(UCHAR ucEventCode_, UCHAR* pucEventBuffer_)
{
	switch(ucEventCode_)
	{
		case EVENT_RX_ACKNOWLEDGED:
		case EVENT_RX_BURST_PACKET: // intentional fall thru
		case EVENT_RX_BROADCAST:
			HandleReceive((UCHAR*) pucEventBuffer_);
			UpdateDisplay();
			break;
		default:
			break;
	}
}


/**************************************************************************
 * BikeSpdCadDisplay::InitializeSim
 * 
 * Initializes simulator variables
 * 
 * returns: N/A
 *
 **************************************************************************/
void BikeSpdCadDisplay::InitializeSim()
{
	eState = BSC_INIT;
	ucWheelCircumference = System::Convert::ToByte(this->numericUpDown_Sim_WheelCircumference->Value);  // Initial value set on UI
	usCadEventCount = 0;
	usCadPreviousEventCount = 0;
	usCadTime1024 = 0;	
	usCadPreviousTime1024 = 0;
	ulCadAcumEventCount = 0;
	ulCadAcumTime1024 = 0;	
	usSpdEventCount = 0;
	usSpdPreviousEventCount = 0;
	usSpdTime1024 = 0;			
	usSpdPreviousTime1024 = 0;
	ulSpdAcumEventCount = 0;
	ulSpdAcumTime1024 = 0;
	ucCadence = 0;
	ulSpeed = 0;
	ulDistance = 0;
	bStop = FALSE;
	bCoast = FALSE;
}


/**************************************************************************
 * BikeSpdCadDisplay::HandleReceive
 * 
 * Decodes received data
 *
 * pucRxBuffer_: pointer to the buffer containing the received data
 * 
 * returns: N/A
 *
 **************************************************************************/
void BikeSpdCadDisplay::HandleReceive(UCHAR* pucRxBuffer_)
{
	static UCHAR ucNoSpdEventCount = 0;	// Counter for successive transmissions with no new speed events
	static UCHAR ucNoCadEventCount = 0;	// Counter for successive transmissions with no new cadence events
	USHORT usEventDiff = 0;
	USHORT usTimeDiff1024 = 0;

	// Decode data page
	usCadTime1024 = pucRxBuffer_[0];
	usCadTime1024 += pucRxBuffer_[1] <<8;
	usCadEventCount = pucRxBuffer_[2];
	usCadEventCount += pucRxBuffer_[3] <<8;
	usSpdTime1024 = pucRxBuffer_[4];
	usSpdTime1024 += pucRxBuffer_[5] <<8;
	usSpdEventCount = pucRxBuffer_[6];
	usSpdEventCount += pucRxBuffer_[7] <<8;

	// Initialize previous values on first message received
	if(eState == BSC_INIT)
	{
		usCadPreviousTime1024 = usCadTime1024;
		usCadPreviousEventCount = usCadEventCount;
		usSpdPreviousTime1024 = usSpdTime1024;
		usSpdPreviousEventCount = usSpdEventCount;
		eState = BSC_ACTIVE;
	}

	// Update cadence calculations on new cadence event
	if(usCadEventCount != usCadPreviousEventCount)
	{
		ucNoCadEventCount = 0;
		bCoast = FALSE;
		// Update cumulative event count
		if(usCadEventCount > usCadPreviousEventCount)
			usEventDiff = usCadEventCount - usCadPreviousEventCount;
		else
			usEventDiff = (USHORT) (0xFFFF - usCadPreviousEventCount + usCadEventCount + 1);
		ulCadAcumEventCount += usEventDiff;

		// Update cumulative time (1/1024s)
		if(usCadTime1024 > usCadPreviousTime1024)
			usTimeDiff1024 = usCadTime1024 - usCadPreviousTime1024;
		else
			usTimeDiff1024 = (USHORT) (0xFFFF - usCadPreviousTime1024 + usCadTime1024 + 1);
		ulCadAcumTime1024 += usTimeDiff1024;

		// Calculate cadence (rpm)
		if(usTimeDiff1024 > 0)
			ucCadence = (UCHAR) ( ((ULONG) usEventDiff * 0xF000) / (ULONG) usTimeDiff1024 );	// 1 min = 0xF000 = 60 * 1024 		
	}
	else
	{
		ucNoCadEventCount++;
		if(ucNoCadEventCount >= MAX_NO_EVENTS)
			bCoast = TRUE;	// Coasting
	}

	// Update speed calculations on new speed event
	if(usSpdEventCount != usSpdPreviousEventCount)
	{
		ucNoSpdEventCount = 0;
		bStop = FALSE;
		// Update cumulative event count
		if(usSpdEventCount > usSpdPreviousEventCount)
			usEventDiff = usSpdEventCount - usSpdPreviousEventCount;
		else
			usEventDiff = (USHORT) (0xFFFF - usSpdPreviousEventCount + usSpdEventCount + 1);
		ulSpdAcumEventCount += usEventDiff;

		// Update cumulative time (1/1024s)
		if(usSpdTime1024 > usSpdPreviousTime1024)
			usTimeDiff1024 = usSpdTime1024 - usSpdPreviousTime1024;
		else
			usTimeDiff1024 = (USHORT) (0xFFFF - usSpdPreviousTime1024 + usSpdTime1024 + 1);
		ulSpdAcumTime1024 += usTimeDiff1024;

		// Calculate speed (meters/h)
		if(ucWheelCircumference)
			ulSpeed = (ucWheelCircumference * 0x9000 * (ULONG) usEventDiff) / (ULONG) usTimeDiff1024;	// 1024 * 36 = 0x9000

		// Calculate distance (cm)
		ulDistance = (ULONG) ucWheelCircumference * ulSpdAcumEventCount;
	}
	else
	{
		ucNoSpdEventCount++;
		if(ucNoSpdEventCount >= MAX_NO_EVENTS)
			bStop = TRUE;	// Stopping
	}

	// Update previous values
	usCadPreviousTime1024 = usCadTime1024;
	usCadPreviousEventCount = usCadEventCount;
	usSpdPreviousTime1024 = usSpdTime1024;
	usSpdPreviousEventCount = usSpdEventCount;
}

/**************************************************************************
 * BikeSpdCadDisplay::UpdateDisplay
 * 
 * Shows received decoded data on GUI
 *
 * ucPageNum_: received page
 *
 * returns:  N/A
 *
 **************************************************************************/
void BikeSpdCadDisplay::UpdateDisplay()
{	
	// Display received data
	this->label_Trn_CadenceTimeDisplay->Text = System::Convert::ToString(usCadTime1024);
	this->label_Trn_CadCountDisplay->Text = System::Convert::ToString(usCadEventCount);
	this->label_Trn_SpeedTimeDisplay->Text = System::Convert::ToString(usSpdTime1024);
	this->label_Trn_SpdCountDisplay->Text = System::Convert::ToString(usSpdEventCount);

	// Display calculations
	this->label_Calc_CadenceDisplay->Text = System::Convert::ToString(ucCadence);
	this->label_Calc_CadEventCountDisplay->Text = System::Convert::ToString((unsigned int) ulCadAcumEventCount);
	this->label_Calc_SpdEventCountDisplay->Text = System::Convert::ToString((unsigned int) ulSpdAcumEventCount);
	this->label_Calc_SpeedDisplay->Text = System::Convert::ToString((double) ulSpeed/1000);	 // meters/h -> km/h
	this->label_Calc_DistanceDisplay->Text = System::Convert::ToString(System::Math::Round((double) ulDistance/100000, 3));	// cm -> km
	// Most recent available time
	if(ulSpdAcumTime1024 < ulCadAcumTime1024) 
		this->label_Calc_ElapsedSecsDisplay->Text = System::Convert::ToString(System::Math::Round((double) ulCadAcumTime1024/1024,3));	// 1/1024 s -> s
	else
		this->label_Calc_ElapsedSecsDisplay->Text = System::Convert::ToString(System::Math::Round((double) ulSpdAcumTime1024/1024,3));	// 1/1024 s -> s
	// Stopping and coasting detected
	this->label_Coasting->Visible = (bCoast == TRUE && bStop == FALSE);	// Coasting not displayed if bike has stopped
	this->label_Stopped->Visible = (bStop == TRUE);
}


/**************************************************************************
 * BikeSpdCadDisplay::numericUpDown_Sim_WheelCircumference_ValueChanged
 * 
 * Updates wheel circumference value, if updated (either by the user or internally)
 * Validation is already performed by the numericUpDown control
 *
 * returns:  N/A
 *
 **************************************************************************/
System::Void BikeSpdCadDisplay::numericUpDown_Sim_WheelCircumference_ValueChanged(System::Object^  sender, System::EventArgs^  e)
{
	ucWheelCircumference = System::Convert::ToByte(this->numericUpDown_Sim_WheelCircumference->Value);
}