DecodeCrankTorqueFrequency.c 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300
  1. /*
  2. This software is subject to the license described in the License.txt file
  3. included with this software distribution. You may not use this file except in compliance
  4. with this license.
  5. Copyright (c) Dynastream Innovations Inc. 2014
  6. All rights reserved.
  7. */
  8. #include "string.h"
  9. #include "stdlib.h"
  10. #define _USE_MATH_DEFINES
  11. #include "math.h"
  12. #include "PowerDecoder.h"
  13. #include "RecordOutput.h"
  14. #include "DecodeCrankTorqueFrequency.h"
  15. static BPSAMPLER stState;
  16. static PowerRecordReceiver prrPtr;
  17. static double dRecordInterval;
  18. static double dReSyncInterval;
  19. #define UPDATE_EVENT_BYTE 1
  20. #define SLOPE_MSB 2
  21. #define SLOPE_LSB 3
  22. #define TIME_STAMP_MSB 4
  23. #define TIME_STAMP_LSB 5
  24. #define TORQUE_TICKS_MSB 6
  25. #define TORQUE_TICKS_LSB 7
  26. ///////////////////////////////////////////////////////////////////////////////
  27. // void DecodeCrankTorqueFreq_Init(double dRecordInterval_, double dTimeBase_)
  28. ///////////////////////////////////////////////////////////////////////////////
  29. //
  30. // Call this to initialize the decoder.
  31. // dTimeBase_ is set to zero to initialize event based decoding; otherwise
  32. // the timebase value is assumed to be the sensor message update rate.
  33. //
  34. ///////////////////////////////////////////////////////////////////////////////
  35. void DecodeCrankTorqueFreq_Init(double dRecordInterval_, double dTimeBase_, double dReSyncInterval_, PowerRecordReceiver powerRecordReceiverPtr_)
  36. {
  37. ResamplerOutput_Init(&stState, (int)(dRecordInterval_ * CTF_TIME_QUANTIZATION), dRecordInterval_, (int)(dTimeBase_ * CTF_TIME_QUANTIZATION));
  38. stState.usTorqueOffset = 500; // This is a nominal cal point for the SRM's we've seen.
  39. prrPtr = powerRecordReceiverPtr_;
  40. dRecordInterval = dRecordInterval_;
  41. dReSyncInterval = dReSyncInterval_;
  42. }
  43. ///////////////////////////////////////////////////////////////////////////////
  44. // void DecodeCrankTorqueFreq_Message(double dTime_, unsigned char messagePayload_[])
  45. ///////////////////////////////////////////////////////////////////////////////
  46. //
  47. // Message event handler interface.
  48. // This is intended to abstract away the top-level messiness of having to
  49. // detect data gaps or duplicates, etc.
  50. //
  51. ///////////////////////////////////////////////////////////////////////////////
  52. void DecodeCrankTorqueFreq_Message(double dTime_, unsigned char messagePayload_[])
  53. {
  54. // see if the message is new.
  55. if (stState.ucLastEventCount != messagePayload_[UPDATE_EVENT_BYTE])
  56. {
  57. if ((dTime_ - stState.dLastMessageTime) > dReSyncInterval)
  58. {
  59. DecodeCrankTorqueFreq_Resync(dTime_, messagePayload_);
  60. }
  61. else
  62. {
  63. DecodeCrankTorqueFreq(dTime_, messagePayload_);
  64. }
  65. stState.dLastMessageTime = dTime_;
  66. }
  67. }
  68. ///////////////////////////////////////////////////////////////////////////////
  69. // void DecodeCrankTorqueFreq_Resync(double dCurrentTime_, unsigned char messagePayload_[])
  70. ///////////////////////////////////////////////////////////////////////////////
  71. //
  72. // Re-establish data baseline.
  73. ///////////////////////////////////////////////////////////////////////////////
  74. void DecodeCrankTorqueFreq_Resync(double dCurrentTime_, unsigned char messagePayload_[])
  75. {
  76. unsigned short usCurrentTorqueTicks;
  77. unsigned short usCurrentTimeStamp;
  78. // CurrentRecordEpoch is the last time that we should have had a data record.
  79. double dCurrentRecordEpoch = (floor(dCurrentTime_ / dRecordInterval)) * dRecordInterval;
  80. if ((stState.dLastRecordTime != 0) && (dCurrentRecordEpoch - stState.dLastRecordTime < MAXIMUM_TIME_GAP))
  81. {
  82. // Figure out how many records we missed.
  83. stState.ucRecordGapCount = (unsigned char)((dCurrentRecordEpoch - stState.dLastRecordTime + dRecordInterval * 0.5)
  84. / dRecordInterval);
  85. // Transfer the accumulated data to the gap.
  86. stState.fGapEnergy = stState.fAccumEnergy;
  87. stState.fGapRotation = stState.fAccumRotation;
  88. // We need to fill in the gap with records.
  89. RecordOutput_FillGap(prrPtr, &stState);
  90. }
  91. usCurrentTimeStamp = messagePayload_[TIME_STAMP_LSB];
  92. usCurrentTimeStamp += ((unsigned short)messagePayload_[TIME_STAMP_MSB]) << 8;
  93. usCurrentTorqueTicks = messagePayload_[TORQUE_TICKS_LSB];
  94. usCurrentTorqueTicks += ((unsigned short)messagePayload_[TORQUE_TICKS_MSB]) << 8;
  95. stState.fAccumEnergy = 0;
  96. stState.fPendingEnergy = 0;
  97. stState.fGapEnergy = 0;
  98. stState.fAccumRotation = 0;
  99. stState.fPendingRotation = 0;
  100. stState.fGapRotation = 0;
  101. stState.ucRecordGapCount = 0;
  102. stState.ulEventTime = 0;
  103. stState.ulLastRecordTime = 0;
  104. stState.dLastMessageTime = dCurrentTime_;
  105. // Update our saved state.
  106. stState.dLastRecordTime = dCurrentRecordEpoch;
  107. stState.usLastAccumTorque = usCurrentTorqueTicks;
  108. stState.usLastAccumPeriod = usCurrentTimeStamp;
  109. stState.ucLastRotationTicks = messagePayload_[UPDATE_EVENT_BYTE];
  110. stState.ucLastEventCount = messagePayload_[UPDATE_EVENT_BYTE];
  111. }
  112. ///////////////////////////////////////////////////////////////////////////////
  113. //
  114. //
  115. ///////////////////////////////////////////////////////////////////////////////
  116. void DecodeCrankTorqueFreq(double dTime_, unsigned char messagePayload_[])
  117. {
  118. unsigned long ulNewEventTime;
  119. unsigned long ulEventCadence;
  120. unsigned long ulEventPower;
  121. unsigned short usCurrentTorqueTicks;
  122. unsigned short usCurrentTimeStamp;
  123. unsigned short usDeltaTorque;
  124. unsigned short usDeltaPeriod;
  125. unsigned char ucCurrentEventCount = messagePayload_[UPDATE_EVENT_BYTE];
  126. unsigned char ucDeltaEventCount;
  127. unsigned short usTorqueSlope;
  128. float fEventEnergy;
  129. usTorqueSlope = messagePayload_[SLOPE_LSB];
  130. usTorqueSlope += ((unsigned short)messagePayload_[SLOPE_MSB]) << 8;
  131. usCurrentTimeStamp = messagePayload_[TIME_STAMP_LSB];
  132. usCurrentTimeStamp += ((unsigned short)messagePayload_[TIME_STAMP_MSB]) << 8;
  133. usCurrentTorqueTicks = messagePayload_[TORQUE_TICKS_LSB];
  134. usCurrentTorqueTicks += ((unsigned short)messagePayload_[TORQUE_TICKS_MSB]) << 8;
  135. usDeltaTorque = usCurrentTorqueTicks - stState.usLastAccumTorque; // make sure this is done in 16 bit word width!
  136. usDeltaPeriod = usCurrentTimeStamp - stState.usLastAccumPeriod; // make sure this is done in 16 bit word width!
  137. ucDeltaEventCount = ucCurrentEventCount - stState.ucLastEventCount;
  138. // 65535 is an invalid value.
  139. if (usDeltaTorque == 65535)
  140. {
  141. usDeltaTorque = 0;
  142. }
  143. if (usDeltaPeriod && (usDeltaPeriod != 0xFFFF))
  144. {
  145. unsigned long ulTempTorque;
  146. ulNewEventTime = stState.ulEventTime + (unsigned long)usDeltaPeriod;
  147. #if defined (TIMEBASE_DRIFT_CORRECTION)
  148. // This is a correction for cases where the sensor timebase is fast compared to the
  149. // receiver timebase.
  150. if ((dTime_ - (dLastRecordTime + (double)usDeltaPeriod/CTF_TIME_QUANTIZATION)) > (RECORD_INTERVAL))
  151. {
  152. //create a gap to fill.
  153. ulNewEventTime += usRecordInterval;
  154. }
  155. #endif
  156. // We multiply this up by 32 so that we end up with the torque quantized to 1/32 N*m
  157. // like it is for the other crank-torque sensors.
  158. ulTempTorque = ((unsigned long)usDeltaTorque * CTF_TIME_QUANTIZATION * 32) / usDeltaPeriod;
  159. if (ulTempTorque > ((unsigned long)stState.usTorqueOffset * 32))
  160. {
  161. ulTempTorque -= (unsigned long)stState.usTorqueOffset * 32;
  162. }
  163. else
  164. {
  165. ulTempTorque = 0;
  166. }
  167. ulTempTorque *= 10;
  168. ulTempTorque /= usTorqueSlope;
  169. ulEventPower = ((long)(M_PI*2000.0 + 0.5) * ulTempTorque / usDeltaPeriod + 8) >> 4;
  170. ulEventCadence = ((long)ucDeltaEventCount * 60L * CTF_TIME_QUANTIZATION + (usDeltaPeriod >> 1)) / usDeltaPeriod;
  171. fEventEnergy = (float)(M_PI * (float)ulTempTorque / 16.0f);
  172. }
  173. else
  174. {
  175. // This is basically a non-event.
  176. ulEventPower = 0;
  177. ulEventCadence = 0;
  178. fEventEnergy = 0;
  179. ulNewEventTime = stState.ulEventTime;
  180. }
  181. if (((unsigned short)(ulNewEventTime - stState.ulLastRecordTime)) >= stState.usRecordInterval)
  182. {
  183. // The event occurred after the end of the current record epoch.
  184. // First, figure out the number of records in a gap if it exists. This calculation uses
  185. // implicit truncation in the division so the subtraction can't be done first.
  186. stState.ucRecordGapCount = (unsigned char)((ulNewEventTime / stState.usRecordInterval) - (stState.ulLastRecordTime / stState.usRecordInterval) - 1);
  187. // Pending energy goes towards the partial accumulated record we currently have.
  188. stState.fPendingEnergy = stState.fAccumEnergy + fEventEnergy * ((float)(stState.usRecordInterval - (stState.ulEventTime % stState.usRecordInterval))) / ((float)usDeltaPeriod);
  189. // accumulated energy goes towards the *next* event.
  190. stState.fAccumEnergy = fEventEnergy * ((float)(ulNewEventTime % stState.usRecordInterval)) / ((float)usDeltaPeriod);
  191. // Gap energy fills the remainder.
  192. stState.fGapEnergy = fEventEnergy * ((unsigned short)stState.ucRecordGapCount * stState.usRecordInterval) / ((float)usDeltaPeriod);
  193. //Same for rotation.
  194. stState.fPendingRotation = stState.fAccumRotation + (float)ucDeltaEventCount * ((float)(stState.usRecordInterval - (stState.ulEventTime % stState.usRecordInterval))) / ((float)usDeltaPeriod);
  195. stState.fAccumRotation = (float)ucDeltaEventCount * ((float)(ulNewEventTime % stState.usRecordInterval)) / ((float)usDeltaPeriod);
  196. stState.fGapRotation = (float)ucDeltaEventCount * ((unsigned short)stState.ucRecordGapCount * stState.usRecordInterval) / ((float)usDeltaPeriod);
  197. }
  198. else
  199. {
  200. // This event came in before the next record epoch started - this
  201. // will happen when the event period is less than the recording period.
  202. stState.fAccumEnergy += fEventEnergy;
  203. stState.fAccumRotation += (float)ucDeltaEventCount;
  204. stState.fPendingEnergy = 0;
  205. stState.fPendingRotation = 0;
  206. stState.ucRecordGapCount = 0;
  207. }
  208. stState.ulEventTime += (unsigned long)usDeltaPeriod;
  209. if (((unsigned short)(stState.ulEventTime - stState.ulLastRecordTime)) >= stState.usRecordInterval)
  210. {
  211. RecordOutput(prrPtr, &stState);
  212. }
  213. else
  214. {
  215. // We've had an event that either didn't have a rotation associated
  216. // with it (no event time increment) or else it was within the
  217. // recording interval.
  218. if ((dTime_ - stState.dLastRecordTime) > dRecordInterval)
  219. {
  220. while ((dTime_ - stState.dLastRecordTime) > dRecordInterval)
  221. {
  222. stState.dLastRecordTime += ((double)stState.usRecordInterval) / CTF_TIME_QUANTIZATION;
  223. (prrPtr)(stState.dLastRecordTime, stState.dTotalRotation, stState.dTotalEnergy, 0.0, 0.0);
  224. }
  225. }
  226. }
  227. // Propagate the message state information.
  228. stState.ucLastEventCount = ucCurrentEventCount;
  229. stState.ucLastRotationTicks = ucCurrentEventCount;
  230. stState.usLastAccumPeriod = usCurrentTimeStamp;
  231. stState.usLastAccumTorque = usCurrentTorqueTicks;
  232. }
  233. void DecodeCrankTorqueFreq_Calibration(double dTime_, unsigned char messagePayload_[])
  234. {
  235. if (messagePayload_[CALIBRATION_ID_BYTE] != ANT_CTF_CALIBRATION_ID)
  236. {
  237. // bad message
  238. return;
  239. }
  240. switch (messagePayload_[ANT_CTF_CAL_TYPE_BYTE])
  241. {
  242. case ANT_CTF_CAL_ZERO:
  243. // Tricky part here is that we don't have a good way to qualify this
  244. // offset with respect to user actions, unless the input record were to
  245. // also capture head unit requests to the PM.
  246. stState.usTorqueOffset = messagePayload_[ANT_CTF_CAL_ZERO_LSB_BYTE];
  247. stState.usTorqueOffset += ((unsigned short)messagePayload_[ANT_CTF_CAL_ZERO_MSB_BYTE]) << 8;
  248. break;
  249. case ANT_CTF_CAL_SLOPE:
  250. break;
  251. case ANT_CTF_CAL_ESN:
  252. break;
  253. case ANT_CTF_CAL_ACK:
  254. break;
  255. default:
  256. break;
  257. }
  258. }