repo time

Dependencies:   mbed MAX14720 MAX30205 USBDevice

Revision:
20:6d2af70c92ab
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/HspGuiSourceV301/HSPGui/CustomControls/EcgView.cs	Tue Apr 06 06:41:40 2021 +0000
@@ -0,0 +1,1734 @@
+/*******************************************************************************
+* 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 CES_DEMO
+//#define FILE_LOG
+
+using System;
+using System.Collections.Generic;
+using System.ComponentModel;
+using System.Drawing;
+using System.Data;
+using System.Linq;
+using System.Text;
+using System.Windows.Forms;
+using System.Windows.Forms.DataVisualization.Charting;
+using RPCSupport.Streaming;
+using RPCSupport.Devices;
+
+using HealthSensorPlatform.View;
+using HealthSensorPlatform.Presenter;
+using HealthSensorPlatform.Model;
+
+#if CES_DEMO
+using System.Collections;
+#endif
+
+// DEBUG
+using System.IO;
+
+
+namespace HealthSensorPlatform.CustomControls
+{
+    public partial class EcgView : UserControl, IDeviceView, IEcgView
+    {
+        /* Constants */
+        const int Rbias_FMSTR_Init = 0;
+        const int CAL_init = 1;
+        const int ECG_InitStart = 2;
+        const int PACE_InitStart = 3;
+        const int BIOZ_InitStart = 4;
+        const int ECGFast_Init = 5;
+        const int RtoR_InitStart = 6;
+
+        const int RToRMaxValue = 0x3FFF;
+
+#if CES_DEMO
+        const int[] averageOptions = new int[] { 1, 2, 4, 8, 10 };
+#endif
+
+        /* Fields */
+        public InitArgs.RbiasInit rbiasInit;
+        public InitArgs.EcgInitStart ecgArgs;
+        public InitArgs.EcgFastInit ecgFastArgs;
+        public InitArgs.PACEInitStart paceArgs;
+        public InitArgs.BIOZInitStart biozArgs;
+        public InitArgs.RToRInitStart rtorArgs;
+        public InitArgs.CalInitStart calArgs;
+
+        public GetEcgInitArgs GetEcgInitArgsPointer;
+        public GetRToRInitArgs GetRToRInitArgsPointer;
+        public GetBioZInitArgs GetBioZInitArgsPointer;
+        public GetPaceInitArgs GetPaceInitArgsPointer;
+
+        /// <summary>
+        /// Number of R To R points which need to be removed from the chart
+        /// </summary>
+        int rToRRemoveOffset;
+
+        long lastRToRTime;
+
+        double timeResolution = 0.00001525878; 
+
+        double[] clockFreq = { 32768, 32000, 32000, 32768 * 640 / 656 }; // 31968.7804878 = 32768 * 640 / 656
+
+        int frequencyMasterField = 0;
+
+        public double masterClockFrequency = 32768;
+
+#if CES_DEMO
+        public int AverageECG = 1;
+#endif
+
+        private RPCSupport.RPCClient rpcClient;
+        private bool connected = false;
+        //private bool stream = true;
+
+        /// <summary>
+        /// Use to scale the bioZ chart data to milliohm range
+        /// </summary>
+        private int bioZRangeMultiplier = 1;
+
+        bool startedCal = false;
+        bool startedEcg = false;
+        bool startedPace = false;
+        bool startedBioz = false;
+        bool startedRtoR = false;
+
+        private bool enableECG = false;
+        private bool enableRToR = false;
+        private bool enableBioZ = false;
+        private bool enablePace = false;
+
+        int rToRInterval = -1;
+
+        ChartInfo ecgInfo;
+        ChartInfo rToRInfo;
+        ChartInfo bioZInfo; 
+        ChartInfo paceInfo;
+
+        Queue<int> paceTag = new Queue<int>();
+        public PaceData paceData; // TODO: public for testing only
+        RToRCalculator rToRCalculator;
+
+        List<double> heartRateList = new List<double>(10);
+
+        /* CES DEMO average feature, normal operation average is 1 */
+        //int averageEcgCount = 0;
+        //int averageEcgSum = 0;
+
+        StringBuilder fileLogData = new StringBuilder();
+
+		int[, ,] ecgDecimationDelay = new int[,,] { {{650, 1034}, {2922, 3690}, {3370, 4906}, {3370, 4906}}, 
+													{{650, 1034}, {2922, 3690}, {3370, 4906}, {3370, 4906}},
+													{{1242, 2202}, {1242, 2202}, {1242, 2202}, {1242, 2202}},
+													{{1242, 2202}, {1242, 2202}, {1242, 2202}, {1242, 2202}}
+                                                  };
+
+        public int RToRWindowField = 0;
+		
+#if CES_DEMO
+        FirFilter hpf;
+#endif
+
+#if FILE_LOG
+        // DEBUG
+        StreamWriter file; //= new StreamWriter("hsp_ecg_output_data.csv");
+#endif
+
+        /* Constructor */
+        public EcgView()
+        {
+            InitializeComponent();
+
+            //paceInfo = ecgInfo;
+
+            /*foreach(int item in averageOptions)
+            {
+                cboAverageEcg.Items.Add(item.ToString());
+            }*/
+            //cboAverageEcg.SelectedIndex = 0;
+            //cboMaxScale.SelectedIndex = 1;
+
+            chartRtoR.ChartAreas[0].AxisY.Maximum = 100;
+
+            ecgInfo = new ChartInfo
+            {
+                SampleRate = 128, // Power on defaults for ECG
+                Length = 10,
+                Average = 1,
+                Shift = 6,
+                Gain = 20,
+                // ECG is 18 bits, with MSB as sign bit
+                Threshold = 0x1ffff,
+                Offset = 0x40000,
+                Chart = chartECG,
+                SeriesName = "Series",
+            };
+            rToRInfo = new ChartInfo
+            {
+                SampleRate = 128,
+                Length = 10,
+                Chart = chartECG,
+                SeriesName = "SeriesRToR",
+            };
+            bioZInfo = new ChartInfo
+            {
+                SampleRate = 64,
+                Length = 10,
+                Shift = 4,
+                Gain = 20,
+                // BioZ is 20 bits, with MSB as sign bit
+                Threshold = 0x7ffff,
+                Offset = 0x100000,
+                Chart = chartBioz,
+                SeriesName = "Series",
+            };
+            paceInfo = new ChartInfo
+            {
+                SampleRate = 65536 * ecgInfo.SampleRate, // 2 * fMSTR
+                Length = 10,
+                Average = 1,
+                Shift = 0, // Keep tag info
+                Chart = chartECG,
+                SeriesName = "SeriesPace",
+            };
+
+            NamedImage paceFalling = new NamedImage("PaceFallingImage", HealthSensorPlatform.Properties.Resources.pace_falling);
+            chartECG.Images.Add(paceFalling);
+            NamedImage paceRising = new NamedImage("PaceRisingImage", HealthSensorPlatform.Properties.Resources.pace_rising);
+            chartECG.Images.Add(paceRising);
+#if CES_DEMO
+            //grpAverage.Visible = true;
+
+            hpf = new FirFilter();
+            //cboEcgHpf.SelectedIndex = 0;
+#endif
+        }
+
+        /* Delegates */
+        public delegate InitArgs.EcgInitStart GetEcgInitArgs();
+        public delegate InitArgs.RToRInitStart GetRToRInitArgs();
+        public delegate InitArgs.BIOZInitStart GetBioZInitArgs();
+        public delegate InitArgs.PACEInitStart GetPaceInitArgs();
+        //public delegate void StreamingStartStopEventHandler(StreamingStartStopEventArgs e);
+
+        /* Events */
+        /// <summary>
+        /// Streaming event 
+        /// </summary>
+        public event EventHandler<StreamingStartStopEventArgs> StreamingStartStop;
+
+        /* Enums */
+        public enum StreamDataType
+        {
+            Ecg,
+            RToR,
+            Pace,
+            BioZ
+        };
+
+        /* Properties */
+        public RPCSupport.RPCClient RPCClient
+        {
+            set
+            {
+                rpcClient = value;
+                //streamHandler = new EventHandler<PartialArrayIntAvailableEventArgs>(On_AppendChart);
+                //rpcClient.streaming.PartialArrayIntAvailable += streamHandler;
+            }
+        }
+        /*
+        public bool Stream
+        {
+            get
+            {
+                return stream;
+            }
+            set
+            {
+                stream = value;
+                if (stream == true)
+                    rpcClient.streaming.PartialArrayIntAvailable += streamHandler;
+                else
+                    rpcClient.streaming.PartialArrayIntAvailable -= streamHandler;
+            }
+        }
+*/
+        public bool Connected
+        {
+            get
+            {
+                return connected;
+            }
+            set
+            {
+                connected = value;
+            }
+        }
+        public bool EnableECG
+        {
+            get
+            {
+                return enableECG;
+            }
+            set
+            {
+                enableECG = value;
+                chartECG.Enabled = value;
+            }
+        }
+        public bool EnableRToR
+        {
+            get
+            {
+                return enableRToR;
+            }
+            set
+            {
+                enableRToR = value;
+                chartRtoR.Enabled = value;
+                grpBxHeartRate.Enabled = value;
+            }
+        }
+        public bool EnableBioZ
+        {
+            get
+            {
+                return enableBioZ;
+            }
+            set
+            {
+                enableBioZ = value;
+                chartBioz.Enabled = value;
+            }
+        }
+        public bool EnablePace
+        {
+            get
+            {
+                return enablePace;
+            }
+            set
+            {
+                enablePace = value;
+                chartPace.Enabled = value;
+            }
+        }
+        public bool EnableDCLeadOff
+        {
+            get
+            {
+                return grpBxDCLeadOff.Enabled;
+            }
+            set
+            {
+                grpBxDCLeadOff.Enabled = value;
+            }
+
+        }
+        public bool EnableEcgDCLeadOff { get; set; }
+        public bool EnableBioZOverUnderRange
+        {
+            get
+            {
+                return grpBxBioZOverUnderRange.Enabled;
+            }
+            set
+            {
+                grpBxBioZOverUnderRange.Enabled = value;
+            }
+        }
+
+        public bool BioZMilliOhmRange
+        {
+            get
+            {
+                return (bioZRangeMultiplier == 1000);
+            }
+
+            set
+            {
+                if (value)
+                {
+                    bioZRangeMultiplier = 1000;
+                    chartBioz.ChartAreas["ChartArea"].AxisY.Title = "Bioz (m‎Ω)";
+                }
+                else
+                {
+                    bioZRangeMultiplier = 1;
+                    chartBioz.ChartAreas["ChartArea"].AxisY.Title = "Bioz (‎Ω)";
+                }
+            }
+        }
+
+        public double SampleRateEcg // Human readable sample rate
+        {
+            get
+            {
+                return ecgInfo.SampleRate;
+            }
+            set
+            {
+                ecgInfo.SampleRate = value;
+                //rToRInfo.SampleRate = value;
+                paceInfo.SampleRate = MasterClockFrequency * 2 * value;
+            }
+        }
+        public double SampleRateBioZ // Human readable sample rate
+        {
+            get
+            {
+                return bioZInfo.SampleRate;
+            }
+            set
+            {
+                bioZInfo.SampleRate = value;
+            }
+        }
+        public int GainECG // Human readable gain
+        {
+            get
+            {
+                return ecgInfo.Gain;
+            }
+            set
+            {
+                ecgInfo.Gain = value;
+            }
+        }
+        public int GainBioZ // Human readable gain
+        {
+            get
+            {
+                return bioZInfo.Gain;
+            }
+            set
+            {
+                bioZInfo.Gain = value;
+            }
+        }
+        public int CurrentBioZ // Human readable bioZ current generator magnitude
+        {
+            get
+            {
+                return bioZInfo.CurrentGenerator;
+            }
+            set
+            {
+                bioZInfo.CurrentGenerator = value;
+            }
+        }
+        /// <summary>
+        /// This value is t_RES = 1 / (2 * fMSTR) = 1 / (2 * 32768). For R-to-R the LSB time is 512*t_RES, while for
+        /// PACE, the value is t_RES.
+        /// </summary>
+        public double TimeResolution
+        {
+            get
+            {
+                return timeResolution;
+            }
+        }
+
+        public ChartInfo EcgInfo { get { return ecgInfo; } }
+        public ChartInfo RToRInfo { get { return rToRInfo; } }
+        public ChartInfo BioZInfo { get { return bioZInfo; } }
+        public ChartInfo PaceInfo { get { return paceInfo; } }
+
+        public InitArgs.EcgInitStart EcgArgs { get { return ecgArgs; } }
+        public InitArgs.RToRInitStart RToRArgs { get { return rtorArgs; } }
+
+        public int MasterClockField { get { return frequencyMasterField; } }
+        public int FrequencyMasterField
+        {
+            get
+            {
+                return frequencyMasterField;
+            }
+
+            set
+            {
+                frequencyMasterField = value;
+                masterClockFrequency = clockFreq[frequencyMasterField];
+                paceInfo.SampleRate = 2 * masterClockFrequency;
+                rToRInfo.SampleRate = masterClockFrequency / 256.0;
+                timeResolution = 1.0 / (2 * masterClockFrequency);
+            }
+        }
+        public double MasterClockFrequency
+        {
+            get
+            {
+                return masterClockFrequency;
+            }
+            /*
+            set
+            {
+                masterClockFrequency = value;
+                paceInfo.SampleRate = 2 * value;
+                TimeResolution = 1 / (2 * value);
+            }*/
+        }
+
+        public int EcgDecimationDelay
+        {
+            get
+            {
+                int fmstr = frequencyMasterField;
+                int ecgRate = ecgArgs.Rate;
+                int lpf = ecgArgs.Dlpf > 0 ? 1 : 0;
+
+                return ecgDecimationDelay[fmstr, ecgRate, lpf];
+            }
+        }
+        public int RToRDelay
+        {
+            get
+            {
+
+                return 5376 + 3370 + 256 * RToRWindowField;
+            }
+        }
+
+        public Chart ChartECG { get { return chartECG; } } // Testing
+        public IHspSetting HspSetting
+        {
+            get;
+            set;
+        }
+
+        private void EcgView_Load(object sender, EventArgs e)
+        {
+            InitChart();
+        }
+
+        String ValueToBits(int value, int width)
+        {
+            String str = "";
+            String val = "";
+            int mask = 1;
+            for (int i = 0; i < width; i++)
+            {
+                if ((mask & value) == mask)
+                {
+                    val = "1";
+                }
+                else
+                {
+                    val = "0";
+                }
+                mask = mask << 1;
+                str = val + str;
+            }
+            return str;
+        }
+
+        String[] ValueToBitStringArray(int width)
+        {
+            List<String> stringList = new List<string>();
+            for (int i = 0; i < Math.Pow(2, width); i++)
+            {
+                stringList.Add(ValueToBits(i, width));
+            }
+            return stringList.ToArray();
+        }
+
+        private void InitChart()
+        {
+            // Reset Averaging
+            //averageEcgSum = 0;
+            //averageEcgCount = 0;
+
+            //ecgInfo.Average = averageOptions[cboAverageEcg.SelectedIndex];
+            ecgInfo.Average = 1; 
+
+            // Rest Pace Tags
+            paceTag.Clear();
+
+            //UpdateChart(chartPace, new int[] {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0});
+            //paceData = new PaceData(new int[] { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 });
+            //UpdateChart(StreamDataType.Ecg, new int[] {1});
+            DisplayEcg(new double[] { 0 }, null, null);
+            //UpdateChart(chartECG, "SeriesRToR", new int[] {0});
+            //UpdateChart(StreamDataType.BioZ, new int[] {0});
+            DisplayBioZ(new double[] { 0 });
+            UpdateChartAxis(chartECG);
+            //UpdateChartAxis(chartRtoR);
+            UpdateChartAxis(chartBioz);
+            //UpdateChartAxis(chartPace);
+
+            paceData = null;
+            
+            //chartRtoR.ChartAreas[0].AxisY.Maximum = 100;
+        }
+
+        private void ResetChart()
+        {
+            paceData = null;
+        }
+
+        private void UpdateChartAxis(System.Windows.Forms.DataVisualization.Charting.Chart chart)
+        {
+            ChartInfo chartInfo = new ChartInfo();
+
+            if (chart.Name == "chartECG")
+            {
+                chartInfo = ecgInfo;
+            }
+            else if (chart.Name == "chartBioz")
+            {
+                chartInfo = bioZInfo;
+            }
+            else if (chart.Name == "chartPace")
+            {
+                chartInfo = paceInfo;
+            }
+            else if (chart.Name == "chartRtoR")
+            {
+                chartInfo = rToRInfo;
+            }
+
+            chart.SuspendLayout();
+            chart.ChartAreas[0].AxisX.Minimum = 0;
+            chart.ChartAreas[0].AxisX.Maximum = chartInfo.Points; 
+            chart.ChartAreas[0].AxisX.Interval = (float)chartInfo.Points / chartInfo.Length;
+
+            if (chart.Name != "chartRtoR")
+            {
+                chart.ChartAreas[0].AxisX.CustomLabels.Clear();
+                // Set X-axis range in seconds
+                for (int i = 0; i < 11; i ++ )
+                {
+                    chart.ChartAreas[0].AxisX.CustomLabels.Add(Math.Ceiling(((float)chartInfo.SampleRate / chartInfo.Average) * (2 * i - 1) / 2), 
+                        Math.Floor(((float)chartInfo.SampleRate / chartInfo.Average) * (2 * i + 1) / 2), i.ToString());
+                }
+            }
+
+            chart.ResumeLayout();
+        }
+
+        public void DisplayEcg(double[] ecg, PacePoint[] pace, double[] rToR)
+        {
+            List<double> xList = new List<double>();
+            List<double> yList = new List<double>();
+            List<double> xListFalling = new List<double>();
+            List<double> yListFalling = new List<double>();
+
+            int offset = chartECG.Series["Series"].Points.Count; // Pace Offset
+
+            chartECG.SuspendLayout();
+
+            for(int i = 0; i < ecg.Length; i++ )
+            {
+                chartECG.Series["Series"].Points.Add(ecg[i]);
+            }
+
+            if (pace != null)
+            {
+                // Shift Old Points - Rising
+                for (int i = 0; i < chartECG.Series["SeriesPace"].Points.Count; i++)
+                {
+                    double xValue = chartECG.Series["SeriesPace"].Points[i].XValue;
+
+                    if (offset > ecgInfo.Points) // Do not shift until chart is full
+                        xValue -= ecg.Length;
+
+                    if (xValue >= 0)
+                    {
+                        xList.Add(xValue);
+                        yList.Add(chartECG.Series["SeriesPace"].Points[i].YValues[0]);
+                    }
+                }
+                // Shift Old Points - Falling
+                for (int i = 0; i < chartECG.Series["SeriesPaceFalling"].Points.Count; i++ )
+                {
+                    double xValueFalling = chartECG.Series["SeriesPaceFalling"].Points[i].XValue;
+
+                    if (offset > ecgInfo.Points)
+                        xValueFalling -= ecg.Length;
+
+                    if (xValueFalling >= 0)
+                    {
+                        xListFalling.Add(xValueFalling);
+                        yListFalling.Add(chartECG.Series["SeriesPaceFalling"].Points[i].YValues[0]);
+                    }
+                }
+
+                // Add New Points
+                for (int i = 0; i < pace.Length; i++)
+                {
+                    if (pace[i].Polarity == true)
+                    {
+                        //xList.Add(pace[i].Time + offset);
+                        //yList.Add(0.05);
+                        chartECG.Series["SeriesPace"].Points.AddXY(pace[i].Time + offset, 0.05*(0+1));
+                        //System.Diagnostics.Debug.Print("Rising: " + (pace[i].Time + offset));
+                    }
+                    else
+                    {
+                        //xListFalling.Add(pace[i].Time + offset);
+                        //yListFalling.Add(-0.05);
+                        chartECG.Series["SeriesPaceFalling"].Points.AddXY(pace[i].Time + offset, -0.08*(0+1));
+                        //System.Diagnostics.Debug.Print("Falling: " + (pace[i].Time + offset));
+                    }
+                }
+
+                // Bind data to chart
+                //chartECG.Series["SeriesPace"].Points.DataBindXY(xList, yList);
+                //chartECG.Series["SeriesPaceFalling"].Points.DataBindXY(xListFalling, yListFalling);
+            }
+
+            UpdateChartFormat(StreamDataType.Ecg, ecgInfo);
+            //if (rToR != null) // Remove extra points
+            //    UpdateChartFormat(StreamDataType.RToR, rToRInfo);
+            if (pace != null) // Not needed anymore due to new plot style
+                UpdateChartFormat(StreamDataType.Pace, PaceInfo, offset + ecg.Length - ecgInfo.Points);
+            chartECG.ResumeLayout();
+        }
+
+        public void DisplayBioZ(double[] bioZ)
+        {
+            chartBioz.SuspendLayout();
+
+            for(int i = 0; i < bioZ.Length; i++)
+            {
+                chartBioz.Series["Series"].Points.Add(bioZ[i] * bioZRangeMultiplier);
+            }
+
+            UpdateChartFormat(StreamDataType.BioZ, bioZInfo);
+
+            chartBioz.ResumeLayout();
+        }
+
+        public double DisplayRToR(int rToR)
+        {
+            double rate;
+            int rToREcg;
+
+            long now = DateTime.Now.Ticks;
+
+            if (rToR == RToRMaxValue) // Ignore the max value, counter overflow condition
+                return 0;
+
+            rate = rToRCalculator.BeatsPerMinute(rToR); // RToRBeatsPerMinute(rToR);
+
+            if (heartRateList.Count < 10)
+                heartRateList.Add(rate);
+            else if (heartRateList.Count == 10)
+            {
+                heartRateList.RemoveAt(0);
+                heartRateList.Add(rate);
+
+                double sum = 0;
+                foreach (double d in heartRateList)
+                    sum += d;
+
+                lblHeartRateAverage10.Text = (sum / 10).ToString("F1");
+
+            }
+
+            lblHeartRateBeatToBeat.Text = rate.ToString("F1");
+
+            if (heartRateList.Count != 1)
+            {
+                rToREcg = rToRCalculator.EcgPoints(rToR, false); // RToREcgPoints(rToR, false);
+            }
+            else
+            {
+                // First Point delay by Tdrtor_ecg
+                //rToREcg = RToREcgPoints(rToR, true) + 1; // DEBUG 3 extra 0x07 are sent from FW at start of stream, 1 extra point for GUI chart init
+                int rToRInEcg = rToRCalculator.EcgPoints(rToR, true);
+                rToREcg = rToRInEcg + 1;
+            }
+
+            if (lastRToRTime < 0)
+                lastRToRTime = now;
+            else
+            {
+                long diff = now - lastRToRTime;
+                TimeSpan elaspedTicks = new TimeSpan(diff);
+
+                int fullScaleTime = (int)(Math.Pow(2, 14) * 512 * TimeResolution + 0.5);
+
+                if (elaspedTicks.TotalSeconds > fullScaleTime)
+                {
+                    int overflowCount = (int)(elaspedTicks.TotalSeconds / fullScaleTime);
+
+                    for (int i = 0; i < overflowCount; i++ )
+                        rToREcg += rToRCalculator.EcgPoints(EcgDelay.RToRMaxValue, false); //RToREcgPoints(0x3FFF, false);
+                }
+
+                lastRToRTime = now;
+            }
+
+            for (int i = 1; i < rToREcg - rToRRemoveOffset; i++ )
+            {
+                chartECG.Series["SeriesRToR"].Points.Add(-5000);
+            }
+            int rToRCount = chartECG.Series["SeriesRToR"].Points.Count;
+
+            if (rToRCount < chartECG.Series["Series"].Points.Count)
+                chartECG.Series["SeriesRToR"].Points.Add(chartECG.Series["Series"].Points[rToRCount].YValues[0]); // R to R comes in after ECG
+            else
+                chartECG.Series["SeriesRToR"].Points.Add(0); // R to R comes in faster than ECG, add as 0 for now
+
+            rToRRemoveOffset = 0;
+
+            return rate;
+        }
+
+        public void BioZFunction(bool enable)
+        {
+            chartBioz.Visible = enable;
+
+            if (enable == false)
+            {
+                //tableLayoutPanelCharts.RowCount = 1;
+                tableLayoutPanelCharts.RowStyles[1] = new RowStyle(SizeType.Percent, 0);
+                tableLayoutPanelCharts.RowStyles[0] = new RowStyle(SizeType.Percent, 100);
+            }
+            else
+            {
+                tableLayoutPanelCharts.RowStyles[0] = new RowStyle(SizeType.Percent, 50);
+                tableLayoutPanelCharts.RowStyles[1] = new RowStyle(SizeType.Percent, 50);
+            }
+
+        }
+
+
+        /*
+        public double RToRBeatsPerMinute(int rToRCode)
+        {
+            return 60 * 1000/ RToRMillisecond(rToRCode);
+        }
+
+        public double RToRMillisecond(int rToRCode)
+        {
+            return rToRCode * 512.0 * 1000 / (2 * MasterClockFrequency);
+        }
+
+        public int RToREcgPoints(int rToR, bool first)
+        {
+            double sampleRateRToR = MasterClockFrequency / 256;
+            int rToRDifferentialDelay = RToRDelay - EcgDecimationDelay;
+
+            double rToRInEcgSamples;
+            int rToRInEcgSamplesInt;
+
+            if (first)
+            {
+                rToRInEcgSamples = (rToR - rToRDifferentialDelay / 256.0) * (SampleRateECG / sampleRateRToR);
+                rToRInEcgSamplesInt = (int)(rToRInEcgSamples + 0.5);
+
+                rToRInEcgSampleError = rToRInEcgSamplesInt - rToRInEcgSamples;
+
+                return rToRInEcgSamplesInt;
+            }
+            else
+            {
+                rToRInEcgSamples = rToR * (SampleRateECG / sampleRateRToR) - rToRInEcgSampleError;
+                rToRInEcgSamplesInt = (int)(rToRInEcgSamples + 0.5);
+
+                rToRInEcgSampleError = rToRInEcgSamplesInt - rToRInEcgSamples;
+
+                return rToRInEcgSamplesInt;
+
+            }
+        }
+        */
+
+        private double[] UpdateChartFormat(StreamDataType dataType, ChartInfo chartInfo)
+        {
+            Chart chart = null;
+            String series = "";
+            double[] chartData;
+            int minRound = 0, maxRound = 0;
+            double min = Double.MaxValue, max = Double.MinValue;
+            int count;
+            
+            switch (dataType)
+            {
+                case StreamDataType.Ecg:
+                    chart = chartECG;
+                    series = "Series";
+                    break;
+                case StreamDataType.BioZ:
+                    chart = chartBioz;
+                    series = "Series";
+                    break;
+                case StreamDataType.RToR:
+                    chart = chartECG;
+                    series = "SeriesRToR";
+                    break;
+                case StreamDataType.Pace:
+                    chart = chartECG;
+                    series = "SeriesPace";
+                    break;
+
+            }
+            
+            count = chart.Series[series].Points.Count;
+
+            // Remove extra points
+            while (count > chartInfo.Points)
+            {
+                chart.Series[series].Points.RemoveAt(0);
+                if (dataType == StreamDataType.Ecg && EnableRToR) // Scroll R To R with ECG
+                    if (chart.Series["SeriesRToR"].Points.Count > 0) // TODO
+                        chart.Series["SeriesRToR"].Points.RemoveAt(0);
+                    else
+                        rToRRemoveOffset++;
+
+                count = chart.Series[series].Points.Count;
+            }
+
+            chartData = new double[chart.Series[series].Points.Count];
+
+            // Copy data points and find min/max value
+            for (int i = count / 2; i < count; i++) // Autoscale on last half of data only
+            {
+                chartData[i] = chart.Series[series].Points[i].YValues[0];
+
+                if (chartData[i] < min)
+                    min = chartData[i];
+                if (chartData[i] > max)
+                    max = chartData[i];
+            }
+
+            if (min == max) // prevent any invalid ranges
+                max = max + 1;
+
+            if (chartInfo == ecgInfo)
+            {
+                // Round to 1mV for ECG
+                minRound = ((int)(min / 1) - 1) * 1;
+                maxRound = ((int)(max / 1) + 1) * 1;
+
+                if (minRound == -1 && maxRound == 1) // Manual control of auto interval
+                    chart.ChartAreas[0].AxisY.Interval = 0.5;
+                else
+                    chart.ChartAreas[0].AxisY.Interval = 0;
+            }
+            else if (chartInfo == bioZInfo)
+            {
+                // Round to 100's for automatic axis scaling - for raw codes
+                minRound = (((int)min / 100 - 1) * 100);
+                maxRound = (((int)max / 100 + 1) * 100);
+            }
+            /*else if (chartInfo == rToRInfo)
+            {
+                // Round to 100ms
+                minRound = 0; // R to R rate should never be negative;
+                maxRound = ((int)(max / 100) + 1) * 100;
+            }
+            else
+            {
+                // Round to 100's for automatic axis scaling - for us of Pace length
+                minRound = (((int)min / 100 - 1) * 100);
+                maxRound = (((int)max / 100 + 1) * 100);
+            }*/
+
+            if (chartInfo == ecgInfo || chartInfo == bioZInfo)
+            {
+                // Set full Y-axis range
+                chart.ChartAreas[0].AxisY.Minimum = minRound;
+                chart.ChartAreas[0].AxisY.Maximum = maxRound;
+            }
+
+            return chartData;
+        }
+
+        private double[] UpdateChartFormat(StreamDataType dataType, ChartInfo chartInfo, int shift)
+        {
+            List<double> xList = new List<double>();
+            List<double> yList = new List<double>();
+            List<double> xListFalling = new List<double>();
+            List<double> yListFalling = new List<double>();
+
+            int offset = chartECG.Series["Series"].Points.Count; // Pace Offset
+
+            // Shift Old Points - Rising
+            for (int i = 0; i < chartECG.Series["SeriesPace"].Points.Count; i++)
+            {
+                double xValue = chartECG.Series["SeriesPace"].Points[i].XValue;
+
+                if (offset >= ecgInfo.Points) // Do not shift until chart is full
+                    xValue -= shift;
+
+                if (xValue >= 0)
+                {
+                    xList.Add(xValue);
+                    yList.Add(chartECG.Series["SeriesPace"].Points[i].YValues[0]);
+                }
+            }
+            // Shift Old Points - Falling
+            for (int i = 0; i < chartECG.Series["SeriesPaceFalling"].Points.Count; i++)
+            {
+                double xValueFalling = chartECG.Series["SeriesPaceFalling"].Points[i].XValue;
+
+                if (offset >= ecgInfo.Points)
+                    xValueFalling -= shift;
+
+                if (xValueFalling >= 0)
+                {
+                    xListFalling.Add(xValueFalling);
+                    yListFalling.Add(chartECG.Series["SeriesPaceFalling"].Points[i].YValues[0]);
+                }
+            }
+
+            chartECG.Series["SeriesPace"].Points.DataBindXY(xList, yList);
+            chartECG.Series["SeriesPaceFalling"].Points.DataBindXY(xListFalling, yListFalling);
+
+            return yList.ToArray();
+        }
+
+        /*
+        public EcgFifo[] ConvertEcg(int[] data)
+        {
+            EcgFifo[] voltage = new EcgFifo[data.Length];
+            ChartInfo chartInfo = ecgInfo;
+            int dataShift;
+
+            for (int i = 0; i < data.Length; i++ )
+            {
+                dataShift = data[i] >> chartInfo.Shift;
+
+                // Two's Complement Conversions
+                if (dataShift > chartInfo.Threshold)
+                {
+                    dataShift -= chartInfo.Offset;
+                }
+
+                voltage[i].Data = 1000 * 7.62939453125e-6 * dataShift / chartInfo.Gain;
+                voltage[i].PTag = data[i] & 0x07;
+                voltage[i].ETag = (data[i] >> 3) & 0x07;
+            }
+
+            return voltage;
+        }
+
+        public BioZFifo[] ConvertBioZ(int[] data)
+        {
+            BioZFifo[] impedence = new BioZFifo[data.Length];
+            ChartInfo chartInfo = bioZInfo;
+            int dataShift;
+            int dataPoint;
+
+            for (int i = 0; i < data.Length; i ++)
+            {
+                dataShift = data[i] >> chartInfo.Shift;
+
+                // Two's Complement Conversions
+                if (dataShift > chartInfo.Threshold)
+                {
+                    dataShift -= chartInfo.Offset;
+                }
+
+                // 1.9734 = 1/2^19 * 1e-6
+                impedence[i].Data = dataShift * bioZRangeMultiplier * 1.9073486328125 / 
+                    (chartInfo.Gain * ((chartInfo.CurrentGenerator == 0) ? 1 : chartInfo.CurrentGenerator));
+                impedence[i].BTag = data[i] & 0x07;
+            }
+
+            return impedence;
+        }
+
+        public PaceData ConvertPace(int[] data)
+        {
+            paceData = new PaceData(data);
+
+            return paceData;
+        }
+
+        public double ConvertRToR(int data)
+        {
+            rToRInterval = data;
+
+            return rToRInterval;
+        }
+         */
+
+        int[] ProcessRToR(int[] ecgData)
+        {
+            int[] rToRArray;
+
+            rToRArray = new int[ecgData.Length];
+            for (int i = 0; i < rToRArray.Length; i++)
+                rToRArray[i] = Int32.MinValue;
+
+            if (rToRInterval > 0) // Valid R to R
+            {
+                var ecgCode = EcgMaximumCode(ecgData);
+                rToRArray[ecgCode.Item1] = ecgCode.Item2;
+            }
+
+            return rToRArray;
+        }
+
+        public Tuple<int, int> EcgMaximumCode(int[] data)
+        {
+            int i;
+            int max = Int32.MinValue;
+            int maxCode = 0;
+            int maxIndex = 0;
+            int point;
+
+            for (i = 0; i < data.Length; i++ )
+            {
+                point = data[i] >> ecgInfo.Shift;
+                if (data[i] > ecgInfo.Threshold)
+                    point -= ecgInfo.Offset;
+
+                if (point > max) // Two's complement offset
+                {
+                    max = point;
+                    maxCode = data[i];
+                    maxIndex = i;
+                }
+            }
+
+            return new Tuple<int, int>(maxIndex, maxCode);
+        }
+
+        private bool LeadOffBit(int value, int bit)
+        {
+            int state;
+            int mask = 1 << bit;
+            state = ((value & mask) == mask) ? 1 : 0;
+            return state == 1;
+        }
+
+        //public void On_AppendChart(object sender, PartialArrayIntAvailableEventArgs e)
+        //{
+            /*
+            int leadOffState;
+
+            if (e.array1.Length > 0) // Occurs with streaming from flash
+            {
+                leadOffState = e.array1[0];
+
+                switch (e.reportID)
+                {
+                    case PartialArrayIntAvailableEventArgs.PACKET_MAX30001_LEADOFF_DC:
+                        if (LeadOffBit(leadOffState, 8)) // check for ECG
+                        {
+                            UpdateLeadOffState(lblLdoffPh, LeadOffBit(leadOffState, 3));
+                            UpdateLeadOffState(lblLdoffPl, LeadOffBit(leadOffState, 2));
+                            UpdateLeadOffState(lblLdoffNh, LeadOffBit(leadOffState, 1));
+                            UpdateLeadOffState(lblLdoffNl, LeadOffBit(leadOffState, 0));
+                        }
+                        if (LeadOffBit(leadOffState, 9)) // check for BIOZ
+                        {
+                            UpdateLeadOffState(lblLdoffPh, LeadOffBit(leadOffState, 3));
+                            UpdateLeadOffState(lblLdoffPl, LeadOffBit(leadOffState, 2));
+                            UpdateLeadOffState(lblLdoffNh, LeadOffBit(leadOffState, 1));
+                            UpdateLeadOffState(lblLdoffNl, LeadOffBit(leadOffState, 0));
+                        }
+                        break;
+                    case PartialArrayIntAvailableEventArgs.PACKET_MAX30001_LEADOFF_AC:
+                        UpdateLeadOffState(lblBioZACOver, LeadOffBit(leadOffState, 0));
+                        UpdateLeadOffState(lblBioZACUnder, LeadOffBit(leadOffState, 1));
+                        break;
+                }
+            }
+            */
+            /*
+            StringBuilder outputData = new StringBuilder();
+
+            if (e.reportID == PartialArrayIntAvailableEventArgs.PACKET_MAX30001_ECG)
+            {
+                if (e.array1.Length != 0)
+                {
+                    if (ecgBuffer != null)
+                    { 
+                        UpdateChart(StreamDataType.Ecg, ecgBuffer);
+
+                        if (EnableRToR)
+                        {
+                            int[] rToRArray = ProcessRToR(ecgBuffer);
+
+                            UpdateChart(StreamDataType.RToR, rToRArray);
+                            rToRInterval = -1; // R to R processed reset value
+                        }
+                    }
+
+                    ecgBuffer = ecgBuffer1;
+                    ecgBuffer1 = e.array1; 
+                    // DEBUG
+                    foreach (int data in e.array1)
+                    {
+                        outputData.Append(data.ToString());
+                        outputData.Append(Environment.NewLine);
+                    }
+
+                    file.Write(outputData.ToString());
+
+                    ConvertEcg(e.array1);
+                }
+            }
+            if (e.reportID == PartialArrayIntAvailableEventArgs.PACKET_MAX30001_PACE)
+            {
+                if (e.array1.Length != 0)
+                {
+                    // Store Pace data for update with ECG PTAGs
+                    ConvertPace(e.array1);
+                }
+            }
+            if (e.reportID == PartialArrayIntAvailableEventArgs.PACKET_MAX30001_RTOR)
+            {
+                if (e.array1.Length != 0)
+                {
+                    ConvertRToR(e.array1[0]);
+                }
+            }
+            if (e.reportID == PartialArrayIntAvailableEventArgs.PACKET_MAX30001_BIOZ)
+            {
+                if (e.array1.Length != 0)
+                {
+                    ConvertBioZ(e.array1);
+                    //UpdateChart(StreamDataType.BioZ, e.array1);
+                }
+            }*/
+        //}
+
+        private void Start()
+        {
+            //ParseRbiasArgs();
+            /*rpcClient.MAX30001.Rbias_FMSTR_Init(
+                rbiasInit.En_rbias,
+                rbiasInit.Rbias,
+                rbiasInit.Rbiasp,
+                rbiasInit.Rbiasn,
+                rbiasInit.Fmstr
+            );*/
+
+
+            //if (ckbxCal.Checked)
+            /*{
+                startedCal = true;
+                //ParseCalArgs();
+                calArgs.En_Vcal = 0;
+                calArgs.Vmode = 0;
+                calArgs.Vmag = 1;
+                calArgs.Fcal = 3;
+                calArgs.Thigh = 0x1F;
+                calArgs.Fifty = 0;
+                rpcClient.MAX30001.CAL_InitStart(
+                    calArgs.En_Vcal,
+                    calArgs.Vmode,
+                    calArgs.Vmag,
+                    calArgs.Fcal,
+                    calArgs.Thigh,
+                    calArgs.Fifty);
+            }*/
+
+            /*
+            rpcClient.MAX30001.INT_assignment(MAX30001.max30001_intrpt_Location.MAX30001_INT_B, MAX30001.max30001_intrpt_Location.MAX30001_NO_INT, MAX30001.max30001_intrpt_Location.MAX30001_NO_INT,  //  en_enint_loc,      en_eovf_loc,   en_fstint_loc,
+                       MAX30001.max30001_intrpt_Location.MAX30001_INT_2B, MAX30001.max30001_intrpt_Location.MAX30001_INT_B, MAX30001.max30001_intrpt_Location.MAX30001_NO_INT,  //  en_dcloffint_loc,  en_bint_loc,   en_bovf_loc,
+                       MAX30001.max30001_intrpt_Location.MAX30001_INT_2B, MAX30001.max30001_intrpt_Location.MAX30001_INT_2B, MAX30001.max30001_intrpt_Location.MAX30001_NO_INT,  //  en_bover_loc,      en_bundr_loc,  en_bcgmon_loc,
+                       MAX30001.max30001_intrpt_Location.MAX30001_INT_B, MAX30001.max30001_intrpt_Location.MAX30001_NO_INT, MAX30001.max30001_intrpt_Location.MAX30001_NO_INT,  //  en_pint_loc,       en_povf_loc,   en_pedge_loc,
+                       MAX30001.max30001_intrpt_Location.MAX30001_INT_2B, MAX30001.max30001_intrpt_Location.MAX30001_INT_B, MAX30001.max30001_intrpt_Location.MAX30001_NO_INT,  //  en_lonint_loc,     en_rrint_loc,  en_samp_loc,
+                       MAX30001.max30001_intrpt_type.MAX30001_INT_ODNR, MAX30001.max30001_intrpt_type.MAX30001_INT_ODNR);                //  intb_Type,         int2b_Type)
+            */
+            if (connected)
+            {
+                rpcClient.MAX30001.WriteReg(0x02, 0x03); // Stop Interrupts
+                rpcClient.MAX30001.WriteReg(0x03, 0x03); // Stop Interrupts
+            }
+
+            if (enableECG)    
+            {
+                startedEcg = true;
+                if (GetEcgInitArgsPointer != null)
+                {
+                    ecgArgs = GetEcgInitArgsPointer();
+                    rpcClient.MAX30001.ECG_InitStart(
+                        ecgArgs.En_ecg,
+                        ecgArgs.Openp,
+                        ecgArgs.Openn,
+                        ecgArgs.Pol,
+                        ecgArgs.Calp_sel,
+                        ecgArgs.Caln_sel,
+                        ecgArgs.E_fit,
+                        ecgArgs.Rate,
+                        ecgArgs.Gain,
+                        ecgArgs.Dhpf,
+                        ecgArgs.Dlpf);
+                }
+
+ /*               rpcClient.MAX30001.ECGFast_Init(
+                    ecgFastArgs.Clr_Fast,
+                    ecgFastArgs.Fast,
+                    ecgFastArgs.Fast_Th);*/
+            }
+
+            if (enablePace)
+            {
+                startedPace = true;
+                if (GetPaceInitArgsPointer != null)
+                {
+                    paceArgs = GetPaceInitArgsPointer();
+                    rpcClient.MAX30001.PACE_InitStart(
+                        paceArgs.En_pace,
+                        paceArgs.Clr_pedge,
+                        paceArgs.Pol,
+                        paceArgs.Gn_diff_off,
+                        paceArgs.Gain,
+                        paceArgs.Aout_lbw,
+                        paceArgs.Aout,
+                        paceArgs.Dacp,
+                        paceArgs.Dacn);
+                }
+            }
+            if (enableBioZ)
+            {
+                startedBioz = true;
+                if (GetBioZInitArgsPointer != null)
+                {
+                    biozArgs = GetBioZInitArgsPointer();
+                    rpcClient.MAX30001.BIOZ_InitStart(
+                        biozArgs.En_bioz,
+                        biozArgs.Openp,
+                        biozArgs.Openn,
+                        biozArgs.Calp_sel,
+                        biozArgs.Caln_sel,
+                        biozArgs.CG_mode,
+                        biozArgs.B_fit,
+                        biozArgs.Rate,
+                        biozArgs.Ahpf,
+                        biozArgs.Ext_rbias,
+                        biozArgs.Gain,
+                        biozArgs.Dhpf,
+                        biozArgs.Dlpf,
+                        biozArgs.Fcgen,
+                        biozArgs.Cgmon,
+                        biozArgs.Cgmag,
+                        biozArgs.Phoff);
+                }
+            }
+            if (enableRToR)
+            {
+                startedRtoR = true;
+                if (GetRToRInitArgsPointer != null)
+                {
+                    rtorArgs = GetRToRInitArgsPointer();
+                    rpcClient.MAX30001.RtoR_InitStart(
+                        rtorArgs.En_rtor,
+                        rtorArgs.Wndw,
+                        rtorArgs.Gain,
+                        rtorArgs.Pavg,
+                        rtorArgs.Ptsf,
+                        rtorArgs.Hoff,
+                        rtorArgs.Ravg,
+                        rtorArgs.Rhsf,
+                        rtorArgs.Clr_rrint);
+                }
+
+                rToRCalculator = new RToRCalculator(frequencyMasterField, ecgArgs.Rate, ecgArgs.Dlpf, rtorArgs.Wndw);
+            }
+
+            /*rpcClient.MAX30001.Rbias_FMSTR_Init(
+                0,
+                0,
+                0,
+                0,
+                0);*/
+
+            rpcClient.MAX30001.INT_assignment(MAX30001.max30001_intrpt_Location.MAX30001_INT_B, MAX30001.max30001_intrpt_Location.MAX30001_NO_INT, MAX30001.max30001_intrpt_Location.MAX30001_NO_INT,  //  en_enint_loc,      en_eovf_loc,   en_fstint_loc,
+                      MAX30001.max30001_intrpt_Location.MAX30001_INT_2B, MAX30001.max30001_intrpt_Location.MAX30001_INT_B, MAX30001.max30001_intrpt_Location.MAX30001_NO_INT,  //  en_dcloffint_loc,  en_bint_loc,   en_bovf_loc,
+                      MAX30001.max30001_intrpt_Location.MAX30001_INT_2B, MAX30001.max30001_intrpt_Location.MAX30001_INT_2B, MAX30001.max30001_intrpt_Location.MAX30001_NO_INT,  //  en_bover_loc,      en_bundr_loc,  en_bcgmon_loc,
+                      MAX30001.max30001_intrpt_Location.MAX30001_INT_B, MAX30001.max30001_intrpt_Location.MAX30001_NO_INT, MAX30001.max30001_intrpt_Location.MAX30001_NO_INT,  //  en_pint_loc,       en_povf_loc,   en_pedge_loc,
+                      MAX30001.max30001_intrpt_Location.MAX30001_INT_2B, MAX30001.max30001_intrpt_Location.MAX30001_INT_B, MAX30001.max30001_intrpt_Location.MAX30001_NO_INT,  //  en_lonint_loc,     en_rrint_loc,  en_samp_loc,
+                      MAX30001.max30001_intrpt_type.MAX30001_INT_ODNR, MAX30001.max30001_intrpt_type.MAX30001_INT_ODNR);                //  intb_Type,         int2b_Type)
+
+            if (startedEcg | startedCal | startedPace | startedBioz | startedRtoR)
+            {
+                rpcClient.MAX30001.StartStreaming();
+            }
+
+            // Clear Lead Off
+            UpdateLeadOffState(lblLdoffPh, false);
+            UpdateLeadOffState(lblLdoffPl, false);
+            UpdateLeadOffState(lblLdoffNh, false);
+            UpdateLeadOffState(lblLdoffNl, false);
+            UpdateLeadOffState(lblBioZACOver, false);
+            UpdateLeadOffState(lblBioZACUnder, false); 
+
+            // Clear Charts 
+            chartECG.Series["Series"].Points.Clear();
+            chartECG.Series["SeriesRToR"].Points.Clear();
+            chartECG.Series["SeriesPace"].Points.Clear();
+            chartECG.Series["SeriesPaceFalling"].Points.Clear();
+            //chartRtoR.Series["Series"].Points.Clear();
+            chartBioz.Series["Series"].Points.Clear();
+            //chartPace.Series["Series"].Points.Clear();
+            InitChart();
+            fileLogData.Clear(); // Clear initiation points
+
+            // Reset R To R state
+            rToRRemoveOffset = 0;
+            lastRToRTime = -1;
+
+            // Clear Heart Rate
+            heartRateList = new List<double>(10);
+            lblHeartRateAverage10.Text = "--";
+            lblHeartRateBeatToBeat.Text = "--";
+
+#if CES_DEMO
+            hpf.Reset();
+#endif
+            btnMonitor.Text = "Stop Monitor";
+            if (StreamingStartStop != null)
+            {
+                StreamingStartStop(this, new StreamingStartStopEventArgs() { state = true });
+            }
+#if FILE_LOG
+            // Debug
+            DateTime localDate = DateTime.Now;
+
+            file = new StreamWriter("hsp_ecg_output_data.csv");
+            file.WriteLine("# " + localDate.ToString() + ", Sample Rate Register Setting: " + ecgArgs.Rate);
+            file.WriteLine(@"# ECG (μV)     Raw Integer");
+#endif
+
+            // DEBUG - simulate the "first" R To R value as a > 10 second value to see what happens
+            //DisplayRToR(0x600);
+        }
+        private void btnMonitor_Click(object sender, EventArgs e)
+        {
+            if (btnMonitor.Text == "Start Monitor")
+            {
+                if (Connected)
+                {
+                    //if (ckbxEcg.Checked | ckbxPace.Checked | ckbxBioz.Checked | ckbxRtoR.Checked)
+                    if (EnableECG | EnableBioZ)
+                        Start();
+                    else if (EnableRToR)
+                        MessageBox.Show("Also enable ECG for R-to-R streaming", "Streaming");
+                    else
+                        MessageBox.Show("Enable ECG or BioZ before starting streaming", "Streaming");
+                }
+            }
+            else
+            {
+                Stop();
+            }
+        }
+
+        private void Stop()
+        {
+            if (startedEcg | startedCal | startedPace | startedBioz | startedRtoR)
+            {
+                rpcClient.MAX30001.StopStreaming(connected);
+
+                //rpcClient.MAX30001.WriteReg(0x02, 0);
+                //rpcClient.MAX30001.WriteReg(0x03, 0);
+            }
+
+            startedCal = false;
+            startedEcg = false;
+            startedPace = false;
+            startedBioz = false;
+            startedRtoR = false;
+
+            btnMonitor.Text = "Start Monitor";
+            if (StreamingStartStop != null)
+                StreamingStartStop(this, new StreamingStartStopEventArgs() { state = false });
+
+#if FILE_LOG
+            // Debug
+            file.Close();
+#endif
+        }
+
+        // Clean up streaming 
+        public void Close()
+        {
+            // Disable streaming if enabled
+            if (StreamingStartStop != null && btnMonitor.Text == "Stop Monitor")
+            {
+                StreamingStartStop(this, new StreamingStartStopEventArgs() { state = false });
+                Stop();
+            }
+        }
+
+
+        private void chkbxCheckedChanged(object sender, EventArgs e)
+        {
+            /*
+            EnableDisableControls(CAL_init, ckbxCal.Checked);
+            EnableDisableControls(ECG_InitStart, ckbxEcg.Checked);
+            EnableDisableControls(PACE_InitStart, ckbxPace.Checked);
+            EnableDisableControls(BIOZ_InitStart, ckbxBioz.Checked);
+            EnableDisableControls(ECGFast_Init, ckbxEcg.Checked);
+            EnableDisableControls(RtoR_InitStart, ckbxRtoR.Checked);
+            */
+        }
+
+        private void btnStartTest_Click(object sender, EventArgs e)
+        {
+            System.Windows.Forms.DataVisualization.Charting.Chart test = chartECG;
+
+            double ymin = test.ChartAreas[0].AxisY.Minimum;  
+            double ymax = test.ChartAreas[0].AxisY.Maximum;
+            if (btnStartTest.Text == "Start Test")
+            {
+                rpcClient.MAX30001.StartTest();
+                btnStartTest.Text = "Stop Test";
+            }
+            else
+            {
+                Stop();
+                btnStartTest.Text = "Start Test";
+            }
+
+        }
+
+        public void SetDCLeadOff(int leadOffState)
+        {
+            if (LeadOffBit(leadOffState, 8)) // check for ECG
+            {
+                UpdateLeadOffState(lblLdoffPh, LeadOffBit(leadOffState, 3));
+                UpdateLeadOffState(lblLdoffPl, LeadOffBit(leadOffState, 2));
+                UpdateLeadOffState(lblLdoffNh, LeadOffBit(leadOffState, 1));
+                UpdateLeadOffState(lblLdoffNl, LeadOffBit(leadOffState, 0));
+            }
+            if (LeadOffBit(leadOffState, 9)) // check for BIOZ
+            {
+                UpdateLeadOffState(lblLdoffPh, LeadOffBit(leadOffState, 3));
+                UpdateLeadOffState(lblLdoffPl, LeadOffBit(leadOffState, 2));
+                UpdateLeadOffState(lblLdoffNh, LeadOffBit(leadOffState, 1));
+                UpdateLeadOffState(lblLdoffNl, LeadOffBit(leadOffState, 0));
+            }
+        }
+
+        public void SetACLeadOff(int leadOffState)
+        {
+            UpdateLeadOffState(lblBioZACOver, LeadOffBit(leadOffState, 0));
+            UpdateLeadOffState(lblBioZACUnder, LeadOffBit(leadOffState, 1));
+        }
+
+        //
+        // Update LeadOff GUI State
+        //
+        private void UpdateLeadOffState(TextBox tb, bool state)
+        {
+            tb.Text = (state == true) ? "1" : "0";
+            if (state == true) tb.BackColor = Color.LightGreen;
+            else tb.BackColor = Color.Yellow;
+        }
+
+        private void UpdateLeadOffState(Label lbl, bool state)
+        {
+            lbl.ForeColor = (state == true) ? MaximStyle.MaximColor.Red : MaximStyle.MaximColor.Green;
+        }
+
+        /* Inner Classes of EcgView */
+
+
+
+        /// <summary>
+        /// Information used to generate the X-axis of a chart
+        /// </summary>
+        public class ChartInfo
+        {
+            /// <summary>
+            /// Sample Rate
+            /// </summary>
+            private double sampleRate = 0;
+            public double SampleRate
+            {
+                get
+                {
+                    return sampleRate;
+                }
+                set
+                {
+                    sampleRate = value;
+                    points = Convert.ToInt32(length * sampleRate / average);
+                }
+            }
+
+            /// <summary>
+            /// Length of chart in seconds
+            /// </summary>
+            private int length = 1;
+            public int Length
+            {
+                get
+                {
+                    return length;
+                }
+                set
+                {
+                    length = value;
+                    points = Convert.ToInt32(length * sampleRate / average);
+                }
+            }
+
+            /// <summary>
+            /// Number of total points in chart
+            /// </summary>
+            private int points = 1;
+            public int Points
+            {
+                get
+                {
+                    return points;
+                }
+            }
+
+            public int Gain;
+
+            private int average = 1;
+            public int Average
+            {
+                get
+                {
+                    return average;
+                }
+                set
+                {
+                    average = value;
+                    points = Convert.ToInt32(length * sampleRate / average);
+                }
+            }
+
+            /// <summary>
+            /// Value passed the threshold is negative
+            /// </summary>
+            private int threshold = Int32.MaxValue;
+            public int Threshold
+            {
+                get
+                {
+                    return threshold;
+                }
+                set
+                {
+                    threshold = value;
+                }
+            }
+
+            /// <summary>
+            /// Amount to offset value by to convert to negative 
+            /// </summary>
+            private int offset = 0;
+            public int Offset
+            {
+                get
+                {
+                    return offset;
+                }
+                set
+                {
+                    offset = value;
+                }
+            }
+
+            /// <summary>
+            /// Number of bits to shift raw data by
+            /// </summary>
+            public int Shift;
+
+
+            /// <summary>
+            /// Current generator for BioZ
+            /// </summary>
+            public int CurrentGenerator;
+
+            public Chart Chart;
+
+            public String SeriesName;
+        }
+
+        public class PacePoint
+        {
+            public double Time;
+            public bool Polarity;
+
+            public PacePoint(double time, bool polarity)
+            {
+                this.Time = time;
+                this.Polarity = polarity;
+            }
+        }
+
+        // DEBUG
+        int offsetTest = 0;
+        private void button1_Click(object sender, EventArgs e)
+        {
+            //timer1.Enabled = !timer1.Enabled;
+            chartECG.Series["SeriesPace"].Points.AddXY(1.99354553222656 + offsetTest, 0.05 * (1));
+            chartECG.Series["SeriesPaceFalling"].Points.AddXY(1.99455261230469 + offsetTest, -0.08 * (1));
+
+            offsetTest += 100;
+        }
+
+        // DEBUG
+        private void timer1_Tick(object sender, EventArgs e)
+        {
+            /*
+            PartialArrayIntAvailableEventArgs ecg1 = new PartialArrayIntAvailableEventArgs();
+            PartialArrayIntAvailableEventArgs ecg2 = new PartialArrayIntAvailableEventArgs();
+            PartialArrayIntAvailableEventArgs rr1 = new PartialArrayIntAvailableEventArgs();
+
+            ecg1.array1 = new int[] { 0x00, 0x00, 0x00, 0x00, 0xfffff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
+            ecg2.array1 = new int[] { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
+            rr1.array1 = new int[] { 0x88 };
+
+            ecg1.reportID = PartialArrayIntAvailableEventArgs.PACKET_MAX30001_ECG;
+            ecg2.reportID = PartialArrayIntAvailableEventArgs.PACKET_MAX30001_ECG;
+            rr1.reportID = PartialArrayIntAvailableEventArgs.PACKET_MAX30001_RTOR;
+
+            if (clicks % 15 == 0)
+            {
+                On_AppendChart(this, ecg2);
+            }
+            else if (clicks % 5 == 0)
+            {
+                On_AppendChart(this, ecg1);
+                On_AppendChart(this, rr1);
+            }
+            else
+                On_AppendChart(this, ecg2);
+
+            clicks++;
+            */
+            DisplayEcg(new double[] { 0, 0, 0, 0, 1, 2, 3, 4, 5 }, null, new double[] {-10, -10, -10, -10, -10, -10, 4, -10, -10});
+        }
+    }
+}