repo time

Dependencies:   mbed MAX14720 MAX30205 USBDevice

Revision:
20:6d2af70c92ab
diff -r 002c398f14cc -r 6d2af70c92ab HspGuiSourceV301/HSPGui/CustomControls/OpticalView.cs
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/HspGuiSourceV301/HSPGui/CustomControls/OpticalView.cs	Tue Apr 06 06:41:40 2021 +0000
@@ -0,0 +1,1366 @@
+/*******************************************************************************
+* 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.
+*******************************************************************************
+*/
+
+#define USE_MEDICALCHARTHELPER
+//#define CES_DEMO
+
+using System;
+using System.Collections.Generic;
+using System.Collections;
+using System.ComponentModel;
+using System.Drawing;
+using System.Data;
+using System.Linq;
+using System.Text;
+using System.Windows.Forms;
+using RPCSupport.Streaming;
+using Maxim.Charting;
+using System.Threading;
+using HealthSensorPlatform.View;
+
+using System.Windows.Forms.DataVisualization.Charting;
+
+// DEBUG
+using System.IO;
+
+namespace HealthSensorPlatform.CustomControls
+{
+    public partial class OpticalView : UserControl, IDeviceView, IOpticalAlgorithmView
+    {
+        /* Constant Fields */
+        static string StartString = "Start Monitor";
+        static string StopString = "Stop Monitor";
+
+        const int ChartTime = 4; // 4 seconds
+
+        /* Fields */
+        private bool connected = false;
+        private bool streaming = false;
+
+        private RPCSupport.RPCClient rpcClient;
+        //private MedicalChartHelper medicalChartLED;
+        //private MedicalChartHelper medicalChartAccel;
+        private AutoScaleCalculator fifo1Calc;
+        private AutoScaleCalculator fifo2Calc;
+        private AutoScaleCalculator fifo3Calc;
+
+        eStreamMode streamMode;
+        byte fifo_waterlevel_mark = 0x0f;
+        byte sample_avg;
+        byte sample_rate;
+        byte pulse_width;
+        byte red_led_current;
+        byte ir_led_current;
+        byte green_led_current;
+        byte slot_1;
+        byte slot_2;
+        byte slot_3;
+        byte slot_4;
+
+        // Heart Rate Sensor
+        int points; // Number of x-axis points for a plot
+        int sampleRate; // Human readable sample rate
+        int sampleAverage; // Human readable number of average points
+        // LIS2HD
+        int sampleRateAccel; 
+        int pointsAccel;
+
+        EventHandler<PartialArrayIntAvailableEventArgs> appendChart; // Allows event to be unregistered
+
+        Dictionary<string, MedianFilter> intervalDict = new Dictionary<string, MedianFilter>();
+        Dictionary<string, AutoScaleCalculator> chartScaleDict = new Dictionary<string, AutoScaleCalculator>();
+
+        List<Control> streamingControls = new List<Control>();
+
+#if CES_DEMO
+        // Algorithm Data
+        Queue red;
+        Queue ir;
+        Queue led;
+        // struct to run algorithms
+        AlgorithmMobileBU.AlgorithmConfiguration config;
+        AlgorithmMobileBU.AlgorithmOutput output;
+        int algoCounter = 0;
+#endif
+
+        // DEBUG
+        //StreamWriter file = new StreamWriter("hsp_output_data.csv");
+
+        /* Constructors */
+        public OpticalView()
+        {
+            InitializeComponent();
+
+            OpticalSensorInitControls();
+
+            cboSampleRate.SelectedIndexChanged += new EventHandler(cboSRPWLed_SelectedIndexChanged);
+            cboPulseWidth.SelectedIndexChanged += new EventHandler(cboSRPWLed_SelectedIndexChanged);
+            cboRedLED.SelectedIndexChanged += new EventHandler(cboSRPWLed_SelectedIndexChanged);
+            cboIRLED.SelectedIndexChanged += new EventHandler(cboSRPWLed_SelectedIndexChanged);
+            cboGreenLED.SelectedIndexChanged += new EventHandler(cboSRPWLed_SelectedIndexChanged);
+
+            cboSampleRate.SelectedIndexChanged += new EventHandler(cboSampleRate_SelectedIndexChanged);
+            cboSampleAvg.SelectedIndexChanged += new EventHandler(cboSampleRate_SelectedIndexChanged);
+
+            streamingControls.AddRange(new Control[] { panel1, maximGroupBoxSettings, maximGroupBoxLEDcurrents, maximGroupBoxLEDTimingSlots, btnDefaults });
+#if CES_DEMO
+            // Algorithm 
+            red = new Queue();
+            ir = new Queue();
+            led = new Queue();
+
+            config = new AlgorithmMobileBU.AlgorithmConfiguration();
+            output = new AlgorithmMobileBU.AlgorithmOutput();
+
+            config.snrNoFingerThreshold = -9;
+            config.acLowPerfusionThreshold = 950;
+            config.isAGC = 0;
+
+            //updateAlgorithm();           
+            lblHeartRate.Visible = true;
+            lblHeartRateText.Visible = true;
+#endif
+        }
+
+        /* Delegates */
+        //public delegate void StreamingStartStopEventHandler(StreamingStartStopEventArgs e);
+
+        /* Events */
+        //public event StreamingStartStopEventHandler StreamingStartStop;
+        public event EventHandler<StreamingStartStopEventArgs> StreamingStartStop;
+
+        /* Enums */
+        public enum eStreamMode
+        {
+            eHR,
+            eSPO2,
+            eMulti
+        }
+
+        /* Properties */
+        public RPCSupport.RPCClient RPCClient
+        {
+            set
+            {
+                rpcClient = value;
+                appendChart = new EventHandler<PartialArrayIntAvailableEventArgs>(On_AppendChart);
+                rpcClient.streaming.PartialArrayIntAvailable += appendChart;
+            }
+        }
+
+        public bool Connected
+        {
+            get
+            {
+                return connected;
+            }
+            set
+            {
+                connected = value;
+                if (connected == false)
+                    if (btnMonitoring.Text == StopString)
+                        streamingStartStop();
+            }
+        }
+
+        public int AccelSampleRate
+        {
+            get
+            {
+                return sampleRateAccel;
+            }
+        }
+
+        public int OpticalSampleRate
+        {
+            get
+            {
+                return sampleRate / sampleAverage;
+            }
+        }
+        
+        public eStreamMode ModeConfiguration
+        {
+            get
+            {
+                return streamMode;
+            }
+        }
+
+        /* Methods */
+
+        // Clean up streaming 
+        public void Close()
+        {
+            rpcClient.streaming.PartialArrayIntAvailable -= appendChart; // Stop event from firing
+            // Disable streaming if enabled
+            if (btnMonitoring.Text == StopString)
+                streamingStartStop();
+        }
+
+        public void DisplayAlgorithmResult(double heartRateBPM, bool heartRateBPMValid, double heartRateBPMSignalStrength, 
+            double spO2Percent, bool spO2PercentValid, double spO2PercentSignalStrength)
+        {
+            // 100Hz only - TODO
+            if (OpticalSampleRate != 100)
+                return;
+
+            if (heartRateBPMValid)
+            {
+                lblHeartRate.ForeColor = SystemColors.ControlText;
+                lblHeartRate.Text = string.Format("{0}", heartRateBPM * (sampleRate / 100.0)); // 100.0 for default sample rate
+            }
+            else
+            {
+                lblHeartRate.ForeColor = Color.Gray;
+            }
+
+            if (spO2PercentValid)
+            {
+                lblSpO2.ForeColor = SystemColors.ControlText;
+                lblSpO2.Text = string.Format("{0:0.0}", spO2Percent);
+            }
+            else
+            {
+                lblSpO2.ForeColor = Color.Gray;
+            }
+        }
+
+        public void DisplayAlgorithmReset()
+        {
+            lblHeartRate.Text = "----";
+            lblHeartRate.ForeColor = Color.Gray;
+
+            lblSpO2.Text = "----";
+            lblSpO2.ForeColor = Color.Gray;
+        }
+
+        System.Windows.Forms.Timer timer = new System.Windows.Forms.Timer();
+        void InitGraphs()
+        {
+            /*
+            medicalChartLED = new MedicalChartHelper(chartLED,
+                "ChartArea1Red", "SeriesRed", "Red ADC Code",
+                "ChartArea2IR", "SeriesIR", "IR ADC Code",
+                "ChartArea3Green", "SeriesGreen", "Green ADC Code",
+                MedicalChartHelper.DataFormats.FormatUnsigned);
+            medicalChartLED._plotPoints = 200;
+            medicalChartAccel = new MedicalChartHelper(chartAccel,
+                "ChartArea4AccelX", "SeriesAccelX", "Accelerometer X",
+                "ChartArea5AccelY", "SeriesAccelY", "Accelerometer Y",
+                "ChartArea6AccelZ", "SeriesAccelZ", "Accelerometer Z",
+                MedicalChartHelper.DataFormats.Format16bit2sComplement);
+            */
+
+            MedianFilter fifo1 = new MedianFilter(10);
+            MedianFilter fifo2 = new MedianFilter(10);
+            MedianFilter fifo3 = new MedianFilter(10);
+            MedianFilter accelx = new MedianFilter(10);
+            MedianFilter accely = new MedianFilter(10);
+            MedianFilter accelz = new MedianFilter(10);
+
+            intervalDict.Add("ChartArea1Red", fifo1);
+            intervalDict.Add("ChartArea2IR", fifo2);
+            intervalDict.Add("ChartArea3Green", fifo3);
+
+            intervalDict.Add("ChartArea4AccelX", accelx);
+            intervalDict.Add("ChartArea5AccelY", accely);
+            intervalDict.Add("ChartArea6AccelZ", accelz);
+
+            fifo1Calc = new AutoScaleCalculator(0, 90000);
+            fifo1Calc.Minimum = 0;
+            fifo1Calc.Maximum = Math.Pow(2, 18);
+            fifo1Calc.Intervals = 5;
+            fifo1Calc.MinimumRange = 200;
+            fifo1Calc.ScaleTrigger = 0.8;
+            fifo1Calc.RescaleTargetRange = 0.25;
+            chartScaleDict.Add("ChartArea1Red", fifo1Calc);
+
+            fifo2Calc = new AutoScaleCalculator(0, 90000);
+            fifo2Calc.Minimum = 0;
+            fifo2Calc.Maximum = Math.Pow(2, 18);
+            fifo2Calc.Intervals = 5;
+            fifo2Calc.MinimumRange = 200;
+            fifo2Calc.ScaleTrigger = 0.8;
+            fifo2Calc.RescaleTargetRange = 0.25;
+            chartScaleDict.Add("ChartArea2IR", fifo2Calc);
+
+            fifo3Calc = new AutoScaleCalculator(0, 90000);
+            fifo3Calc.Minimum = 0;
+            fifo3Calc.Maximum = Math.Pow(2, 18);
+            fifo3Calc.Intervals = 5;
+            fifo3Calc.MinimumRange = 200;
+            fifo3Calc.ScaleTrigger = 0.8;
+            fifo3Calc.RescaleTargetRange = 0.25;
+            chartScaleDict.Add("ChartArea3Green", fifo2Calc);
+
+            AutoScaleCalculator accelXCalc = new AutoScaleCalculator(-10000, 10000);
+            accelXCalc.Minimum = -32768;
+            accelXCalc.Maximum = 32767;
+            accelXCalc.Intervals = 5;
+            accelXCalc.MinimumRange = 500;
+            accelXCalc.ScaleTrigger = 0.8;
+            accelXCalc.RescaleTargetRange = 0.25;
+            chartScaleDict.Add("ChartArea4AccelX", accelXCalc);
+
+            AutoScaleCalculator accelYCalc = new AutoScaleCalculator(-10000, 10000);
+            accelYCalc.Minimum = -32768;
+            accelYCalc.Maximum = 32767;
+            accelYCalc.Intervals = 5;
+            accelYCalc.MinimumRange = 500;
+            accelYCalc.ScaleTrigger = 0.8;
+            accelYCalc.RescaleTargetRange = 0.25;
+            chartScaleDict.Add("ChartArea5AccelY", accelYCalc);
+
+            AutoScaleCalculator accelZCalc = new AutoScaleCalculator(-10000, 10000);
+            accelZCalc.Minimum = -32768;
+            accelZCalc.Maximum = 32767;
+            accelZCalc.Intervals = 5;
+            accelZCalc.MinimumRange = 500;
+            accelZCalc.ScaleTrigger = 0.8;
+            accelZCalc.RescaleTargetRange = 0.25;
+            chartScaleDict.Add("ChartArea6AccelZ", accelZCalc);
+
+            /*
+            Random rand = new Random();
+            int[] data1 = new int[10];
+            int[] data2 = new int[10];
+            int[] data3 = new int[10];
+            double[] data1_ = new double[10];
+            double[] data2_ = new double[10];
+            double[] data3_ = new double[10];
+            for (int i = 0; i < data1.Length; i++)
+            {
+                data1[i] = rand.Next(10);
+                data2[i] = rand.Next(10);
+                data3[i] = rand.Next(10);
+                data1_[i] = rand.NextDouble() * 10;
+                data2_[i] = rand.NextDouble() * 10;
+                data3_[i] = rand.NextDouble() * 10;
+            }
+            */
+
+            /*
+            int[] dummyData = new int[] { 0 };
+            medicalChartLED.AppendDataChartArea1(dummyData, 0, 0);
+            medicalChartLED.AppendDataChartArea2(dummyData, 0, 0);
+            medicalChartLED.AppendDataChartArea3(dummyData, 0, 0);
+            //medicalChartAccel._xCount1++;
+            //medicalChartAccel._xCount2++;
+            //medicalChartAccel._xCount3++;
+            medicalChartAccel.AppendDataChartArea1(dummyData, 0, 0);
+            medicalChartAccel.AppendDataChartArea2(dummyData, 0, 0);
+            medicalChartAccel.AppendDataChartArea3(dummyData, 0, 0);
+            */
+
+            setupGraphs();
+
+            chartLED.ChartAreas["ChartArea3Green"].Visible = false;
+            timer.Interval = 50;    
+            //timer.Tick += new EventHandler(timer_Tick);
+        }
+
+        void setupGraphs()
+        {
+            chartLED.SuspendLayout();
+            chartAccel.SuspendLayout();
+
+            /* Initialize Empty Chart Grid */
+            chartLED.Series["SeriesRed"].Points.Clear();
+            chartLED.Series["SeriesIR"].Points.Clear();
+            chartLED.Series["SeriesGreen"].Points.Clear();
+
+            chartAccel.Series["SeriesAccelX"].Points.Clear();
+            chartAccel.Series["SeriesAccelY"].Points.Clear();
+            chartAccel.Series["SeriesAccelZ"].Points.Clear();
+
+            // Clear labels
+            chartLED.ChartAreas["ChartArea1Red"].AxisX.CustomLabels.Clear();
+            chartLED.ChartAreas["ChartArea2IR"].AxisX.CustomLabels.Clear();
+            chartLED.ChartAreas["ChartArea3Green"].AxisX.CustomLabels.Clear();
+            chartAccel.ChartAreas["ChartArea4AccelX"].AxisX.CustomLabels.Clear();
+            chartAccel.ChartAreas["ChartArea5AccelY"].AxisX.CustomLabels.Clear();
+            chartAccel.ChartAreas["ChartArea6AccelZ"].AxisX.CustomLabels.Clear();
+
+            chartLED.Series["SeriesRed"].Points.Add(0);
+            chartLED.Series["SeriesIR"].Points.Add(0);
+            chartLED.Series["SeriesGreen"].Points.Add(0);
+
+            chartAccel.Series["SeriesAccelX"].Points.Add(0);
+            chartAccel.Series["SeriesAccelY"].Points.Add(0);
+            chartAccel.Series["SeriesAccelZ"].Points.Add(0);
+
+            // Initialize Plots for OS24
+            chartLED.ChartAreas["ChartArea1Red"].AxisX.Interval = points / ChartTime;
+            chartLED.ChartAreas["ChartArea2IR"].AxisX.Interval = points / ChartTime;
+            chartLED.ChartAreas["ChartArea3Green"].AxisX.Interval = points / ChartTime;
+            chartLED.ChartAreas["ChartArea1Red"].AxisX.Maximum = points;
+            chartLED.ChartAreas["ChartArea2IR"].AxisX.Maximum = points;
+            chartLED.ChartAreas["ChartArea3Green"].AxisX.Maximum = points;
+            chartLED.ChartAreas["ChartArea1Red"].AxisY.Maximum = 90000;
+            chartLED.ChartAreas["ChartArea1Red"].AxisY.Minimum = 0;
+            chartLED.ChartAreas["ChartArea2IR"].AxisY.Maximum = 90000;
+            chartLED.ChartAreas["ChartArea2IR"].AxisY.Minimum = 0;
+            chartLED.ChartAreas["ChartArea3Green"].AxisY.Maximum = 90000;
+            chartLED.ChartAreas["ChartArea3Green"].AxisY.Minimum = 0;
+
+            // Initialize Plots for LIS2HD
+            sampleRateAccel = sampleRate;
+            //pointsAccel = (sampleRateAccel / sampleAverage) * ChartTime;
+            pointsAccel = (sampleRateAccel) * ChartTime;
+            chartAccel.ChartAreas["ChartArea4AccelX"].AxisX.Interval = pointsAccel / ChartTime;
+            chartAccel.ChartAreas["ChartArea5AccelY"].AxisX.Interval = pointsAccel / ChartTime;
+            chartAccel.ChartAreas["ChartArea6AccelZ"].AxisX.Interval = pointsAccel / ChartTime;
+            chartAccel.ChartAreas["ChartArea4AccelX"].AxisX.Maximum = pointsAccel;
+            chartAccel.ChartAreas["ChartArea5AccelY"].AxisX.Maximum = pointsAccel;
+            chartAccel.ChartAreas["ChartArea6AccelZ"].AxisX.Maximum = pointsAccel;
+            chartAccel.ChartAreas["ChartArea4AccelX"].AxisY.Minimum = -10000;
+            chartAccel.ChartAreas["ChartArea5AccelY"].AxisY.Minimum = -10000;
+            chartAccel.ChartAreas["ChartArea6AccelZ"].AxisY.Minimum = -10000;
+            chartAccel.ChartAreas["ChartArea4AccelX"].AxisY.Maximum = 10000;
+            chartAccel.ChartAreas["ChartArea5AccelY"].AxisY.Maximum = 10000;
+            chartAccel.ChartAreas["ChartArea6AccelZ"].AxisY.Maximum = 10000;
+
+            // Set X-axis labels in seconds
+            for (int i = 0; i < ChartTime + 1; i++)
+            {
+                chartLED.ChartAreas["ChartArea1Red"].AxisX.CustomLabels.Add( (sampleRate / sampleAverage) * (2 * i - 1) / 2, (sampleRate / sampleAverage) * (2 * i + 1) / 2, i.ToString());
+                chartLED.ChartAreas["ChartArea2IR"].AxisX.CustomLabels.Add( (sampleRate / sampleAverage) * (2 * i - 1) / 2, (sampleRate / sampleAverage) * (2 * i + 1) / 2, i.ToString());
+                chartLED.ChartAreas["ChartArea3Green"].AxisX.CustomLabels.Add( (sampleRate / sampleAverage) * (2 * i - 1) / 2, (sampleRate / sampleAverage) * (2 * i + 1) / 2, i.ToString());
+
+                chartAccel.ChartAreas["ChartArea4AccelX"].AxisX.CustomLabels.Add(sampleRateAccel * (2 * i - 1) / 2, sampleRateAccel * (2 * i + 1) / 2, i.ToString());
+                chartAccel.ChartAreas["ChartArea5AccelY"].AxisX.CustomLabels.Add(sampleRateAccel * (2 * i - 1) / 2, sampleRateAccel * (2 * i + 1) / 2, i.ToString());
+                chartAccel.ChartAreas["ChartArea6AccelZ"].AxisX.CustomLabels.Add(sampleRateAccel * (2 * i - 1) / 2, sampleRateAccel * (2 * i + 1) / 2, i.ToString());
+            }
+
+            chartLED.ResumeLayout();
+            chartLED.Invalidate();
+            chartAccel.ResumeLayout();
+            chartAccel.Invalidate();
+        }
+
+        void timer_Tick(object sender, EventArgs e)
+        {
+            /*Single tmpData = dynamicData[0].Y;
+            for (int i = 0; i < dynamicData.Length - 1; i++)
+            {
+                dynamicData[i].Y = dynamicData[i + 1].Y;
+            }
+            dynamicData[dynamicData.Length - 1].Y = tmpData;*/
+
+            //PointF[] newData = GenerateDynamicData(100, 5);
+            //UpdateLedChart("SeriesRed", newData);
+            //UpdateLedChart("SeriesIR", newData);
+            //UpdateLedChart("SeriesGreen", newData);
+        }
+
+        /*private void UpdateLedChart(string seriesName, PointF[] data)
+        {
+            int count; 
+
+            chartLED.SuspendLayout();
+            //chartLED.Series[seriesName].Points.Clear();
+            foreach (PointF pt in data)
+            {
+                //chartLED.Series[seriesName].Points.AddXY(pt.X, pt.Y);
+                chartLED.Series[seriesName].Points.Add(pt.Y);
+            }
+
+            for (count = chartLED.Series[seriesName].Points.Count; 
+                count > points; 
+                count = chartLED.Series[seriesName].Points.Count)
+            {
+                chartLED.Series[seriesName].Points.RemoveAt(0);
+            } 
+            chartLED.ResumeLayout();
+            chartLED.Invalidate();
+        }*/
+
+        private void UpdateXYZChart(string seriesName, int[] data)
+        {
+            int datapoint;
+            int count;
+
+            chartAccel.SuspendLayout();
+            //chartLED.Series[seriesName].Points.Clear();
+            foreach (int val in data)
+            {
+                //chartLED.Series[seriesName].Points.AddXY(pt.X, pt.Y);
+                datapoint = val;
+                // Two's complement conversion
+                if (val > 0x8000)
+                {
+                    datapoint = datapoint - 0x10000;
+                }
+                    chartAccel.Series[seriesName].Points.Add(datapoint);
+            }
+
+            for (count = chartAccel.Series[seriesName].Points.Count;
+                count > pointsAccel;
+                count = chartAccel.Series[seriesName].Points.Count)
+            {
+                chartAccel.Series[seriesName].Points.RemoveAt(0);
+            }
+            //chartAccel.ResetAutoValues();
+            chartAccel.ResumeLayout();
+            chartAccel.Invalidate();
+        }
+
+        private void UpdateLedChart(string seriesName, int[] data)
+        {
+            int[] dataPoints = new int[points];
+            int j = 0;
+            int count;
+
+            chartLED.SuspendLayout();
+            //chartLED.Series[seriesName].Points.Clear();
+
+            // Update Chart with new data
+            if (data.Length > points)
+                j = data.Length - points;
+
+            for (; j < data.Length; j++ )
+            {
+                chartLED.Series[seriesName].Points.Add(data[j]);
+#if CES_DEMO
+                if (seriesName.CompareTo("SeriesRed") == 0)
+                    red.Enqueue(data[j]);
+                else if (seriesName.CompareTo("SeriesIR") == 0)
+                    ir.Enqueue(data[j]);
+                else if (seriesName.CompareTo("SeriesGreen") == 0)
+                    led.Enqueue(data[j]);
+#endif
+            }
+
+            for (count = chartLED.Series[seriesName].Points.Count; 
+                count > points; 
+                count = chartLED.Series[seriesName].Points.Count)
+            {
+                chartLED.Series[seriesName].Points.RemoveAt(0);
+            }
+        }
+
+        private void UpdateLedChartScale(string chartName, string seriesName)
+        {
+            int min = Int32.MaxValue, max = 0;
+            int i, j;
+            int[] data = new int[points];
+            int graphCount;
+            //int maxRound, minRound;
+
+            graphCount = chartLED.Series[seriesName].Points.Count;
+
+            for (j = 0; j < graphCount; j++ )
+            {
+                data[j] = (int)chartLED.Series[seriesName].Points[j].YValues[0];
+            }
+
+            //if (data.Length < sampleRate)
+            //    return; // not enough data to draw graph
+            /*else*/
+            if (graphCount < (sampleRate / sampleAverage) * 2) // look back 2 seconds
+            {
+                i = 0;
+            }
+            else // look back 2 seconds
+            {
+                i = graphCount - (sampleRate / sampleAverage) * 2;
+            }
+
+            min = data[i];
+            max = data[i];
+            for (; i < graphCount; i++ )
+            {
+                if (data[i] < min && data[i] != 0)
+                    min = data[i];
+                else if (data[i] > max)
+                    max = data[i];
+            }
+
+            /*
+            MedianFilter filter;
+            intervalDict.TryGetValue(chartName, out filter);
+
+            var minMax = chartInterval(min, max, chartLED.ChartAreas[chartName].AxisY.Minimum, chartLED.ChartAreas[chartName].AxisY.Maximum, filter);
+            minRound = minMax.Item1;
+            maxRound = minMax.Item2;
+
+
+
+            // For no finger on sensor
+            if (max < 500)
+                max = 500;
+            */
+
+            AutoScaleCalculator chartScale;
+            chartScaleDict.TryGetValue(chartName, out chartScale);
+
+            var minMax = chartScale.Interval(min, max);
+
+            // Round to nearest 100 with averaging
+            //chartLED.ChartAreas[chartName].AxisY.Maximum = (chartLED.ChartAreas[chartName].AxisY.Maximum + (max / 100 + 1) * 100)/2;
+            //chartLED.ChartAreas[chartName].AxisY.Maximum = (((int)chartLED.ChartAreas[chartName].AxisY.Maximum + max)/200 + 1) * 100;
+            //chartLED.ChartAreas[chartName].AxisY.Minimum = (chartLED.ChartAreas[chartName].AxisY.Minimum + (min / 100 - 1) * 100)/2;
+            //chartLED.ChartAreas[chartName].AxisY.Minimum = (((int)chartLED.ChartAreas[chartName].AxisY.Minimum + min)/200 - 1) * 100;
+            chartLED.ChartAreas[chartName].AxisY.Minimum = minMax.Item1;
+            chartLED.ChartAreas[chartName].AxisY.Maximum = minMax.Item2;
+
+            chartLED.ResumeLayout();
+            chartLED.Invalidate();
+        }
+
+        private void UpdateXYZChartScale(string chartName, string seriesName)
+        {
+            int min = Int32.MaxValue, max = Int32.MinValue;
+            int i, j;
+            int maxRound, minRound;
+            int graphCount;
+
+            int[] data = new int[pointsAccel];
+
+            graphCount = chartAccel.Series[seriesName].Points.Count;
+
+            // Save all points from plot to data[]
+            for (j = 0; j < graphCount; j++)
+            {
+                data[j] = (int)chartAccel.Series[seriesName].Points[j].YValues[0];
+            }
+
+            if (graphCount < (sampleRateAccel / sampleAverage) * 2) // look back 3 seconds
+                i = 0;
+            else
+                i = graphCount - (sampleRateAccel / sampleAverage) * 2;
+
+            // Find min/max
+            min = data[i];
+            max = data[i];
+            for (; i < graphCount; i++)
+            {
+                if (data[i] < min)
+                    min = data[i];
+                else if (data[i] > max)
+                    max = data[i];
+            }
+
+            /*
+            // Sane defaults
+            if (min == max)
+            {
+                min = 0;
+                max = 100;
+            }
+
+            MedianFilter filter;
+            intervalDict.TryGetValue(chartName, out filter);
+
+            var minMax = chartInterval(min, max, chartAccel.ChartAreas[chartName].AxisY.Minimum, chartAccel.ChartAreas[chartName].AxisY.Maximum, filter);
+            minRound = minMax.Item1;
+            maxRound = minMax.Item2;
+
+            if (maxRound > 32768)
+                maxRound = 33000;
+
+            if (minRound < -32768)
+                minRound = -33000;
+            */
+
+            AutoScaleCalculator chartScale;
+            chartScaleDict.TryGetValue(chartName, out chartScale);
+
+            var minMax = chartScale.Interval(min, max);
+            minRound = (int)minMax.Item1;
+            maxRound = (int)minMax.Item2;
+
+            // Set the Min and Max for Y Axis
+            //chartAccel.ChartAreas[chartName].AxisY.Maximum = (((int)chartAccel.ChartAreas[chartName].AxisY.Maximum + max) / 200 + 1) * 100;
+            //chartAccel.ChartAreas[chartName].AxisY.Minimum = (((int)chartAccel.ChartAreas[chartName].AxisY.Minimum + min) / 200 - 1) * 100;
+            chartAccel.ChartAreas[chartName].AxisY.Maximum = maxRound;
+            chartAccel.ChartAreas[chartName].AxisY.Minimum = minRound;
+            //System.Diagnostics.Debug.Print(chartName + " count: " + graphCount + " minRound: " + minRound + " maxRound: " + maxRound + " min: " + min + " max: " + max);
+
+            chartAccel.ResumeLayout();
+            chartAccel.Invalidate();
+        }
+
+        /*private PointF[] dynamicData;
+        private static PointF[] GenerateDynamicData(double amplitude, int sampleCount)
+        {
+            Random rnd = new Random();
+            PointF[] points = new PointF[sampleCount];
+            points[0] = new PointF(0, (Single)(amplitude * rnd.NextDouble()));
+            for (int i = 1; i < sampleCount; i++)
+            {
+                points[i] = new PointF(i, (Single)(points[i - 1].Y + (amplitude / 10 * (0.5 - rnd.NextDouble()))));
+                if (points[i].Y > amplitude) points[i].Y = (Single)amplitude;
+            }
+            return (points);
+        }*/
+
+        /// <summary>
+        /// Calculate rounded minimum and maximum for chart data
+        /// </summary>
+        /// <param name="minimum"></param>
+        /// <param name="maximum"></param>
+        /// <param name="chartInterval"></param>
+        /// <returns></returns>
+        private static Tuple<int, int> chartInterval(int minimum, int maximum, double chartMinimum, double chartMaximum, MedianFilter chartInterval)
+        {
+            int delta, interval, roundInterval, roundCenter;
+            double mag, magPower, magMostSigDigit;
+            int center;
+
+            // 10% and 90% range
+            int chartRange = (int)(chartMaximum - chartMinimum);
+            int dataRange = maximum - minimum;
+            if (minimum < chartMinimum + chartRange * 0.10 || maximum > chartMaximum - chartRange * 0.10
+                || dataRange > 0.9 * chartRange || dataRange < 0.1 * chartRange)
+            {
+
+                delta = (int)((maximum - minimum));
+
+                interval = delta * (100 / 35) / 5; // delta * 4 = 25% * 4 = 100 (full chart scale)
+                center = minimum + (maximum - minimum) / 2;
+
+                mag = Math.Floor(Math.Log10(interval));
+                magPower = Math.Pow(10, mag);
+
+                magMostSigDigit = (int)(interval / (double)magPower + 0.5);
+
+                if (magMostSigDigit >= 5.0)
+                    magMostSigDigit = 10;
+                else if (magMostSigDigit >= 2.0)
+                    magMostSigDigit = 5;
+                else if (magMostSigDigit >= 1.0)
+                    magMostSigDigit = 2;
+
+                roundInterval = (int)(magMostSigDigit * magPower);
+                if (roundInterval < 100)
+                {
+                    roundInterval = 100;
+                }
+                roundInterval = chartInterval.Filter(roundInterval);
+
+                roundCenter = ((center + 100) / 200) * 200;
+
+                int roundMin = (int)(roundCenter - roundInterval * 2.5);
+                int roundMax = (int)(roundCenter + roundInterval * 2.5);
+                if (roundInterval > 104858)
+                {
+                    roundMin = 0;
+                    roundMax = 550000;
+                }
+                /*else
+                {
+                    if (roundMin < 0) // || roundMax > 524288)
+                    {
+                        roundMin = 0;
+                        roundMax = roundInterval * 5;
+                    }
+                }*/
+
+                return new Tuple<int, int>(roundMin, roundMax);
+            }
+
+            return new Tuple<int, int>((int)chartMinimum, (int)chartMaximum);
+        }
+
+        void OpticalSensorInitControls()
+        {
+            cboMode.Items.AddRange(new string[] { "HR","SPO2","LED" });
+            cboSampleRate.Items.AddRange(new string[] { "50", "100", "200", "400" });
+            cboSampleAvg.Items.AddRange(new string[] { "1", "2", "4", "8", "16", "32" });
+            cboPulseWidth.Items.AddRange(new string[] { "69", "118", "215", "411" });
+            cboADCFSrangenA.Items.AddRange(new string[] { "2048", "4096", "8192", "16384" });
+            double slopeInt = 25.0 / 128.0;
+            for (int index = 0; index < 256; index++)
+            {
+                string str = index < 0x0F ? (0.2 * index).ToString() : (slopeInt * index + slopeInt).ToString();
+                cboIRLED.Items.Add(str);
+                cboRedLED.Items.Add(str);
+                cboGreenLED.Items.Add(str);
+                cboPilotPA.Items.Add(str);
+            }
+
+            OpticalSensorDefaults();
+            calculateAvgCurrent();
+
+            InitGraphs();            
+        }
+
+        public void OpticalSensorDefaults()
+        {
+            cboMode.SelectedItem = "SPO2";
+            cboRedLED.SelectedItem = "10.15625";
+            cboIRLED.SelectedItem = "10.15625";
+            cboGreenLED.SelectedItem = "10.15625";
+            cboSampleRate.SelectedItem = "100";
+            cboPulseWidth.SelectedItem = "411";
+            cboSampleAvg.SelectedItem = "1";
+            cboADCFSrangenA.SelectedItem = "8192";
+
+            cboLEDslot1.SelectedIndex = 1;
+            cboLEDslot2.SelectedIndex = 2;
+            cboLEDslot3.SelectedIndex = 3;
+            cboLEDslot4.SelectedIndex = 0;
+
+            // Used to generate the initial chart grid
+            sample_rate = (byte)cboSampleRate.SelectedIndex;
+            sample_avg = (byte)cboSampleAvg.SelectedIndex;
+
+            sampleRate = (1 << sample_rate) * 50; // (1 << sample_rate) = 2^sample_rate
+            sampleAverage = (1 << sample_avg); // 2^sample_avg
+            points = (sampleRate / sampleAverage) * ChartTime; // 4 seconds, 2^sample_av
+
+            sampleRateAccel = sampleRate; 
+            pointsAccel = (sampleRateAccel / sampleAverage) * ChartTime;
+        }
+
+        private void cbo_IndexChanged(object sender, EventArgs e)
+        {
+            if ((MaximStyle.MaximComboBox)sender == cboMode)
+            {
+                bool HRmode = (cboMode.Text == "HR");
+                bool SPO2mode = (cboMode.Text == "SPO2");
+                bool multiLEDmode = (cboMode.Text == "LED");
+                //labelcboRedLED.Enabled = myMAX30101.valid_Red;
+                //cboRedLED.Enabled = myMAX30101.valid_Red;
+                bool valid_IR = (multiLEDmode || SPO2mode);
+                bool valid_Green = multiLEDmode;
+                bool valid_Red = multiLEDmode;
+                labelcboIRLED.Enabled = valid_IR;
+                cboIRLED.Enabled = valid_IR;
+                labelcboGreenLED.Enabled = valid_Green;
+                cboGreenLED.Enabled = valid_Green;
+                cboLEDslot1.Enabled = multiLEDmode;
+                cboLEDslot2.Enabled = multiLEDmode;
+                cboLEDslot3.Enabled = multiLEDmode;
+                cboLEDslot4.Enabled = multiLEDmode;
+                {
+                    // Hide unused LED channel graphs
+                    // | mode | ChartArea1Red | ChartArea2IR | ChartArea3Green |
+                    // | LED  |   visible     |   visible    |   visible       | MAX30101 only
+                    // | SPO2 |   visible     |   visible    |   hidden        |
+                    // | HR   |   visible     |   hidden     |   hidden        | MAX30101: Red only. MAX30100: IR only.
+                    valid_Red = true;
+                    valid_IR = multiLEDmode || SPO2mode;
+                    valid_Green = multiLEDmode;
+                    
+                    chartLED.ChartAreas["ChartArea1Red"].Visible = valid_Red;
+                    chartLED.ChartAreas["ChartArea2IR"].Visible = valid_IR;
+                    chartLED.ChartAreas["ChartArea3Green"].Visible = valid_Green;                   
+                }
+            }
+        }
+
+        public void On_AppendChart(object sender, PartialArrayIntAvailableEventArgs e)
+        {
+            if (streaming)
+            {
+                if ((e.reportID & 0xF0) == PartialArrayIntAvailableEventArgs.PACKET_MAX30101)
+                {
+                    if (e.array1.Length != 0)
+                    {
+                        UpdateLedChart("SeriesRed", e.array1);
+                        UpdateLedChartScale("ChartArea1Red", "SeriesRed");
+                    }
+                    if (e.array2.Length != 0)
+                    {
+                        UpdateLedChart("SeriesIR", e.array2);
+                        UpdateLedChartScale("ChartArea2IR", "SeriesIR");
+                    }
+                    if (e.array3.Length != 0)
+                    {
+                        UpdateLedChart("SeriesGreen", e.array3);
+                        UpdateLedChartScale("ChartArea3Green", "SeriesGreen");
+                    }
+                }
+                if (e.reportID == PartialArrayIntAvailableEventArgs.PACKET_LIS2DH)
+                {
+                    if (e.array1.Length != 0)
+                    {
+                        UpdateXYZChart("SeriesAccelX", e.array1);
+                        UpdateXYZChartScale("ChartArea4AccelX", "SeriesAccelX");
+                    }
+                    if (e.array2.Length != 0)
+                    {
+                        UpdateXYZChart("SeriesAccelY", e.array2);
+                        UpdateXYZChartScale("ChartArea5AccelY", "SeriesAccelY");
+                    }
+                    if (e.array3.Length != 0)
+                    {
+                        UpdateXYZChart("SeriesAccelZ", e.array3);
+                        UpdateXYZChartScale("ChartArea6AccelZ", "SeriesAccelZ");
+                    }
+
+#if CES_DEMO
+                if (sampleRate == 100)
+                    updateAlgorithm();
+#endif
+                }
+            }
+        }
+
+#if CES_DEMO
+        private void updateAlgorithm()
+        {
+            int rtn = 0;
+
+            while (red.Count != 0 ) //&& ir.Couunt != 0 && led.Count != 0)
+            {
+                StringBuilder outputData = new StringBuilder();
+                int redVal = (Int32)red.Dequeue(); // Always dequeue red, checked in while loop
+                int irVal = ir.Count > 0 ? (Int32)ir.Dequeue() : 1;
+                int ledVal = led.Count > 0 ? (Int32)led.Dequeue() : 1;
+
+                switch (streamMode)
+                {
+                    case eStreamMode.eSPO2:
+                        rtn = AlgorithmMobileBU.runAlgorithm_win(irVal, redVal, 1, // ir, r, led
+                                    25, sampleRate, ++algoCounter, 
+                                    0, 0, 0, ref config, ref output);
+                        break;
+                    case eStreamMode.eHR: // TODO: Not functioning
+                        rtn = AlgorithmMobileBU.runAlgorithm_win(1, redVal, 1, // ir, r, led
+                                    25, sampleRate, ++algoCounter, 
+                                    0, 0, 0, ref config, ref output);
+                        break;
+                    case eStreamMode.eMulti:
+                        rtn = AlgorithmMobileBU.runAlgorithm_win(irVal, redVal, ledVal, // ir, r, led
+                                    25, sampleRate, ++algoCounter, 
+                                    0, 0, 0, ref config, ref output);
+                        break;
+                }
+
+                //outputData.Append(DateTime.Now.ToString("HH:mm:ss.ffff"));
+                //outputData.Append(",");
+                //outputData.Append(irVal);
+                //outputData.Append(",");
+                //outputData.Append(redVal);
+                //outputData.Append(",");
+                //outputData.Append(0); // Can be green
+                //outputData.Append(",");
+                //outputData.Append(0); // Ax
+                //outputData.Append(",");
+                //outputData.Append(0); // Ay
+                //outputData.Append(",");
+                //outputData.Append(0); // Az
+                //outputData.Append(",");
+
+                //file.WriteLine(outputData.ToString()); // Raw data output
+            }
+
+            lblHeartRate.Text = Math.Round(output.hr).ToString(); //redIn.ToString();
+            lblRespRate.Text = output.resp_rate.ToString("N");
+            lblSpO2.Text = output.R == 0 ? "0" : (-45.060 * output.R * output.R + 30.354 * output.R + 94.845).ToString("N");//output.spo2.ToString("N");
+            lblHeartRateVariation.Text = output.hrv.ToString("N");
+            lblPulseInterval.Text = output.pulse_interval.ToString("N");
+        }
+
+#endif
+
+        private void OpticalView_Load(object sender, EventArgs e)
+        {
+            //OpticalSensorInitControls();
+            //OpticalSensorDefaults();
+        }
+
+        private void streamingStartStop()
+        {
+            if (cboMode.Text == "HR") streamMode = eStreamMode.eHR;
+            if (cboMode.Text == "SPO2") streamMode = eStreamMode.eSPO2;
+            if (cboMode.Text == "LED") streamMode = eStreamMode.eMulti;
+
+            if (Connected && btnMonitoring.Text == StartString)
+            {
+                streaming = true;
+                sample_rate = (byte)cboSampleRate.SelectedIndex;
+                sample_avg = (byte)cboSampleAvg.SelectedIndex;
+                pulse_width = (byte)cboPulseWidth.SelectedIndex;
+                red_led_current = (byte)cboRedLED.SelectedIndex;
+                ir_led_current = (byte)cboIRLED.SelectedIndex;
+                green_led_current = (byte)cboGreenLED.SelectedIndex;
+                slot_1 = (byte)cboLEDslot1.SelectedIndex;
+                slot_2 = (byte)cboLEDslot2.SelectedIndex;
+                slot_3 = (byte)cboLEDslot3.SelectedIndex;
+                slot_4 = (byte)cboLEDslot4.SelectedIndex;
+
+                sampleRate = Int32.Parse((string)cboSampleRate.SelectedItem);
+                sampleAverage = Int32.Parse((string)cboSampleAvg.SelectedItem);
+                points = (sampleRate / sampleAverage ) * ChartTime;
+
+                foreach (Control c in streamingControls)
+                    c.Enabled = false;
+
+#if CES_DEMO
+                // For Algorithm
+                sampleRate = (1 << sample_rate) * 50; // (1 << sample_rate) = 2^sample_rate
+                sampleAverage = (1 << sample_avg); // 2^sample_avg
+                points = (sampleRate / sampleAverage) * ChartTime; // 4 seconds, 2^sample_av
+
+                sampleRateAccel = 10; // TODO, update with real value from Jerry
+                pointsAccel = (sampleRateAccel / sampleAverage) * ChartTime;
+
+                // Reset algorithm counter
+                algoCounter = 0;
+
+                if (sampleRate == 100)
+                {
+                    lblHeartRateText.Enabled = true;
+                    lblHeartRate.Enabled = true;
+                    lblSpO2.Enabled = true;
+                    lblSpO2Text.Enabled = true;
+                }
+                else
+                {
+                    lblHeartRateText.Enabled = false;
+                    lblHeartRate.Enabled = false;
+                    lblSpO2.Enabled = false;
+                    lblSpO2Text.Enabled = false;
+                }
+#endif
+
+                // Setup grid
+                setupGraphs();
+
+                switch (streamMode)
+                {
+                    case eStreamMode.eHR:
+                        rpcClient.MAX30101.HRmode_init(fifo_waterlevel_mark, sample_avg, sample_rate, pulse_width,
+                                                                         red_led_current, (byte)(sample_rate + 4));
+                        break;
+                    case eStreamMode.eMulti:
+                        rpcClient.MAX30101.Multimode_init(fifo_waterlevel_mark, sample_avg, sample_rate, pulse_width,
+                                             red_led_current, ir_led_current, green_led_current,
+                                             slot_1, slot_2, slot_3, slot_4, (byte)(sample_rate + 4));
+                        break;
+                    case eStreamMode.eSPO2:
+                        rpcClient.MAX30101.SpO2mode_init(fifo_waterlevel_mark, sample_avg, sample_rate, pulse_width,
+                                                                         red_led_current, ir_led_current, (byte)(sample_rate + 4));
+                        break;
+                }
+
+                btnMonitoring.Text = StopString;
+
+                if (StreamingStartStop != null) 
+                    StreamingStartStop(this, new StreamingStartStopEventArgs() { state = true });
+
+            }
+            else if (btnMonitoring.Text == StopString)
+            {
+                streaming = false;
+                switch (streamMode)
+                {
+                    case eStreamMode.eHR:
+                        rpcClient.MAX30101.HRmode_stop(connected);
+                        break;
+                    case eStreamMode.eMulti:
+                        rpcClient.MAX30101.Multimode_stop(connected);
+                        break;
+                    case eStreamMode.eSPO2:
+                        rpcClient.MAX30101.SpO2mode_stop(connected);
+                        break;
+                }
+                //rpcClient.MAX30101.StopStreaming();
+                btnMonitoring.Text = StartString;
+
+                foreach (Control c in streamingControls)
+                    c.Enabled = true;
+                
+                if (StreamingStartStop != null)
+                    StreamingStartStop(this, new StreamingStartStopEventArgs() { state = false });
+            }
+        }
+
+        private void btnMonitoring_Click(object sender, EventArgs e)
+        {
+            //if (Connected)
+            streamingStartStop();
+        }
+
+        private void button1_Click(object sender, EventArgs e)
+        {
+            timer.Start();
+        }
+
+        private void button1_Click_1(object sender, EventArgs e)
+        {
+            /* Debug*/
+            //int count = chartLED.Series["SeriesRed"].Points.Count;
+            //int count2 = chartAccel.Series["SeriesAccelX"].Points.Count;
+            UpdateXYZChartScale("ChartArea6AccelZ", "SeriesAccelZ");
+        }
+
+        private void btnDefaults_Click(object sender, EventArgs e)
+        {
+            OpticalSensorDefaults();
+        }
+
+        private void cboSRPWLed_SelectedIndexChanged(object sender, EventArgs e)
+        {
+            calculateAvgCurrent();
+        }
+
+        private void calculateAvgCurrent()
+        {
+            double ratio = double.Parse(cboPulseWidth.Text) * 1e-6 * double.Parse(cboSampleRate.Text);
+            string _noDataString = "---";
+            if (ratio <= 1)
+            {
+                lblAvgIRLEDcurrent.Text = String.Format("{0:0.00}", double.Parse(cboIRLED.Text) * ratio);
+                lblAvgRedLEDcurrent.Text = String.Format("{0:0.00}", double.Parse(cboRedLED.Text) * ratio);
+                // https://jira.maxim-ic.com/browse/OS24EVK-17 Green LED: Multi-LED Mode timeslice; FIFO Read Red, IR, Green
+                lblAvgGreenLEDcurrent.Text = String.Format("{0:0.00}", double.Parse(cboGreenLED.Text) * ratio);
+                // https://jira.maxim-ic.com/browse/OS24EVK-32 update lblAvgPilotPALEDcurrent from cboPilotPA similar to lblAvgGreenLEDcurrent
+                //lblAvgPilotPALEDcurrent.Text = String.Format("{0:0.00}", double.Parse(cboPilotPA.Text) * ratio);
+            }
+            else
+            {
+                lblAvgIRLEDcurrent.Text = _noDataString;
+                lblAvgRedLEDcurrent.Text = _noDataString;
+                // https://jira.maxim-ic.com/browse/OS24EVK-17 Green LED: Multi-LED Mode timeslice; FIFO Read Red, IR, Green
+                lblAvgGreenLEDcurrent.Text = _noDataString;
+                // https://jira.maxim-ic.com/browse/OS24EVK-32 update lblAvgPilotPALEDcurrent from cboPilotPA similar to lblAvgGreenLEDcurrent
+                lblAvgPilotPALEDcurrent.Text = _noDataString;
+            }
+        }
+
+        void cboSampleRate_SelectedIndexChanged(object sender, EventArgs e)
+        {
+            if ((string)cboSampleRate.SelectedItem == "100" && (string)cboSampleAvg.SelectedItem == "1")
+                grpAlgorithm.Enabled = true;
+            else
+                grpAlgorithm.Enabled = false;
+
+            sampleRate = Int32.Parse((string)cboSampleRate.SelectedItem);
+            sampleAverage = Int32.Parse((string)cboSampleAvg.SelectedItem);
+        }
+
+        public string SettingsString()
+        {
+            StringBuilder strBuilder = new StringBuilder();
+
+            strBuilder.Append("% ");
+            strBuilder.Append("Mode = ");
+            strBuilder.Append(cboMode.SelectedItem);
+            strBuilder.Append(Environment.NewLine);
+
+            strBuilder.Append("% ");
+            strBuilder.Append("Sample Rate (Hz) = ");
+            strBuilder.Append(cboSampleRate.SelectedItem);
+            strBuilder.Append(" Sample Average = ");
+            strBuilder.Append(cboSampleAvg.SelectedItem);
+            strBuilder.Append(" Pulse Width (us) = ");
+            strBuilder.Append(cboPulseWidth.SelectedItem);
+            strBuilder.Append(" ADC Full Scale Range (nA) = ");
+            strBuilder.Append(cboADCFSrangenA.SelectedItem);
+            strBuilder.Append(Environment.NewLine);
+
+            strBuilder.Append("% ");
+            strBuilder.Append(" LED Red (mA) = ");
+            strBuilder.Append(cboRedLED.SelectedItem);
+            strBuilder.Append(" LED IR (mA) = ");
+            strBuilder.Append(cboIRLED.SelectedItem);
+            strBuilder.Append(" LED Green (mA) = ");
+            strBuilder.Append(cboGreenLED.SelectedItem);
+            strBuilder.Append(Environment.NewLine);
+
+            strBuilder.Append("% ");
+            strBuilder.Append("LED Slot 1 = ");
+            strBuilder.Append(cboLEDslot1.SelectedIndex);
+            strBuilder.Append(" LED Slot 2 = ");
+            strBuilder.Append(cboLEDslot2.SelectedIndex);
+            strBuilder.Append(" LED Slot 3 = ");
+            strBuilder.Append(cboLEDslot3.SelectedIndex);
+            strBuilder.Append(" LED Slot 4 = ");
+            strBuilder.Append(cboLEDslot4.SelectedIndex);
+
+            return strBuilder.ToString();
+        }
+
+        public string AccelSettingString()
+        {
+            return "% Sample Rate (Hz) = " + AccelSampleRate;
+        }
+
+        public InitArgs.HRModeInitStart GetHRModeArgs()
+        {
+            InitArgs.HRModeInitStart args = new InitArgs.HRModeInitStart();
+
+            args.FifoWaterlevelMark = fifo_waterlevel_mark;
+            args.SampleRate = (byte)cboSampleRate.SelectedIndex;
+            args.SampleAverage = (byte)cboSampleAvg.SelectedIndex;
+            args.PulseWidth = (byte)cboPulseWidth.SelectedIndex;
+            args.RedLedCurrent = (byte)cboRedLED.SelectedIndex;
+
+            return args;
+        }
+
+        public InitArgs.SpO2HRModeInitStart GetSpO2HRModeArgs()
+        {
+            InitArgs.SpO2HRModeInitStart args = new InitArgs.SpO2HRModeInitStart();
+
+            args.FifoWaterlevelMark = fifo_waterlevel_mark;
+            args.SampleRate = (byte)cboSampleRate.SelectedIndex;
+            args.SampleAverage = (byte)cboSampleAvg.SelectedIndex;
+            args.PulseWidth = (byte)cboPulseWidth.SelectedIndex;
+            args.RedLedCurrent = (byte)cboRedLED.SelectedIndex;
+            args.IRLedCurrent = (byte)cboIRLED.SelectedIndex;
+
+            return args;
+        }
+
+        public InitArgs.MultiModeInitStart GetMultiModeArgs()
+        {
+            InitArgs.MultiModeInitStart args = new InitArgs.MultiModeInitStart();
+
+            args.FifoWaterlevelMark = fifo_waterlevel_mark;
+            args.SampleRate = (byte)cboSampleRate.SelectedIndex;
+            args.SampleAverage = (byte)cboSampleAvg.SelectedIndex;
+            args.PulseWidth = (byte)cboPulseWidth.SelectedIndex;
+            args.RedLedCurrent = (byte)cboRedLED.SelectedIndex;
+            args.IRLedCurrent = (byte)cboIRLED.SelectedIndex;
+            args.GreenLedCurrent = (byte)cboGreenLED.SelectedIndex;
+            args.Slot1 = (byte)cboLEDslot1.SelectedIndex;
+            args.Slot2 = (byte)cboLEDslot2.SelectedIndex;
+            args.Slot3 = (byte)cboLEDslot3.SelectedIndex;
+            args.Slot4 = (byte)cboLEDslot4.SelectedIndex;
+
+            return args;
+        }
+
+        public void SetHRModeArgs(InitArgs.HRModeInitStart args)
+        {
+            cboSampleRate.SelectedIndex = args.SampleRate; 
+            cboSampleAvg.SelectedIndex = args.SampleAverage;
+            cboPulseWidth.SelectedIndex = args.PulseWidth;
+            cboRedLED.SelectedIndex = args.RedLedCurrent;
+        }
+        
+        public void SetSpO2HRModeArgs(InitArgs.SpO2HRModeInitStart args)
+        {
+            cboSampleRate.SelectedIndex = args.SampleRate; 
+            cboSampleAvg.SelectedIndex = args.SampleAverage;
+            cboPulseWidth.SelectedIndex = args.PulseWidth;
+            cboRedLED.SelectedIndex = args.RedLedCurrent;
+            cboIRLED.SelectedIndex = args.IRLedCurrent;
+        }
+
+        public void SetMultiModeArgs(InitArgs.MultiModeInitStart args)
+        {
+            cboSampleRate.SelectedIndex = args.SampleRate;
+            cboSampleAvg.SelectedIndex = args.SampleAverage;
+            cboPulseWidth.SelectedIndex = args.PulseWidth;
+            cboRedLED.SelectedIndex = args.RedLedCurrent;
+            cboIRLED.SelectedIndex = args.IRLedCurrent;
+            cboGreenLED.SelectedIndex = args.GreenLedCurrent;
+            cboLEDslot1.SelectedIndex = args.Slot1;
+            cboLEDslot2.SelectedIndex = args.Slot2;
+            cboLEDslot3.SelectedIndex = args.Slot3;
+            cboLEDslot4.SelectedIndex = args.Slot4;
+        }
+
+        class MedianFilter
+        {
+            Queue<int> items;
+            int depth;
+
+            public MedianFilter(int depth)
+            {
+                items = new Queue<int>(depth + 1);
+                this.depth = depth;
+            }
+
+            public int Filter(int current)
+            {
+                int last;
+
+                items.Enqueue(current);
+                if (items.Count > depth)
+                {
+                    items.Dequeue();
+                }
+
+                last = items.Peek();
+
+                if (items.Count < depth)
+                {
+                    return current;
+                }
+                else
+                {
+                    int count;
+                    int[] arrayItems = items.ToArray();
+                    int maxCount = 0;
+                    int maxKey = 0;
+
+                    Dictionary<int, int> valueOccurance = new Dictionary<int, int>();
+
+                    for (int i = 0; i < arrayItems.Length; i++)
+                    {
+                        bool exists = valueOccurance.TryGetValue(arrayItems[i], out count);
+                        if (exists)
+                        {
+                            count++;
+                            valueOccurance.Remove(arrayItems[i]);
+                            valueOccurance.Add(arrayItems[i], count);
+                        }
+                        else
+                        {
+                            valueOccurance.Add(arrayItems[i], 1);
+                        }
+                    }
+
+                    foreach (KeyValuePair<int, int> item in valueOccurance)
+                    {
+                        if (item.Key > maxCount)
+                        {
+                            maxKey = item.Key;
+                        }
+                    }
+
+                    return maxKey;
+                }
+
+            }
+        }
+
+        /*
+        public class StreamingStartStopEventArgs : EventArgs
+        {
+            public bool state { get; set; }
+        }*/
+
+    }
+}