![](/media/cache/img/default_profile.jpg.50x50_q85.jpg)
repo time
Dependencies: mbed MAX14720 MAX30205 USBDevice
Diff: HspGuiSourceV301/HSPGui/CustomControls/EcgView.cs
- 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}); + } + } +}