/* 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 "BikeCadenceSensor.h" /************************************************************************** * BikeCadenceSensor::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 BikeCadenceSensor::ANT_eventNotification(UCHAR ucEventCode_, UCHAR* pucEventBuffer_) { switch(ucEventCode_) { case EVENT_TX: HandleTransmit((UCHAR*) pucEventBuffer_); break; default: break; } } /************************************************************************** * BikeCadenceSensor::InitializeSim * * Initializes simulator variables * * returns: N/A * **************************************************************************/ void BikeCadenceSensor::InitializeSim() { // Simulation timer ulTimerInterval = 667; //90rpm // Initialization of simulator variables ulRunTime = 0; ulRunTime16000 = 0; ucReserved = BC_RESERVED; usEventCount = 0; ucMinCadence = (UCHAR)this->numericUpDown_Sim_MinCadence->Value; // Default minimum cadence is set on UI ucCurCadence = (UCHAR)this->numericUpDown_Sim_CurrentCadence->Value; // Default current cadence is set on UI ucMaxCadence = (UCHAR)this->numericUpDown_Sim_MaxCadence->Value; // Default maximum cadence is set on UI ulElapsedTime2 = 0; usTime1024 = 0; // Common Pages (set on UI) ucMfgID = System::Convert::ToByte(this->textBox_ManfIDChange->Text); 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); // Status 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 = BC_PAGE1; // By default, support all defined pages } /************************************************************************** * BikeCadenceSensor::HandleTransmit * * Encode data generated by simulator for transmission * * pucTxBuffer_: pointer to the transmit buffer * * returns: N/A * **************************************************************************/ void BikeCadenceSensor::HandleTransmit(UCHAR* pucTxBuffer_) { static UCHAR ucMessageNum = 0; // Message count static UCHAR ucPageToggle = 0; // Page toggle tracker UCHAR ucPageNum = BC_PAGE0; // Page number // Send global pages every 65th message (not available in legacy sensors) if(!bLegacy) { if(ucMessageNum++ == BC_BACKGROUND_INTERVAL-1) { ucMessageNum = 0; switch(ucNextBackgroundPage) { case 1: ucPageNum = BC_PAGE1; ucNextBackgroundPage++; break; case 2: ucPageNum = BC_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 = BC_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 = BC_PAGE2; } } else { // Otherwise, send only once ucNextBackgroundPage = BC_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 BC_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 BC_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 BC_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 BC_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 & BC_TOGGLE_MASK); } } /************************************************************************** * BikeCadenceSensor::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 BikeCadenceSensor::onTimerTock(USHORT eventTime) { UCHAR 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: // Cadence 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 = ucMaxCadence-ucCurCadence; tempOffset = ((tempOffset & 0xC0) >> 6) + ((tempOffset & 0x20) >>5) + ((tempOffset & 0x10) >>4)+1; if(bSweepAscending) ucCurCadence += tempOffset; else ucCurCadence -= tempOffset; // Ensure value is not larger than max or smaller than min if(ucCurCadence >= ucMaxCadence) { ucCurCadence = ucMaxCadence; bSweepAscending = FALSE; } if(ucCurCadence <= ucMinCadence) { ucCurCadence = ucMinCadence; bSweepAscending = TRUE; } break; default: break; } // Update timer interval (in ms) if(ucCurCadence) ulTimerInterval = (ULONG) 60000/ucCurCadence; // 60 seconds/revolutions per minute else ulTimerInterval = 600000; // Coasting, set interval to a very large value // Update display UpdateDisplay(); } /*************************************************************************** * BikeCadenceSensor::UpdateDisplay * * Updates displayed simulator data on GUI * * returns: N/A * **************************************************************************/ void BikeCadenceSensor::UpdateDisplay() { this->label_Trn_EventCountDisplay->Text = usEventCount.ToString(); // Event count this->label_ElapsedSecsDisplay->Text = ((ulElapsedTime2 & 0x00FFFFFF) << 1).ToString(); // Operating time (s) if(ucSimDataType == SIM_SWEEP) // Update only if it is changing this->numericUpDown_Sim_CurrentCadence->Value = ucCurCadence; // Current cadence generated by simulator (rpm) this->label_Trn_TimeDisplay->Text = usTime1024.ToString(); // Time of last event (1/1024s) } /************************************************************************** * BikeCadenceSensor::radioButton_SimTypeChanged * * Select method to generate simulator data, from user input (GUI) * * returns: N/A * **************************************************************************/ void BikeCadenceSensor::radioButton_SimTypeChanged (System::Object^ sender, System::EventArgs^ e) { this->numericUpDown_Sim_CurrentCadence->Enabled = !this->numericUpDown_Sim_CurrentCadence->Enabled; this->numericUpDown_Sim_MaxCadence->Enabled = !this->numericUpDown_Sim_MaxCadence->Enabled; this->numericUpDown_Sim_MinCadence->Enabled = !this->numericUpDown_Sim_MinCadence->Enabled; if(this->radioButton_Sim_Fixed->Checked) { ucSimDataType = SIM_FIXED; } else if(this->radioButton_Sim_Sweep->Checked) { bSweepAscending = TRUE; ucSimDataType = SIM_SWEEP; } } /************************************************************************** * BikeCadenceSensor::button_AdvancedUpdate_Click * * Validates and updates product information, from user input (GUI) * * returns: N/A * **************************************************************************/ System::Void BikeCadenceSensor::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; } } /************************************************************************** * BikeCadenceSensor::button_UpdateTime_Click * * Validates and updates cumulative operating time, from user input (GUI) * * returns: N/A * **************************************************************************/ System::Void BikeCadenceSensor::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; } } /************************************************************************** * BikeCadenceSensor::numericUpDown_Sim_CurCadence_ValueChanged * * Validates and updates the current cadence, from user input (GUI) * * returns: N/A * **************************************************************************/ System::Void BikeCadenceSensor::numericUpDown_Sim_CurCadence_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_CurrentCadence->Enabled) { ucCurCadence = (UCHAR)this->numericUpDown_Sim_CurrentCadence->Value; ForceUpdate(); } } /************************************************************************** * BikeCadenceSensor::numericUpDown_Sim_MinMaxCadence_ValueChanged * * If the user has changed the min or max cadence, validate that * minimum < current < maximum * * returns: N/A * **************************************************************************/ System::Void BikeCadenceSensor::numericUpDown_Sim_MinMaxCadence_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_MinCadence->Value < this->numericUpDown_Sim_MaxCadence->Value) { ucMinCadence = (UCHAR)this->numericUpDown_Sim_MinCadence->Value; ucMaxCadence = (UCHAR)this->numericUpDown_Sim_MaxCadence->Value; if(ucCurCadence > ucMaxCadence) ucCurCadence = ucMaxCadence; else if(ucCurCadence < ucMinCadence) ucCurCadence = ucMinCadence; if(ucCurCadence != ucPrevCadence) { this->numericUpDown_Sim_CurrentCadence->Value = ucCurCadence; ForceUpdate(); } } else { // If the values were invalid, set numeric values to last valid values this->numericUpDown_Sim_MinCadence->Value = ucMinCadence; this->numericUpDown_Sim_MaxCadence->Value = ucMaxCadence; } } /************************************************************************** * BikeCadenceSensor::checkBox_Legacy_CheckedChanged * * Enable simulation of legacy sensors for testing backward compatibility of receivers * * returns: N/A * **************************************************************************/ System::Void BikeCadenceSensor::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 = BC_RESERVED; checkBox_Legacy->Checked = FALSE; checkBox_SendBasicPage->Enabled = TRUE; } } else { bLegacy = FALSE; ucReserved = BC_RESERVED; checkBox_SendBasicPage->Enabled = TRUE; } } /************************************************************************** * BikeCadenceSensor::checkBox_SendBasicPage_CheckedChanged * * Selects transmission of minimum data set * Minimum data set does not include Page 1 * * returns: N/A * **************************************************************************/ System::Void BikeCadenceSensor::checkBox_SendBasicPage_CheckedChanged(System::Object^ sender, System::EventArgs^ e) { if(checkBox_SendBasicPage->Checked) { bTxMinimum = TRUE; ucNextBackgroundPage = BC_PAGE2; // Page 1 is disabled ucBackgroundCount = 0; // Reset background page count } else { bTxMinimum = FALSE; ucNextBackgroundPage = BC_PAGE1; // Enable Page 1 ucBackgroundCount = 0; // Reset background page count } } /************************************************************************** * BikeCadenceSensor::ForceUpdate * * Causes a timer event, to force the simulator to update all calculations * * returns: N/A * **************************************************************************/ void BikeCadenceSensor::ForceUpdate() { timerHandle->Interval = 250; }