repo time

Dependencies:   mbed MAX14720 MAX30205 USBDevice

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)
+        }
+   
+    }
+}