/* 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 "BikeSpeedSensor.h" /************************************************************************** * BikeSpeedSensor::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 BikeSpeedSensor::ANT_eventNotification(UCHAR ucEventCode_, UCHAR* pucEventBuffer_) { switch(ucEventCode_) { case EVENT_TX: HandleTransmit((UCHAR*) pucEventBuffer_); break; default: break; } } /************************************************************************** * BikeSpeedSensor::InitializeSim * * Initializes simulator variables * * returns: N/A * **************************************************************************/ void BikeSpeedSensor::InitializeSim() { ulTimerInterval = 248; //30kph, 207cm wheel ulRunTime = 0; ulRunTime16000 = 0; ucReserved = BS_RESERVED; usEventCount = 0; ucWheelCircumference = System::Convert::ToByte(this->numericUpDown_Sim_WheelCircumference->Value); // Initial value set on UI ulMinSpeed = (ULONG) (System::Convert::ToDouble(this->numericUpDown_Sim_SpdMinOutput->Value) * 1000); // Initial value set on UI (km/h -> meter/h) ulCurSpeed = (ULONG) (System::Convert::ToDouble(this->numericUpDown_Sim_SpdCurOutput->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) ulElapsedTime2 = 0; usTime1024 = 0; ucMfgID = System::Convert::ToByte(this->textBox_ManfIDChange->Text); // Background data initially set on UI ucHwVersion = System::Convert::ToByte(this->textBox_HardwareVerChange->Text); ucSwVersion = System::Convert::ToByte(this->textBox_SoftwareVerChange->Text); ucModelNum = System::Convert::ToByte(this->textBox_ModelNumChange->Text); usSerialNum = System::Convert::ToUInt16(this->textBox_SerialNumChange->Text); ucSimDataType = SIM_FIXED; // Generate single fixed heart rate value by default bLegacy = FALSE; // Disable legacy mode by default bTxMinimum = FALSE; // Transmit full data set by default bSweepAscending = TRUE; // If sweeping, start with ascending values by default ucBackgroundCount = 0; ucNextBackgroundPage = BS_PAGE1; // By default, support all defined pages } /************************************************************************** * BikeSpeedSensor::HandleTransmit * * Encode data generated by simulator for transmission * * pucTxBuffer_: pointer to the transmit buffer * * returns: N/A * **************************************************************************/ void BikeSpeedSensor::HandleTransmit(UCHAR* pucTxBuffer_) { static UCHAR ucMessageNum = 0; // Message count static UCHAR ucPageToggle = 0; // Page toggle tracker UCHAR ucPageNum = BS_PAGE0; // Page number // Send background pages every 65th message (not available in legacy sensors) if(!bLegacy) { if(ucMessageNum++ == BS_BACKGROUND_INTERVAL-1) { ucMessageNum = 0; switch(ucNextBackgroundPage) { case 1: ucPageNum = BS_PAGE1; ucNextBackgroundPage++; break; case 2: ucPageNum = BS_PAGE2; if(bTxMinimum) { // If using minimum data set, background pages are sent three times to ensure they are received by displays with lower message rate if(++ucBackgroundCount > 2) { ucBackgroundCount = 0; ucNextBackgroundPage ++; } } else { // Otherwise, send only once ucNextBackgroundPage++; } break; case 3: ucPageNum = BS_PAGE3; if(bTxMinimum) { // If using minimum data set, background pages are sent three times to ensure they are received by displays with lower message rate if(++ucBackgroundCount > 2) { ucBackgroundCount = 0; ucNextBackgroundPage = BS_PAGE2; } } else { // Otherwise, send only once ucNextBackgroundPage = BS_PAGE1; } break; default: break; } } } // Fill in common info if(!bLegacy) pucTxBuffer_[0] = ucPageNum; else pucTxBuffer_[0] = ucReserved; // Legacy sensors only, do not interpret at receiver, do not use as reference for new sensor designs pucTxBuffer_[4] = (UCHAR) (usTime1024 & 0xFF); pucTxBuffer_[5] = (UCHAR) (usTime1024 >> 8) & 0xFF; pucTxBuffer_[6] = (UCHAR) (usEventCount & 0xFF); pucTxBuffer_[7] = (UCHAR) (usEventCount >> 8) & 0xFF; // Page specific info switch(ucPageNum) { case BS_PAGE0: pucTxBuffer_[1] = ucReserved; // Reserved, do not interpret at the receiver pucTxBuffer_[2] = ucReserved; // Reserved, do not interpret at the receiver pucTxBuffer_[3] = ucReserved; // Reserved, do not interpret at the receiver break; case BS_PAGE1: pucTxBuffer_[1] = (ulElapsedTime2 & 0xFF); // Cumulative operating time, bits 0-7 (intervals of 2s) pucTxBuffer_[2] = (ulElapsedTime2 >> 8) & 0xFF; // Cumulative operating time, bits 8-15 (intervals of 2s) pucTxBuffer_[3] = (ulElapsedTime2 >> 16) & 0xFF; // Cumulative operating time, bits 16-23 (intervals of 2s) break; case BS_PAGE2: pucTxBuffer_[1] = ucMfgID; // Manufacturing ID pucTxBuffer_[2] = usSerialNum & 0x00FF; // Low byte of serial number pucTxBuffer_[3] = (usSerialNum & 0xFF00) >>8; // High byte of serial number break; case BS_PAGE3: pucTxBuffer_[1] = ucHwVersion; // Hardware version pucTxBuffer_[2] = ucSwVersion; // Software version pucTxBuffer_[3] = ucModelNum; // Model number break; default: break; } // Handle page toggle bit: toggle every four messages if(!bLegacy) { ucPageToggle += 0x20; pucTxBuffer_[0] += (ucPageToggle & BS_TOGGLE_MASK); } } /************************************************************************** * BikeSpeedSensor::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 BikeSpeedSensor::onTimerTock(USHORT eventTime) { ULONG tempOffset = 0; // Temporary variable to calculate sweeping intervals // Update event count ++usEventCount; // Update event time ulRunTime16000 += (ulTimerInterval << 4); // Multiply by 16 to convert from ms to 1/16000s usTime1024 = (USHORT) ((ulRunTime16000 << 3) / 125); // Convert to 1/1024s - multiply by 1024/16000 ulRunTime += ulTimerInterval; while(ulRunTime/2000) // 2000 ms { ++ulElapsedTime2; // elapsed time is updated every 2 seconds ulRunTime -=2000; } switch(ucSimDataType) { 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 tempOffset = ulMaxSpeed-ulCurSpeed; tempOffset = ((tempOffset & 0x7000) >> 6) + ((tempOffset & 0xE00) >> 5) + ((tempOffset & 0x1C) >> 4)+0x7F; if(bSweepAscending) ulCurSpeed += tempOffset; else ulCurSpeed -= tempOffset; // Ensure next value is not less than min or more than max if(ulCurSpeed >= ulMaxSpeed) { ulCurSpeed = ulMaxSpeed; bSweepAscending = FALSE; } if(ulCurSpeed <= ulMinSpeed) { ulCurSpeed = ulMinSpeed; bSweepAscending = TRUE; } break; default: break; } // Update timer interval (in ms) if(ulCurSpeed) ulTimerInterval = (ULONG) 36000 * ucWheelCircumference/ulCurSpeed; // in ms (wheel circumference is in cm, speed in meter/h) else ulTimerInterval = 600000; // Stopping, set timer interval to a very large value // Update display UpdateDisplay(); } /*************************************************************************** * BikeSpeedSensor::UpdateDisplay * * Updates displayed simulator data on GUI * * returns: N/A * **************************************************************************/ void BikeSpeedSensor::UpdateDisplay() { this->label_Trn_EventCountDisplay->Text = System::Convert::ToString(usEventCount); // Event count this->label_ElapsedSecsDisplay->Text = System::Convert::ToString((unsigned int)(ulElapsedTime2 & 0x00FFFFFF)*2); // Operating time (s) if(ucSimDataType == SIM_SWEEP) // Update only if it is changing this->numericUpDown_Sim_SpdCurOutput->Value = System::Convert::ToDecimal((double) ulCurSpeed/1000); // Current speed generated, meter/h -> km/h this->label_Trn_TimeDisplay->Text = System::Convert::ToString(usTime1024); // Time of last event (1/1024s) } /************************************************************************** * BikeSpeedSensor::radioButton_SimTypeChanged * * Select method to generate simulator data, from user input (GUI) * * returns: N/A * **************************************************************************/ void BikeSpeedSensor::radioButton_SimTypeChanged (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->radioButton_Sim_Fixed->Checked) { ucSimDataType = SIM_FIXED; } else if(this->radioButton_Sim_Sweep->Checked) { bSweepAscending = TRUE; ucSimDataType = SIM_SWEEP; } } /************************************************************************** * BikeSpeedSensor::button_AdvancedUpdate_Click * * Validates and updates product information, from user input (GUI) * * returns: N/A * **************************************************************************/ System::Void BikeSpeedSensor::button_AdvancedUpdate_Click(System::Object^ sender, System::EventArgs^ e) { label_AdvancedError->Visible = false; label_AdvancedError->Text = "Error: "; //convert and catch failed conversions try{ ucMfgID = System::Convert::ToByte(this->textBox_ManfIDChange->Text); } catch(...){ label_AdvancedError->Text = System::String::Concat(label_AdvancedError->Text, " MFID"); label_AdvancedError->Visible = true; } try{ usSerialNum = System::Convert::ToUInt16(this->textBox_SerialNumChange->Text); } catch(...){ label_AdvancedError->Text = System::String::Concat(label_AdvancedError->Text, " Ser#"); label_AdvancedError->Visible = true; } try{ ucHwVersion = System::Convert::ToByte(this->textBox_HardwareVerChange->Text); } catch(...){ label_AdvancedError->Text = System::String::Concat(label_AdvancedError->Text, " HWVr"); label_AdvancedError->Visible = true; } try{ ucSwVersion = System::Convert::ToByte(this->textBox_SoftwareVerChange->Text); } catch(...){ label_AdvancedError->Text = System::String::Concat(label_AdvancedError->Text, " SWVr"); label_AdvancedError->Visible = true; } try{ ucModelNum = System::Convert::ToByte(this->textBox_ModelNumChange->Text); } catch(...){ label_AdvancedError->Text = System::String::Concat(label_AdvancedError->Text, " Mdl#"); label_AdvancedError->Visible = true; } } /************************************************************************** * BikeSpeedSensor::button_UpdateTime_Click * * Validates and updates cumulative operating time, from user input (GUI) * * returns: N/A * **************************************************************************/ System::Void BikeSpeedSensor::button_UpdateTime_Click(System::Object^ sender, System::EventArgs^ e) { ULONG ulCumulativeTime = 0; label_AdvancedError->Visible = false; try { ulCumulativeTime = System::Convert::ToUInt32(this->textBox_ElpTimeChange->Text); if(ulCumulativeTime > 33554430) // Cumulative Operating Time rollover: 2 * 0xFFFFFF = 33554430 seconds throw "Cumulative operating time exceeds rollover value"; ulElapsedTime2 = ulCumulativeTime >> 1; // Cumulative time is stored in intervals of 2 seconds // Update display (in seconds) label_ElapsedSecsDisplay->Text = ((ulElapsedTime2 & 0x00FFFFFF) << 1).ToString(); } catch(...) { label_AdvancedError->Text = "Error: Time"; label_AdvancedError->Visible = true; } } /************************************************************************** * BikeSpeedSensor::numericUpDown_Sim_SpdCurOutput_ValueChanged * * Validates and updates the current speed value, from user input (GUI) * * returns: N/A * **************************************************************************/ System::Void BikeSpeedSensor::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 value if set by the user if(this->numericUpDown_Sim_SpdCurOutput->Enabled) { ulCurSpeed = (ULONG) (System::Convert::ToDouble(this->numericUpDown_Sim_SpdCurOutput->Value) * 1000); // kn/h -> m/h ForceUpdate(); } } /************************************************************************** * BikeSpeedSensor::numericUpDown_Sim_SpdMinMaxOutput_ValueChanged * * If the user has changed the min or max speed, validate that * minimum < current < maximum * * returns: N/A * **************************************************************************/ System::Void BikeSpeedSensor::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 -> meter/h ulMaxSpeed = (ULONG) (System::Convert::ToDouble(this->numericUpDown_Sim_SpdMaxOutput->Value) * 1000); // km/h -> meter/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); // meter/h -> km/h 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); // meter/h -> km/h this->numericUpDown_Sim_SpdMaxOutput->Value = System::Convert::ToDecimal((double) ulMaxSpeed/1000); // meter/h -> km/h } } /************************************************************************** * BikeSpeedSensor::checkBox_Legacy_CheckedChanged * * Enable simulation of legacy sensors for testing backward compatibility of receivers * * returns: N/A * **************************************************************************/ System::Void BikeSpeedSensor::checkBox_Legacy_CheckedChanged(System::Object^ sender, System::EventArgs^ e) { // Enable simulation of legacy sensors for testing backward compatibility of receivers if(checkBox_Legacy->Checked) { System::Windows::Forms::DialogResult result = MessageBox::Show(L"This option is available for backward compatibility testing.\nIt should not be used as a reference in the development of new sensors.", L"Warning", MessageBoxButtons::OKCancel); if( result == ::DialogResult::OK ) { bLegacy = TRUE; // Generate random data for reserved field to simulate legacy receivers // This should not be interpreted by the receiver, and by no means should be used as a reference for new designs // This feature is avaialable only for testing backwards compatibility srand(ulElapsedTime2); ucReserved = (UCHAR) ((rand() % 240) + 16); // Generate a random number between 16 and 255 checkBox_SendBasicPage->Checked = FALSE; checkBox_SendBasicPage->Enabled = FALSE; } else { bLegacy = FALSE; ucReserved = BS_RESERVED; checkBox_Legacy->Checked = FALSE; checkBox_SendBasicPage->Enabled = TRUE; } } else { bLegacy = FALSE; ucReserved = BS_RESERVED; checkBox_SendBasicPage->Enabled = TRUE; } } /************************************************************************** * BikeSpeedSensor::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 BikeSpeedSensor::numericUpDown_Sim_WheelCircumference_ValueChanged(System::Object^ sender, System::EventArgs^ e) { ucWheelCircumference = System::Convert::ToByte(this->numericUpDown_Sim_WheelCircumference->Value); } /************************************************************************** * BikeSpeedSensor::checkBox_SendBasicPage_CheckedChanged * * Selects transmission of minimum data set * Minimum data set does not include Page 1 * * returns: N/A * **************************************************************************/ System::Void BikeSpeedSensor::checkBox_SendBasicPage_CheckedChanged(System::Object^ sender, System::EventArgs^ e) { if(checkBox_SendBasicPage->Checked) { bTxMinimum = TRUE; ucNextBackgroundPage = BS_PAGE2; // Page 1 is disabled ucBackgroundCount = 0; // Reset background page count } else { bTxMinimum = FALSE; ucNextBackgroundPage = BS_PAGE1; // Enable Page 1 ucBackgroundCount = 0; // Reset background page count } } /************************************************************************** * BikeSpeedSensor::ForceUpdate * * Causes a timer event, to force the simulator to update all calculations * * returns: N/A * **************************************************************************/ void BikeSpeedSensor::ForceUpdate() { timerHandle->Interval = 250; }