Important changes to repositories hosted on mbed.com
Mbed hosted mercurial repositories are deprecated and are due to be permanently deleted in July 2026.
To keep a copy of this software download the repository Zip archive or clone locally using Mercurial.
It is also possible to export all your personal repositories from the account settings page.
Dependencies: mbed MAX14720 MAX30205 USBDevice
MaximSKAlgorithmClass.cs
00001 /******************************************************************************* 00002 * Copyright (C) 2016 Maxim Integrated Products, Inc., All rights Reserved. 00003 * 00004 * This software is protected by copyright laws of the United States and 00005 * of foreign countries. This material may also be protected by patent laws 00006 * and technology transfer regulations of the United States and of foreign 00007 * countries. This software is furnished under a license agreement and/or a 00008 * nondisclosure agreement and may only be used or reproduced in accordance 00009 * with the terms of those agreements. Dissemination of this information to 00010 * any party or parties not specified in the license agreement and/or 00011 * nondisclosure agreement is expressly prohibited. 00012 * 00013 * The above copyright notice and this permission notice shall be included 00014 * in all copies or substantial portions of the Software. 00015 * 00016 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 00017 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 00018 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 00019 * IN NO EVENT SHALL MAXIM INTEGRATED BE LIABLE FOR ANY CLAIM, DAMAGES 00020 * OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 00021 * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 00022 * OTHER DEALINGS IN THE SOFTWARE. 00023 * 00024 * Except as contained in this notice, the name of Maxim Integrated 00025 * Products, Inc. shall not be used except as stated in the Maxim Integrated 00026 * Products, Inc. Branding Policy. 00027 * 00028 * The mere transfer of this software does not imply any licenses 00029 * of trade secrets, proprietary technology, copyrights, patents, 00030 * trademarks, maskwork rights, or any other form of intellectual 00031 * property whatsoever. Maxim Integrated Products, Inc. retains all 00032 * ownership rights. 00033 ******************************************************************************* 00034 */ 00035 00036 using System; 00037 using System.Collections; 00038 using System.Collections.Generic; 00039 using System.Linq; 00040 using System.Text; 00041 00042 //------------------------------------------------------------------------------------------ 00043 // OS24EVK-59 split into HeartRateApp EXE and MAX30101 DLL. 00044 // Moved all MAX30101 DLL classes into namespace Maxim.MAX30101GUI 00045 // Moved all HeartRateApp GUI classes into namespace Maxim.MAX30101 00046 // OS24EVK-59 Create separate project that builds Maxim.MAX30101GUI DLL library 00047 00048 namespace Maxim.MAX30101GUI 00049 { 00050 /// <summary> 00051 /// Encapsulate the Maxim SKA Algorithm 00052 /// </summary> 00053 public class MaximSKAlgorithmClass : MaximAlgorithmClass 00054 { 00055 public MaximSKAlgorithmClass() 00056 { 00057 // https://jira.maxim-ic.com/browse/OS24EVK-40 remove "algorithm" from algorithm names 00058 // https://jira.maxim-ic.com/browse/OS24EVK-45 Rename PBA / SKA algorithms 00059 Name = "Maxim Example Algorithm 2 [SKA.dll]"; 00060 } 00061 00062 // https://jira.maxim-ic.com/browse/OS24EVK-15 any SKA dependencies? e.g. init MaximSKAlgorithmClass._cboPulseWidthIndex from (byte)(cboPulseWidth.SelectedIndex) 00063 // public byte _cboPulseWidthIndex = 0; 00064 00065 // Steve Koh's variables 00066 int _heartRateBPMint; // alias HR 00067 float _spO2Percentfloat; // alias fSPO2 00068 00069 const int _SKA_windowSize = 100; 00070 const int _SKA_bufferSize = 500; 00071 // https://jira.maxim-ic.com/browse/OS24EVK-15 MaximSKAlgorithmClass accumulate irBuf[0..] _SKA_bufferSize for SKA.SKA_work 00072 int[] irBuf = new int[_SKA_bufferSize]; 00073 int[] redBuf = new int[_SKA_bufferSize]; 00074 int[] greenBuf = new int[_SKA_bufferSize]; 00075 // https://jira.maxim-ic.com/browse/OS24EVK-35 Support HR from Green LED (SKA) 00076 ArrayList _SKA_IRList = new ArrayList(); 00077 ArrayList _SKA_tempIRList = new ArrayList(); 00078 ArrayList _SKA_redList = new ArrayList(); 00079 ArrayList _SKA_tempRedList = new ArrayList(); 00080 ArrayList _SKA_greenList = new ArrayList(); 00081 ArrayList _SKA_tempGreenList = new ArrayList(); 00082 int _SKA_numSamplesSinceLastCalc; 00083 int _SKA_elapsedSecond; 00084 //int _SKA_validHRcount; 00085 //int _SKA_invalidHRcount; 00086 //int _SKA_validSPO2count; 00087 //int _SKA_invalidSPO2count; 00088 //int _SKA_lastValidHRtimestamp; 00089 //int _SKA_lastValidSPO2timestamp; 00090 // diagnostic: output from SKA algorithm filtering 00091 float[] irAcBuf = new float[100]; 00092 float[] redAcBuf = new float[100]; 00093 // end of Steve Koh's variables 00094 00095 /// <summary> 00096 /// Clear any internal class members, 00097 /// in response to startMonitorToolStripMenuItem 00098 /// </summary> 00099 override public void Clear() 00100 { 00101 _SKA_elapsedSecond = 1; 00102 //_SKA_lastValidHRtimestamp = 1; 00103 //_SKA_lastValidSPO2timestamp = 1; 00104 //_SKA_validHRcount = 0; 00105 //_SKA_invalidHRcount = 0; 00106 //_SKA_validSPO2count = 0; 00107 //_SKA_invalidSPO2count = 0; 00108 00109 _SKA_IRList = new ArrayList(); 00110 _SKA_tempIRList = new ArrayList(); 00111 _SKA_redList = new ArrayList(); 00112 _SKA_tempRedList = new ArrayList(); 00113 _SKA_greenList = new ArrayList(); 00114 _SKA_tempGreenList = new ArrayList(); 00115 _SKA_numSamplesSinceLastCalc = 0; 00116 // https://jira.maxim-ic.com/browse/OS24EVK-15 SKA need preload _SKA_IRList _SKA_bufferSize in startMonitorToolStripMenuItem_Click 00117 for (int index = 0; index < _SKA_bufferSize; index++) 00118 { 00119 _SKA_IRList.Add(0); 00120 _SKA_redList.Add(0); 00121 _SKA_greenList.Add(0); 00122 } 00123 } 00124 00125 /// <summary> 00126 /// <para>InterfaceRedIRGreenLEDdataConsumer</para> 00127 /// <para>Producer-Consumer data sink for 00128 /// raw Red/IR/Green LED data. 00129 /// Produced by MAX30101, consumed by algorithm. 00130 /// </para> 00131 /// </summary> 00132 /// <param name="sampleNumber"></param> 00133 /// <param name="rawIR"></param> 00134 /// <param name="rawRed"></param> 00135 /// <param name="rawGreen"></param> 00136 /// <param name="rawIRvalid"></param> 00137 /// <param name="rawRedvalid"></param> 00138 /// <param name="rawGreenvalid"></param> 00139 override public void ConsumeRedIRGreenLEDdata( 00140 int sampleNumber, 00141 int rawIR, 00142 int rawRed, 00143 int rawGreen, 00144 bool rawIRvalid, 00145 bool rawRedvalid, 00146 bool rawGreenvalid 00147 ) 00148 { 00149 base.ConsumeRedIRGreenLEDdata(sampleNumber, rawIR, rawRed, rawGreen, rawIRvalid, rawRedvalid, rawGreenvalid); 00150 00151 bool valid_IR = rawIRvalid; 00152 bool valid_Red = rawRedvalid; 00153 bool valid_Green = rawGreenvalid; 00154 // https://jira.maxim-ic.com/browse/OS24EVK-25 SpO2 mode if LED mode and Red and IR data 00155 bool HRvalid = (valid_Red || valid_Green); 00156 bool SPO2valid = (valid_Red && valid_IR); 00157 // 00158 // example: https://jira.maxim-ic.com/browse/OS24EVK-37 SpO2 Temperature Compensation 00159 #region SpO2_Temperature_Compensation 00160 // SpO2 Temperature Compensation (See MAX30101 data sheet, Applications Information) 00161 // Estimate red LED temperature rise from peak current and pulse width configuration settings, 00162 // then use estimated red LED temperature to estimate actual red LED wavelength. 00163 // 00164 // double red_LED_temperature_delta = lookup table from peak current and duty cycle 00165 // See MAX30101 data sheet Table 13. RED LED Current Settings vs LED Temperature Rise 00166 // 00167 // double red_LED_temperatureDegreesC = base._temperatureDegreesC + red_LED_temperature_delta; 00168 // 00169 // double red_LED_wavelength = lookup table from red_LED_temperatureDegreesC 00170 // See MAX30101 data sheet toc10 RED LED PEAK WAVELENGTH vs TEMPERATURE 00171 // 00172 #endregion SpO2_Temperature_Compensation 00173 00174 double heartRateBPM; // = (double)heartRateBPMint 00175 bool heartRateBPMValid = false; 00176 double heartRateBPMSignalStrength = 0; 00177 double spO2Percent; // = (double)spO2Percentfloat 00178 bool spO2PercentValid = false; 00179 double spO2PercentSignalStrength = 0; // = (double)SPO2error 00180 float SPO2error = 0; 00181 // https://jira.maxim-ic.com/browse/OS24EVK-15 move SKA algorithm into MaximSKAlgorithmClass 00182 00183 // https://jira.maxim-ic.com/browse/OS24EVK-15 MaximSKAlgorithmClass accumulate irBuf[0..] _SKA_bufferSize for SKA.SKA_work 00184 // int[] irBuf = new int[_SKA_bufferSize]; 00185 // int[] redBuf = new int[_SKA_bufferSize]; 00186 for (int index = 0; index < _SKA_tempRedList.Count; index++) 00187 { 00188 _SKA_IRList.Add((int)_SKA_tempIRList[index]); 00189 _SKA_IRList.RemoveAt(0); 00190 _SKA_redList.Add((int)_SKA_tempRedList[index]); 00191 _SKA_redList.RemoveAt(0); 00192 _SKA_greenList.Add((int)_SKA_tempGreenList[index]); 00193 _SKA_greenList.RemoveAt(0); 00194 _SKA_numSamplesSinceLastCalc++; 00195 } 00196 _SKA_tempIRList.Clear(); 00197 _SKA_tempRedList.Clear(); 00198 _SKA_tempGreenList.Clear(); 00199 // 00200 if (_SKA_numSamplesSinceLastCalc < _SKA_windowSize) 00201 { 00202 _SKA_IRList.Add(rawIR); 00203 _SKA_IRList.RemoveAt(0); 00204 _SKA_redList.Add(rawRed); 00205 _SKA_redList.RemoveAt(0); 00206 _SKA_greenList.Add(rawGreen); 00207 _SKA_greenList.RemoveAt(0); 00208 } 00209 else 00210 { 00211 _SKA_tempIRList.Add(rawIR); 00212 _SKA_tempRedList.Add(rawRed); 00213 _SKA_tempGreenList.Add(rawGreen); 00214 } 00215 _SKA_numSamplesSinceLastCalc++; 00216 // 00217 if (_SKA_numSamplesSinceLastCalc >= _SKA_windowSize) 00218 { 00219 _SKA_numSamplesSinceLastCalc = 0; 00220 for (int index = 0; index < _SKA_IRList.Count; index++) 00221 { 00222 irBuf[index] = (int)_SKA_IRList[index]; 00223 redBuf[index] = (int)_SKA_redList[index]; 00224 greenBuf[index] = (int)_SKA_greenList[index]; 00225 } 00226 00227 // Steve Koh's algorithm uses Fs=100Sps. It processes 500 samples at a time. Shift in 100 new samples before the function is called. 00228 // Since we're running at 100Sps, this routine is called every 100 samples. 00229 // Although _SKA_elapsedSecond = num samples / Fs, Steve used an integer type and so only Fs=100Sps or 50Sps would work. Regardless, we are using fixed 100Sps. 00230 // 00231 // Heart Rate/SpO2 Monitor function takes 18bit input irBuffer[500] and redBuffer[500]. 00232 // Inputs: 00233 // int lapsedSecond -> _SKA_elapsedSecond must be >3 before anything happens 00234 // const int irBuffer[500] -> irBuf 00235 // const int redBuffer[500] -> redBuf 00236 // Outputs: 00237 // unsigned int *heartRate -> HR Heart Rate in beats per minute. 00238 // float *SPO2 -> fSPO2 SpO2 value as %saturation. 00239 // boolean_T *SPO2valid -> SPO2valid 00240 // boolean_T *HRvalid -> HRvalid 00241 // float *SPO2errorStatus -> SPO2error looks like a signal strength? 00242 // float last100Ir[100] -> irAcBuf 00243 // float last100Red[100] -> redAcBuf 00244 // 00245 if (valid_Green && !valid_IR) // Green mode - green into IR channel 00246 { 00247 // https://jira.maxim-ic.com/browse/OS24EVK-15 move SKA algorithm into MaximSKAlgorithmClass 00248 // https://jira.maxim-ic.com/browse/OS24EVK-35 Support HR from Green LED (SKA Algorithm) 00249 // for SKA green LED support, looks like we have to preload redBuf with Green LED data... 00250 // no IR, but we can use Green instead for HR. (No SpO2 measurement so both channels Green.) 00251 SKA.SKA_work( 00252 _SKA_elapsedSecond, 00253 greenBuf, 00254 greenBuf, 00255 ref _heartRateBPMint, 00256 ref _spO2Percentfloat, 00257 ref spO2PercentValid, 00258 ref heartRateBPMValid, 00259 ref SPO2error, 00260 irAcBuf, 00261 redAcBuf); 00262 } 00263 else if (valid_Red && !valid_IR && !valid_Green) // HR mode - red into IR channel 00264 { 00265 SKA.SKA_work( 00266 _SKA_elapsedSecond, 00267 redBuf, 00268 redBuf, 00269 ref _heartRateBPMint, 00270 ref _spO2Percentfloat, 00271 ref spO2PercentValid, 00272 ref heartRateBPMValid, 00273 ref SPO2error, 00274 irAcBuf, 00275 redAcBuf); 00276 } 00277 else // SpO2 mode 00278 { 00279 // https://jira.maxim-ic.com/browse/OS24EVK-15 move SKA algorithm into MaximSKAlgorithmClass 00280 // normal HR from Red, SpO2 if IR is available 00281 // SKA.SKA_work(_SKA_elapsedSecond, irBuf, redBuf, ref HR, ref fSPO2, ref SPO2valid, ref HRvalid, ref SPO2error, irAcBuf, redAcBuf); 00282 SKA.SKA_work( 00283 _SKA_elapsedSecond, 00284 irBuf, 00285 redBuf, 00286 ref _heartRateBPMint, 00287 ref _spO2Percentfloat, 00288 ref spO2PercentValid, 00289 ref heartRateBPMValid, 00290 ref SPO2error, 00291 irAcBuf, 00292 redAcBuf); 00293 } 00294 _SKA_elapsedSecond++; 00295 00296 00297 // https://jira.maxim-ic.com/browse/OS24EVK-15 SKA algorithm report results by InterfaceHeartRateSpO2dataConsumer.ConsumeHeartRateSpO2data(heartRateBPM, ...) 00298 heartRateBPM = (double)_heartRateBPMint; 00299 spO2Percent = (double)_spO2Percentfloat; 00300 // OS24EVK-35 don't report SpO2 if there was not valid red and IR data (i.e. green only) 00301 if (!(valid_Red && valid_IR)) { spO2PercentValid = false; } 00302 spO2PercentSignalStrength = (double)SPO2error; 00303 // https://jira.maxim-ic.com/browse/OS24EVK-15 SKAAlgorithm reporting SpO2 999 as if that was valid. 00304 // SKA Algorithm reports SpO2 bogus value 999% as valid; need to determine why reported as a valid result. 00305 // spO2PercentValid = (spO2PercentSignalStrength > 0); 00306 // don't modify spO2PercentValid; already passed by ref from SKA.SKA_work() 00307 ReportResultsHeartRateSpO2dataAvailable(heartRateBPM, heartRateBPMValid, heartRateBPMSignalStrength, spO2Percent, spO2PercentValid, spO2PercentSignalStrength); 00308 System.Diagnostics.Debug.Print("heartRateBPM: " + heartRateBPM + " heartRateBPMValid: " + heartRateBPMValid + " spO2Percent: " + spO2Percent + " spO2PercentValid: " + spO2PercentValid); 00309 } // if (_SKA_numSamplesSinceLastCalc >= _SKA_windowSize) 00310 } 00311 00312 } 00313 }
Generated on Thu Jul 28 2022 18:07:15 by
1.7.2