/* This software is subject to the license described in the License.txt file included with this software distribution. You may not use this file except in compliance with this license. Copyright (c) Dynastream Innovations Inc. 2012 All rights reserved. */ #include "StdAfx.h" #include "BikeSpdCadSensor.h" /************************************************************************** * BikeSpdCadSensor::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 BikeSpdCadSensor::ANT_eventNotification(UCHAR ucEventCode_, UCHAR* pucEventBuffer_) { switch(ucEventCode_) { case EVENT_TX: HandleTransmit((UCHAR*) pucEventBuffer_); break; default: break; } } /************************************************************************** * BikeSpdCadSensor::InitializeSim * * Initializes simulator variables * * returns: N/A * **************************************************************************/ void BikeSpdCadSensor::InitializeSim() { // Simulation Timer ulRunTime16000 = 0; ulTimerInterval = 247; // First event, before cadence/speed events, to initialize sim data ulNextCadInterval = 667; //90 rpm ulNextSpdInterval = 248; //30kph, 207cm wheel // Cadence usCadEventCount = 0; usCadTime1024 = 0; ucCurCadence = (UCHAR)this->numericUpDown_Sim_CadCurOutput->Value; // Default current cadence is set on UI ucMinCadence = (UCHAR)this->numericUpDown_Sim_CadMinOutput->Value; // Default minimum cadence is set on UI ucMaxCadence = (UCHAR)this->numericUpDown_Sim_CadMaxOutput->Value; // Default maximum cadence is set on UI ucCadSimDataType = SIM_FIXED; bCadSweepAscending = TRUE; // Speed usSpdEventCount = 0; usSpdTime1024 = 0; ulCurSpeed = (ULONG) (System::Convert::ToDouble(this->numericUpDown_Sim_SpdCurOutput->Value) * 1000); // Initial value set on UI (km/h -> meter/h) ulMinSpeed = (ULONG) (System::Convert::ToDouble(this->numericUpDown_Sim_SpdMinOutput->Value) * 1000); // Initial value set on UI (km/h -> meter/h) ulMaxSpeed = (ULONG) (System::Convert::ToDouble(this->numericUpDown_Sim_SpdMaxOutput->Value) * 1000); // Initial value set on UI (km/h -> meter/h) ucWheelCircumference = System::Convert::ToByte(this->numericUpDown_Sim_WheelCircumference->Value); // Initial value set on UI ucSpdSimDataType = SIM_FIXED; bSpdSweepAscending = TRUE; } /************************************************************************** * BikeSpdCadSensor::HandleTransmit * * Encode data generated by simulator for transmission * * pucTxBuffer_: pointer to the transmit buffer * * returns: N/A * **************************************************************************/ void BikeSpdCadSensor::HandleTransmit(UCHAR* pucTxBuffer_) { // Transmission is always the same page pucTxBuffer_[0] = (UCHAR) (usCadTime1024 & 0xFF); pucTxBuffer_[1] = (UCHAR) (usCadTime1024 >> 8) & 0xFF; pucTxBuffer_[2] = (UCHAR) (usCadEventCount & 0xFF); pucTxBuffer_[3] = (UCHAR) (usCadEventCount >> 8) & 0xFF; pucTxBuffer_[4] = (UCHAR) (usSpdTime1024 & 0xFF); pucTxBuffer_[5] = (UCHAR) (usSpdTime1024 >> 8) & 0xFF; pucTxBuffer_[6] = (UCHAR) (usSpdEventCount & 0xFF); pucTxBuffer_[7] = (UCHAR) (usSpdEventCount >> 8) & 0xFF; } /************************************************************************** * BikeSpdCadSensor::onTimerTock * * Simulates a device event, updating simulator data based on this event * Modifications to the timer interval are applied immediately after this * at ANTChannel * * usEventTime_: current time (ms) * * returns: N/A * **************************************************************************/ void BikeSpdCadSensor::onTimerTock(USHORT eventTime) { // Update master time ulRunTime16000 += (ulTimerInterval << 4); // Multiply by 16 to convert from ms to 1/16000s // Handle event and calculate time until next cadence/speed event if(ulNextCadInterval < ulNextSpdInterval) // Cadence event { // Update event count ++usCadEventCount; // Update event time usCadTime1024 = (USHORT) ((ulRunTime16000 << 3) / 125); // Convert to 1/1024s - multiply by 1024/16000 switch(ucCadSimDataType) { case SIM_FIXED: // Value does not change break; case SIM_SWEEP: { // Cadence sweeps between min and max // The jump offset is calculated versus position against the max so it won't get stuck on low values for a long time and won't speed through high values too fast ULONG tempOffset = ucMaxCadence-ucCurCadence; tempOffset = ((tempOffset & 0xC0) >> 6) + ((tempOffset & 0x20) >>5) + ((tempOffset & 0x10) >>4)+1; if(bCadSweepAscending) ucCurCadence += (UCHAR) tempOffset; else ucCurCadence -= (UCHAR) tempOffset; // Ensure next value is not less than min or more than max if(ucCurCadence >= ucMaxCadence) { ucCurCadence = ucMaxCadence; bCadSweepAscending = FALSE; } if(ucCurCadence <= ucMinCadence) { ucCurCadence = ucMinCadence; bCadSweepAscending = TRUE; } break; } default: break; } // Adjust speed interval with the time already elapsed in cadence event ulNextSpdInterval -= ulNextCadInterval; // Update cadence time interval (in ms) if(ucCurCadence) ulNextCadInterval = (ULONG) 60000/ucCurCadence; // 60 seconds/revolutions per minute else ulNextCadInterval = 600000; // coasting (no events, setting interval to a very large value) // Find out which event will occur next if(ulNextCadInterval < ulNextSpdInterval) ulTimerInterval = ulNextCadInterval; else ulTimerInterval = ulNextSpdInterval; } else // Speed event { // Update event count ++usSpdEventCount; // Update event time usSpdTime1024 = (USHORT) ((ulRunTime16000 << 3) / 125); // Convert to 1/1024s - multiply by 1024/16000 switch(ucSpdSimDataType) { case SIM_FIXED: // Speed value does not change break; case SIM_SWEEP: { // Cadence sweeps between min and max // The jump offset is calculated versus position against the max so it won't get stuck on low values for a long time and won't speed through high values too fast ULONG tempOffset = ulMaxSpeed-ulCurSpeed; tempOffset = ((tempOffset & 0x7000) >> 6) + ((tempOffset & 0xE00) >> 5) + ((tempOffset & 0x1C) >> 4)+0x7F; if(bSpdSweepAscending) ulCurSpeed += tempOffset; else ulCurSpeed -= tempOffset; // Ensure value is not less than min or more than max if(ulCurSpeed >= ulMaxSpeed) { ulCurSpeed = ulMaxSpeed; bSpdSweepAscending = FALSE; } if(ulCurSpeed <= ulMinSpeed) { ulCurSpeed = ulMinSpeed; bSpdSweepAscending = TRUE; } break; } default: break; } // Adjust cadence interval with the time already elapsed in speed event ulNextCadInterval -= ulNextSpdInterval; // Update speed time interval (in ms) if(ulCurSpeed) ulNextSpdInterval = (ULONG) 36000 * ucWheelCircumference/ulCurSpeed; // in ms (wheel circumference is in cm, speed in meter/h) else ulNextSpdInterval = 600000; // Stopping (no events, setting interval to a very large value) //Find out which event will occur next if(ulNextSpdInterval < ulNextCadInterval) ulTimerInterval = ulNextSpdInterval; else ulTimerInterval = ulNextCadInterval; } if(ulTimerInterval < 1) //this prevents timerInterval=0 if values are identical and could be used to prevent too short timer intervals ulTimerInterval = 1; UpdateDisplay(); } /*************************************************************************** * BikeSpdCadSensor::UpdateDisplay * * Updates displayed simulator data on GUI * * returns: N/A * **************************************************************************/ void BikeSpdCadSensor::UpdateDisplay() { this->label_Trn_CadCountDisplay->Text = System::Convert::ToString(usCadEventCount); // Cadence event count this->label_Trn_CadenceTimeDisplay->Text = System::Convert::ToString(usCadTime1024); // Time of last cadence event (1/1024s) this->label_Trn_SpdCountDisplay->Text = System::Convert::ToString(usSpdEventCount); // Speed event cont this->label_Trn_SpeedTimeDisplay->Text = System::Convert::ToString(usSpdTime1024); // Time of last speed event (1/1024s) if(ucCadSimDataType == SIM_SWEEP) // Update only if it is changing this->numericUpDown_Sim_CadCurOutput->Value = ucCurCadence; // Current cadence generated by simulator (rpm) if(ucSpdSimDataType == SIM_SWEEP) // Update only if it is changing this->numericUpDown_Sim_SpdCurOutput->Value = System::Convert::ToDecimal((double) ulCurSpeed/1000); // Current speed generated, meters/h -> km/h } /************************************************************************** * BikeSpdCadSensor::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 BikeSpdCadSensor::numericUpDown_Sim_WheelCircumference_ValueChanged(System::Object^ sender, System::EventArgs^ e) { ucWheelCircumference = System::Convert::ToByte(this->numericUpDown_Sim_WheelCircumference->Value); } /************************************************************************** * BikeSpdCadSensor::checkBox_Sim_SpeedSweeping_CheckedChanged * * Select method to generate simulator data, from user input (GUI) * * returns: N/A * **************************************************************************/ System::Void BikeSpdCadSensor::checkBox_Sim_SpeedSweeping_CheckedChanged(System::Object^ sender, System::EventArgs^ e) { this->numericUpDown_Sim_SpdCurOutput->Enabled = !this->numericUpDown_Sim_SpdCurOutput->Enabled; this->numericUpDown_Sim_SpdMinOutput->Enabled = !this->numericUpDown_Sim_SpdMinOutput->Enabled; this->numericUpDown_Sim_SpdMaxOutput->Enabled = !this->numericUpDown_Sim_SpdMaxOutput->Enabled; if(this->checkBox_Sim_SpdSweeping->Checked) { bSpdSweepAscending = TRUE; ucSpdSimDataType = SIM_SWEEP; } else { ucSpdSimDataType = SIM_FIXED; } } /************************************************************************** * BikeSpdCadSensor::checkBox_Sim_CadenceSweeping_CheckedChanged * * Select method to generate simulator data, from user input (GUI) * * returns: N/A * **************************************************************************/ System::Void BikeSpdCadSensor::checkBox_Sim_CadenceSweeping_CheckedChanged(System::Object^ sender, System::EventArgs^ e) { this->numericUpDown_Sim_CadCurOutput->Enabled = !this->numericUpDown_Sim_CadCurOutput->Enabled; this->numericUpDown_Sim_CadMinOutput->Enabled = !this->numericUpDown_Sim_CadMinOutput->Enabled; this->numericUpDown_Sim_CadMaxOutput->Enabled = !this->numericUpDown_Sim_CadMaxOutput->Enabled; if(this->checkBox_Sim_CadSweeping->Checked) { bCadSweepAscending = TRUE; ucCadSimDataType = SIM_SWEEP; } else { ucCadSimDataType = SIM_FIXED; } } /************************************************************************** * BikeSpdCadSensor::numericUpDown_Sim_SpdCurOutput_ValueChanged * * Validates and updates the current speed, from user input (GUI) * * returns: N/A * **************************************************************************/ System::Void BikeSpdCadSensor::numericUpDown_Sim_SpdCurOutput_ValueChanged(System::Object^ sender, System::EventArgs^ e) { // This value is raised whenever the value changes, even if internally // Only update the current pulse if set by the user if(this->numericUpDown_Sim_SpdCurOutput->Enabled) { ulCurSpeed = (ULONG) (System:: Convert::ToDouble(this->numericUpDown_Sim_SpdCurOutput->Value) * 1000); // km/h -> meter/h if(ulCurSpeed) ulNextSpdInterval = (ULONG) 36000 * ucWheelCircumference/ulCurSpeed; // in ms (wheel circumference is in cm, speed in meter/h) else ulNextSpdInterval = 600000; // Stopping (no speed events in a very long time, so display can interpret it as stopping) ForceUpdate(); } } /************************************************************************** * BikeSpdCad::numericUpDown_Sim_SpdMinMaxOutput_ValueChanged * * If the user has changed the min or max speed, validate that * minimum < current < maximum * * returns: N/A * **************************************************************************/ System::Void BikeSpdCadSensor::numericUpDown_Sim_SpdMinMaxOutput_ValueChanged(System::Object^ sender, System::EventArgs^ e) { ULONG ulPrevSpeed = ulCurSpeed; // This event is raised whenever the min and max value change, even if internally // Check minnumericUpDown_Sim_SpdMinOutput->Value < this->numericUpDown_Sim_SpdMaxOutput->Value) { ulMinSpeed = (ULONG) (System::Convert::ToDouble(this->numericUpDown_Sim_SpdMinOutput->Value) * 1000); // km/h -> meters/h ulMaxSpeed = (ULONG) (System::Convert::ToDouble(this->numericUpDown_Sim_SpdMaxOutput->Value) * 1000); // km/h -> meters/h if(ulCurSpeed > ulMaxSpeed) ulCurSpeed = ulMaxSpeed; else if(ulCurSpeed < ulMinSpeed) ulCurSpeed = ulMinSpeed; if(ulCurSpeed != ulPrevSpeed) { this->numericUpDown_Sim_SpdCurOutput->Value = System::Convert::ToDecimal((double) ulCurSpeed/1000); // meters/h -> km/h if(ulCurSpeed) ulNextSpdInterval = (ULONG) 36000 * ucWheelCircumference/ulCurSpeed; // in ms (wheel circumference is in cm, speed in meter/h) else ulNextSpdInterval = 600000; // Stopping (no speed events in a very long time, so display can interpret it as stopping) ForceUpdate(); } } else { // If the values were invalid, set numeric values to last valid values this->numericUpDown_Sim_SpdMinOutput->Value = System::Convert::ToDecimal((double) ulMinSpeed/1000); // meters/h -> km/h this->numericUpDown_Sim_SpdMaxOutput->Value = System::Convert::ToDecimal((double) ulMaxSpeed/1000); // meters/h -> km/h } } /************************************************************************** * BikeSpdCadSensor::numericUpDown_Sim_CadCurOutput_ValueChanged * * Validates and updates the current cadence, from user input (GUI) * * returns: N/A * **************************************************************************/ System::Void BikeSpdCadSensor::numericUpDown_Sim_CadCurOutput_ValueChanged(System::Object^ sender, System::EventArgs^ e) { // This value is raised whenever the value changes, even if internally // Only update the current pulse if set by the user if(this->numericUpDown_Sim_CadCurOutput->Enabled) { ucCurCadence = System::Convert::ToByte(this->numericUpDown_Sim_CadCurOutput->Value); if(ucCurCadence) ulNextCadInterval = (ULONG) 60000/ucCurCadence; // 60 seconds/revolutions per minute else ulNextCadInterval = 600000; // Coasting (no cadence events in a very long time so that display can interpret as coasting) ForceUpdate(); } } /************************************************************************** * BikeSpdCad::numericUpDown_Sim_CadMinMaxOutput_ValueChanged * * If the user has changed the min or max cadence, validate that * minimum < current < maximum * * returns: N/A * **************************************************************************/ System::Void BikeSpdCadSensor::numericUpDown_Sim_CadMinMaxOutput_ValueChanged(System::Object^ sender, System::EventArgs^ e) { UCHAR ucPrevCadence = ucCurCadence; // This event is raised whenever the min and max value change, even if internally // Check minnumericUpDown_Sim_CadMinOutput->Value < this->numericUpDown_Sim_CadMaxOutput->Value) { ucMinCadence = (UCHAR) this->numericUpDown_Sim_CadMinOutput->Value; ucMaxCadence = (UCHAR) this->numericUpDown_Sim_CadMaxOutput->Value; if(ucCurCadence > ucMaxCadence) ucCurCadence = ucMaxCadence; else if(ucCurCadence < ucMinCadence) ucCurCadence = ucMinCadence; if(ucCurCadence != ucPrevCadence) { this->numericUpDown_Sim_CadCurOutput->Value = ucCurCadence; if(ucCurCadence) ulNextCadInterval = (ULONG) 60000/ucCurCadence; // 60 seconds/revolutions per minute else ulNextCadInterval = 600000; // Coasting (no cadence events in a very long time so that display can interpret as coasting) ForceUpdate(); } } else { // If the values were invalid, set numeric values to last valid values this->numericUpDown_Sim_CadMinOutput->Value = ucMinCadence; this->numericUpDown_Sim_CadMaxOutput->Value = ucMaxCadence; } } /************************************************************************** * BikeSpdCad::ForceUpdate * * Causes a timer event, to force the simulator to update all calculations * * returns: N/A * **************************************************************************/ void BikeSpdCadSensor::ForceUpdate() { timerHandle->Interval = 250; }