![](/media/cache/img/default_profile.jpg.50x50_q85.jpg)
repo time
Dependencies: mbed MAX14720 MAX30205 USBDevice
Diff: HspGuiSourceV301/HSPGui/Algorithm/MaximSKAlgorithmClass.cs
- Revision:
- 20:6d2af70c92ab
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/HspGuiSourceV301/HSPGui/Algorithm/MaximSKAlgorithmClass.cs Tue Apr 06 06:41:40 2021 +0000 @@ -0,0 +1,313 @@ +/******************************************************************************* +* Copyright (C) 2016 Maxim Integrated Products, Inc., All rights Reserved. +* +* This software is protected by copyright laws of the United States and +* of foreign countries. This material may also be protected by patent laws +* and technology transfer regulations of the United States and of foreign +* countries. This software is furnished under a license agreement and/or a +* nondisclosure agreement and may only be used or reproduced in accordance +* with the terms of those agreements. Dissemination of this information to +* any party or parties not specified in the license agreement and/or +* nondisclosure agreement is expressly prohibited. +* +* The above copyright notice and this permission notice shall be included +* in all copies or substantial portions of the Software. +* +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +* IN NO EVENT SHALL MAXIM INTEGRATED BE LIABLE FOR ANY CLAIM, DAMAGES +* OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, +* ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +* OTHER DEALINGS IN THE SOFTWARE. +* +* Except as contained in this notice, the name of Maxim Integrated +* Products, Inc. shall not be used except as stated in the Maxim Integrated +* Products, Inc. Branding Policy. +* +* The mere transfer of this software does not imply any licenses +* of trade secrets, proprietary technology, copyrights, patents, +* trademarks, maskwork rights, or any other form of intellectual +* property whatsoever. Maxim Integrated Products, Inc. retains all +* ownership rights. +******************************************************************************* +*/ + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +//------------------------------------------------------------------------------------------ +// OS24EVK-59 split into HeartRateApp EXE and MAX30101 DLL. +// Moved all MAX30101 DLL classes into namespace Maxim.MAX30101GUI +// Moved all HeartRateApp GUI classes into namespace Maxim.MAX30101 +// OS24EVK-59 Create separate project that builds Maxim.MAX30101GUI DLL library + +namespace Maxim.MAX30101GUI +{ + /// <summary> + /// Encapsulate the Maxim SKA Algorithm + /// </summary> + public class MaximSKAlgorithmClass : MaximAlgorithmClass + { + public MaximSKAlgorithmClass() + { + // https://jira.maxim-ic.com/browse/OS24EVK-40 remove "algorithm" from algorithm names + // https://jira.maxim-ic.com/browse/OS24EVK-45 Rename PBA / SKA algorithms + Name = "Maxim Example Algorithm 2 [SKA.dll]"; + } + + // https://jira.maxim-ic.com/browse/OS24EVK-15 any SKA dependencies? e.g. init MaximSKAlgorithmClass._cboPulseWidthIndex from (byte)(cboPulseWidth.SelectedIndex) + // public byte _cboPulseWidthIndex = 0; + + // Steve Koh's variables + int _heartRateBPMint; // alias HR + float _spO2Percentfloat; // alias fSPO2 + + const int _SKA_windowSize = 100; + const int _SKA_bufferSize = 500; + // https://jira.maxim-ic.com/browse/OS24EVK-15 MaximSKAlgorithmClass accumulate irBuf[0..] _SKA_bufferSize for SKA.SKA_work + int[] irBuf = new int[_SKA_bufferSize]; + int[] redBuf = new int[_SKA_bufferSize]; + int[] greenBuf = new int[_SKA_bufferSize]; + // https://jira.maxim-ic.com/browse/OS24EVK-35 Support HR from Green LED (SKA) + ArrayList _SKA_IRList = new ArrayList(); + ArrayList _SKA_tempIRList = new ArrayList(); + ArrayList _SKA_redList = new ArrayList(); + ArrayList _SKA_tempRedList = new ArrayList(); + ArrayList _SKA_greenList = new ArrayList(); + ArrayList _SKA_tempGreenList = new ArrayList(); + int _SKA_numSamplesSinceLastCalc; + int _SKA_elapsedSecond; + //int _SKA_validHRcount; + //int _SKA_invalidHRcount; + //int _SKA_validSPO2count; + //int _SKA_invalidSPO2count; + //int _SKA_lastValidHRtimestamp; + //int _SKA_lastValidSPO2timestamp; + // diagnostic: output from SKA algorithm filtering + float[] irAcBuf = new float[100]; + float[] redAcBuf = new float[100]; + // end of Steve Koh's variables + + /// <summary> + /// Clear any internal class members, + /// in response to startMonitorToolStripMenuItem + /// </summary> + override public void Clear() + { + _SKA_elapsedSecond = 1; + //_SKA_lastValidHRtimestamp = 1; + //_SKA_lastValidSPO2timestamp = 1; + //_SKA_validHRcount = 0; + //_SKA_invalidHRcount = 0; + //_SKA_validSPO2count = 0; + //_SKA_invalidSPO2count = 0; + + _SKA_IRList = new ArrayList(); + _SKA_tempIRList = new ArrayList(); + _SKA_redList = new ArrayList(); + _SKA_tempRedList = new ArrayList(); + _SKA_greenList = new ArrayList(); + _SKA_tempGreenList = new ArrayList(); + _SKA_numSamplesSinceLastCalc = 0; + // https://jira.maxim-ic.com/browse/OS24EVK-15 SKA need preload _SKA_IRList _SKA_bufferSize in startMonitorToolStripMenuItem_Click + for (int index = 0; index < _SKA_bufferSize; index++) + { + _SKA_IRList.Add(0); + _SKA_redList.Add(0); + _SKA_greenList.Add(0); + } + } + + /// <summary> + /// <para>InterfaceRedIRGreenLEDdataConsumer</para> + /// <para>Producer-Consumer data sink for + /// raw Red/IR/Green LED data. + /// Produced by MAX30101, consumed by algorithm. + /// </para> + /// </summary> + /// <param name="sampleNumber"></param> + /// <param name="rawIR"></param> + /// <param name="rawRed"></param> + /// <param name="rawGreen"></param> + /// <param name="rawIRvalid"></param> + /// <param name="rawRedvalid"></param> + /// <param name="rawGreenvalid"></param> + override public void ConsumeRedIRGreenLEDdata( + int sampleNumber, + int rawIR, + int rawRed, + int rawGreen, + bool rawIRvalid, + bool rawRedvalid, + bool rawGreenvalid + ) + { + base.ConsumeRedIRGreenLEDdata(sampleNumber, rawIR, rawRed, rawGreen, rawIRvalid, rawRedvalid, rawGreenvalid); + + bool valid_IR = rawIRvalid; + bool valid_Red = rawRedvalid; + bool valid_Green = rawGreenvalid; + // https://jira.maxim-ic.com/browse/OS24EVK-25 SpO2 mode if LED mode and Red and IR data + bool HRvalid = (valid_Red || valid_Green); + bool SPO2valid = (valid_Red && valid_IR); + // + // example: https://jira.maxim-ic.com/browse/OS24EVK-37 SpO2 Temperature Compensation + #region SpO2_Temperature_Compensation + // SpO2 Temperature Compensation (See MAX30101 data sheet, Applications Information) + // Estimate red LED temperature rise from peak current and pulse width configuration settings, + // then use estimated red LED temperature to estimate actual red LED wavelength. + // + // double red_LED_temperature_delta = lookup table from peak current and duty cycle + // See MAX30101 data sheet Table 13. RED LED Current Settings vs LED Temperature Rise + // + // double red_LED_temperatureDegreesC = base._temperatureDegreesC + red_LED_temperature_delta; + // + // double red_LED_wavelength = lookup table from red_LED_temperatureDegreesC + // See MAX30101 data sheet toc10 RED LED PEAK WAVELENGTH vs TEMPERATURE + // + #endregion SpO2_Temperature_Compensation + + double heartRateBPM; // = (double)heartRateBPMint + bool heartRateBPMValid = false; + double heartRateBPMSignalStrength = 0; + double spO2Percent; // = (double)spO2Percentfloat + bool spO2PercentValid = false; + double spO2PercentSignalStrength = 0; // = (double)SPO2error + float SPO2error = 0; + // https://jira.maxim-ic.com/browse/OS24EVK-15 move SKA algorithm into MaximSKAlgorithmClass + + // https://jira.maxim-ic.com/browse/OS24EVK-15 MaximSKAlgorithmClass accumulate irBuf[0..] _SKA_bufferSize for SKA.SKA_work + // int[] irBuf = new int[_SKA_bufferSize]; + // int[] redBuf = new int[_SKA_bufferSize]; + for (int index = 0; index < _SKA_tempRedList.Count; index++) + { + _SKA_IRList.Add((int)_SKA_tempIRList[index]); + _SKA_IRList.RemoveAt(0); + _SKA_redList.Add((int)_SKA_tempRedList[index]); + _SKA_redList.RemoveAt(0); + _SKA_greenList.Add((int)_SKA_tempGreenList[index]); + _SKA_greenList.RemoveAt(0); + _SKA_numSamplesSinceLastCalc++; + } + _SKA_tempIRList.Clear(); + _SKA_tempRedList.Clear(); + _SKA_tempGreenList.Clear(); + // + if (_SKA_numSamplesSinceLastCalc < _SKA_windowSize) + { + _SKA_IRList.Add(rawIR); + _SKA_IRList.RemoveAt(0); + _SKA_redList.Add(rawRed); + _SKA_redList.RemoveAt(0); + _SKA_greenList.Add(rawGreen); + _SKA_greenList.RemoveAt(0); + } + else + { + _SKA_tempIRList.Add(rawIR); + _SKA_tempRedList.Add(rawRed); + _SKA_tempGreenList.Add(rawGreen); + } + _SKA_numSamplesSinceLastCalc++; + // + if (_SKA_numSamplesSinceLastCalc >= _SKA_windowSize) + { + _SKA_numSamplesSinceLastCalc = 0; + for (int index = 0; index < _SKA_IRList.Count; index++) + { + irBuf[index] = (int)_SKA_IRList[index]; + redBuf[index] = (int)_SKA_redList[index]; + greenBuf[index] = (int)_SKA_greenList[index]; + } + + // Steve Koh's algorithm uses Fs=100Sps. It processes 500 samples at a time. Shift in 100 new samples before the function is called. + // Since we're running at 100Sps, this routine is called every 100 samples. + // 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. + // + // Heart Rate/SpO2 Monitor function takes 18bit input irBuffer[500] and redBuffer[500]. + // Inputs: + // int lapsedSecond -> _SKA_elapsedSecond must be >3 before anything happens + // const int irBuffer[500] -> irBuf + // const int redBuffer[500] -> redBuf + // Outputs: + // unsigned int *heartRate -> HR Heart Rate in beats per minute. + // float *SPO2 -> fSPO2 SpO2 value as %saturation. + // boolean_T *SPO2valid -> SPO2valid + // boolean_T *HRvalid -> HRvalid + // float *SPO2errorStatus -> SPO2error looks like a signal strength? + // float last100Ir[100] -> irAcBuf + // float last100Red[100] -> redAcBuf + // + if (valid_Green && !valid_IR) // Green mode - green into IR channel + { + // https://jira.maxim-ic.com/browse/OS24EVK-15 move SKA algorithm into MaximSKAlgorithmClass + // https://jira.maxim-ic.com/browse/OS24EVK-35 Support HR from Green LED (SKA Algorithm) + // for SKA green LED support, looks like we have to preload redBuf with Green LED data... + // no IR, but we can use Green instead for HR. (No SpO2 measurement so both channels Green.) + SKA.SKA_work( + _SKA_elapsedSecond, + greenBuf, + greenBuf, + ref _heartRateBPMint, + ref _spO2Percentfloat, + ref spO2PercentValid, + ref heartRateBPMValid, + ref SPO2error, + irAcBuf, + redAcBuf); + } + else if (valid_Red && !valid_IR && !valid_Green) // HR mode - red into IR channel + { + SKA.SKA_work( + _SKA_elapsedSecond, + redBuf, + redBuf, + ref _heartRateBPMint, + ref _spO2Percentfloat, + ref spO2PercentValid, + ref heartRateBPMValid, + ref SPO2error, + irAcBuf, + redAcBuf); + } + else // SpO2 mode + { + // https://jira.maxim-ic.com/browse/OS24EVK-15 move SKA algorithm into MaximSKAlgorithmClass + // normal HR from Red, SpO2 if IR is available + // SKA.SKA_work(_SKA_elapsedSecond, irBuf, redBuf, ref HR, ref fSPO2, ref SPO2valid, ref HRvalid, ref SPO2error, irAcBuf, redAcBuf); + SKA.SKA_work( + _SKA_elapsedSecond, + irBuf, + redBuf, + ref _heartRateBPMint, + ref _spO2Percentfloat, + ref spO2PercentValid, + ref heartRateBPMValid, + ref SPO2error, + irAcBuf, + redAcBuf); + } + _SKA_elapsedSecond++; + + + // https://jira.maxim-ic.com/browse/OS24EVK-15 SKA algorithm report results by InterfaceHeartRateSpO2dataConsumer.ConsumeHeartRateSpO2data(heartRateBPM, ...) + heartRateBPM = (double)_heartRateBPMint; + spO2Percent = (double)_spO2Percentfloat; + // OS24EVK-35 don't report SpO2 if there was not valid red and IR data (i.e. green only) + if (!(valid_Red && valid_IR)) { spO2PercentValid = false; } + spO2PercentSignalStrength = (double)SPO2error; + // https://jira.maxim-ic.com/browse/OS24EVK-15 SKAAlgorithm reporting SpO2 999 as if that was valid. + // SKA Algorithm reports SpO2 bogus value 999% as valid; need to determine why reported as a valid result. + // spO2PercentValid = (spO2PercentSignalStrength > 0); + // don't modify spO2PercentValid; already passed by ref from SKA.SKA_work() + ReportResultsHeartRateSpO2dataAvailable(heartRateBPM, heartRateBPMValid, heartRateBPMSignalStrength, spO2Percent, spO2PercentValid, spO2PercentSignalStrength); + System.Diagnostics.Debug.Print("heartRateBPM: " + heartRateBPM + " heartRateBPMValid: " + heartRateBPMValid + " spO2Percent: " + spO2Percent + " spO2PercentValid: " + spO2PercentValid); + } // if (_SKA_numSamplesSinceLastCalc >= _SKA_windowSize) + } + + } +}