repo time
Dependencies: mbed MAX14720 MAX30205 USBDevice
HspGuiSourceV301/HSPGui/Algorithm/MaximSKAlgorithmClass.cs
- Committer:
- darienf
- Date:
- 2021-04-06
- Revision:
- 20:6d2af70c92ab
File content as of revision 20:6d2af70c92ab:
/******************************************************************************* * 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) } } }