repo time

Dependencies:   mbed MAX14720 MAX30205 USBDevice

Committer:
darienf
Date:
Tue Apr 06 06:41:40 2021 +0000
Revision:
20:6d2af70c92ab
another repo

Who changed what in which revision?

UserRevisionLine numberNew contents of line
darienf 20:6d2af70c92ab 1 //using System;
darienf 20:6d2af70c92ab 2 //using System.Collections;
darienf 20:6d2af70c92ab 3 //using System.Collections.Generic;
darienf 20:6d2af70c92ab 4 //using System.ComponentModel;
darienf 20:6d2af70c92ab 5 //using System.Linq;
darienf 20:6d2af70c92ab 6 //using System.Text;
darienf 20:6d2af70c92ab 7 //using System.Windows.Forms;
darienf 20:6d2af70c92ab 8 //using Microsoft.Win32.SafeHandles;
darienf 20:6d2af70c92ab 9
darienf 20:6d2af70c92ab 10 namespace Maxim.MAX30101
darienf 20:6d2af70c92ab 11 {
darienf 20:6d2af70c92ab 12 #pragma warning disable 1574
darienf 20:6d2af70c92ab 13 /// <summary>
darienf 20:6d2af70c92ab 14 /// MedicalChartHelper class, an invisible "helper" class,
darienf 20:6d2af70c92ab 15 /// helps manage an existing standard chart object.
darienf 20:6d2af70c92ab 16 ///
darienf 20:6d2af70c92ab 17 /// Initializes the standard
darienf 20:6d2af70c92ab 18 /// System.Windows.Forms.DataVisualization.Charting.Chart chart
darienf 20:6d2af70c92ab 19 /// with a maximum of 3 chart areas vertically aligned with each other.
darienf 20:6d2af70c92ab 20 ///
darienf 20:6d2af70c92ab 21 /// When data is appended to the chart, the chart series data
darienf 20:6d2af70c92ab 22 /// is initially aligned with the left. Once the X axis reaches the
darienf 20:6d2af70c92ab 23 /// right side of the screen, the chart begins scrolling so that
darienf 20:6d2af70c92ab 24 /// newest data is aligned to the right edge.
darienf 20:6d2af70c92ab 25 ///
darienf 20:6d2af70c92ab 26 /// </summary>
darienf 20:6d2af70c92ab 27 #pragma warning restore 1574
darienf 20:6d2af70c92ab 28 public class MedicalChartHelper
darienf 20:6d2af70c92ab 29 {
darienf 20:6d2af70c92ab 30 private System.Windows.Forms.DataVisualization.Charting.Chart _chart;
darienf 20:6d2af70c92ab 31 private string _chartArea1Name;
darienf 20:6d2af70c92ab 32 private string _chartArea2Name;
darienf 20:6d2af70c92ab 33 private string _chartArea3Name;
darienf 20:6d2af70c92ab 34 private string _series1Name;
darienf 20:6d2af70c92ab 35 private string _series2Name;
darienf 20:6d2af70c92ab 36 private string _series3Name;
darienf 20:6d2af70c92ab 37 private string _chartArea1AxisYTitle;
darienf 20:6d2af70c92ab 38 private string _chartArea2AxisYTitle;
darienf 20:6d2af70c92ab 39 private string _chartArea3AxisYTitle;
darienf 20:6d2af70c92ab 40
darienf 20:6d2af70c92ab 41 /// <summary>
darienf 20:6d2af70c92ab 42 /// Constructor
darienf 20:6d2af70c92ab 43 /// </summary>
darienf 20:6d2af70c92ab 44 /// <param name="chart"></param>
darienf 20:6d2af70c92ab 45 /// <param name="chartArea1Name"></param>
darienf 20:6d2af70c92ab 46 /// <param name="series1Name"></param>
darienf 20:6d2af70c92ab 47 /// <param name="chartArea1AxisYTitle"></param>
darienf 20:6d2af70c92ab 48 /// <param name="chartArea2Name"></param>
darienf 20:6d2af70c92ab 49 /// <param name="series2Name"></param>
darienf 20:6d2af70c92ab 50 /// <param name="chartArea2AxisYTitle"></param>
darienf 20:6d2af70c92ab 51 /// <param name="chartArea3Name"></param>
darienf 20:6d2af70c92ab 52 /// <param name="series3Name"></param>
darienf 20:6d2af70c92ab 53 /// <param name="chartArea3AxisYTitle"></param>
darienf 20:6d2af70c92ab 54 public MedicalChartHelper(System.Windows.Forms.DataVisualization.Charting.Chart chart,
darienf 20:6d2af70c92ab 55 string chartArea1Name = "ChartArea1Red", string series1Name = "SeriesRed", string chartArea1AxisYTitle = "Red ADC Code",
darienf 20:6d2af70c92ab 56 string chartArea2Name = "ChartArea2IR", string series2Name = "SeriesIR", string chartArea2AxisYTitle = "IR ADC Code",
darienf 20:6d2af70c92ab 57 string chartArea3Name = "ChartArea3Green", string series3Name = "SeriesGreen", string chartArea3AxisYTitle = "Green ADC Code",
darienf 20:6d2af70c92ab 58 DataFormats dataFormat = DataFormats.FormatUnsigned
darienf 20:6d2af70c92ab 59 )
darienf 20:6d2af70c92ab 60 {
darienf 20:6d2af70c92ab 61 // implementation: see InitCharting()
darienf 20:6d2af70c92ab 62 // default X axis length should be 10 seconds of data
darienf 20:6d2af70c92ab 63 _chart = chart;
darienf 20:6d2af70c92ab 64 _chartArea1Name = chartArea1Name;
darienf 20:6d2af70c92ab 65 _chartArea2Name = chartArea2Name;
darienf 20:6d2af70c92ab 66 _chartArea3Name = chartArea3Name;
darienf 20:6d2af70c92ab 67 _series1Name = series1Name;
darienf 20:6d2af70c92ab 68 _series2Name = series2Name;
darienf 20:6d2af70c92ab 69 _series3Name = series3Name;
darienf 20:6d2af70c92ab 70 _chartArea1AxisYTitle = chartArea1AxisYTitle;
darienf 20:6d2af70c92ab 71 _chartArea2AxisYTitle = chartArea2AxisYTitle;
darienf 20:6d2af70c92ab 72 _chartArea3AxisYTitle = chartArea3AxisYTitle;
darienf 20:6d2af70c92ab 73
darienf 20:6d2af70c92ab 74 DataFormat = dataFormat;
darienf 20:6d2af70c92ab 75
darienf 20:6d2af70c92ab 76 // VERIFY: OS24EVK-75 replace _xCount with _xCount1, _xCount2, _xCount3
darienf 20:6d2af70c92ab 77 //_xCount = 0;
darienf 20:6d2af70c92ab 78 _xCount1 = 0;
darienf 20:6d2af70c92ab 79 _xCount2 = 0;
darienf 20:6d2af70c92ab 80 _xCount3 = 0;
darienf 20:6d2af70c92ab 81
darienf 20:6d2af70c92ab 82 _chart.Titles[0].Visible = false;
darienf 20:6d2af70c92ab 83
darienf 20:6d2af70c92ab 84 _chart.ChartAreas[_chartArea2Name].AlignWithChartArea = _chartArea1Name;
darienf 20:6d2af70c92ab 85 _chart.ChartAreas[_chartArea3Name].AlignWithChartArea = _chartArea1Name;
darienf 20:6d2af70c92ab 86
darienf 20:6d2af70c92ab 87 _chart.ChartAreas[_chartArea1Name].AxisY.Title = chartArea1AxisYTitle;
darienf 20:6d2af70c92ab 88 _chart.ChartAreas[_chartArea2Name].AxisY.Title = chartArea2AxisYTitle;
darienf 20:6d2af70c92ab 89 _chart.ChartAreas[_chartArea3Name].AxisY.Title = chartArea3AxisYTitle;
darienf 20:6d2af70c92ab 90
darienf 20:6d2af70c92ab 91 //_chart.ChartAreas[_chartArea1Name].AxisX.LabelStyle.Enabled = !_plotXAxisNoLabels;
darienf 20:6d2af70c92ab 92 //_chart.ChartAreas[_chartArea2Name].AxisX.LabelStyle.Enabled = !_plotXAxisNoLabels;
darienf 20:6d2af70c92ab 93 //_chart.ChartAreas[_chartArea3Name].AxisX.LabelStyle.Enabled = !_plotXAxisNoLabels;
darienf 20:6d2af70c92ab 94
darienf 20:6d2af70c92ab 95 _chart.ChartAreas[_chartArea1Name].Visible = true; // valid_Red;
darienf 20:6d2af70c92ab 96 _chart.ChartAreas[_chartArea2Name].Visible = true; // valid_IR;
darienf 20:6d2af70c92ab 97 _chart.ChartAreas[_chartArea3Name].Visible = true; // valid_Green;
darienf 20:6d2af70c92ab 98
darienf 20:6d2af70c92ab 99 _chart.Series[_series1Name].MarkerSize = 1; // Charting.Series.MarkerSize=0 makes no points visible
darienf 20:6d2af70c92ab 100 _chart.Series[_series1Name].BorderWidth = 1; // Charting.Series.BorderWidth is actually the line thickness
darienf 20:6d2af70c92ab 101 _chart.Series[_series1Name].XValueType = System.Windows.Forms.DataVisualization.Charting.ChartValueType.UInt32;
darienf 20:6d2af70c92ab 102 _chart.Series[_series1Name].YValueType = System.Windows.Forms.DataVisualization.Charting.ChartValueType.Int32;
darienf 20:6d2af70c92ab 103
darienf 20:6d2af70c92ab 104 _chart.Series[_series2Name].MarkerSize = 1; // Charting.Series.MarkerSize=0 makes no points visible
darienf 20:6d2af70c92ab 105 _chart.Series[_series2Name].BorderWidth = 1; // Charting.Series.BorderWidth is actually the line thickness
darienf 20:6d2af70c92ab 106 _chart.Series[_series2Name].XValueType = System.Windows.Forms.DataVisualization.Charting.ChartValueType.UInt32;
darienf 20:6d2af70c92ab 107 _chart.Series[_series2Name].YValueType = System.Windows.Forms.DataVisualization.Charting.ChartValueType.Int32;
darienf 20:6d2af70c92ab 108
darienf 20:6d2af70c92ab 109 _chart.Series[_series3Name].MarkerSize = 1; // Charting.Series.MarkerSize=0 makes no points visible
darienf 20:6d2af70c92ab 110 _chart.Series[_series3Name].BorderWidth = 1; // Charting.Series.BorderWidth is actually the line thickness
darienf 20:6d2af70c92ab 111 _chart.Series[_series3Name].XValueType = System.Windows.Forms.DataVisualization.Charting.ChartValueType.UInt32;
darienf 20:6d2af70c92ab 112 _chart.Series[_series3Name].YValueType = System.Windows.Forms.DataVisualization.Charting.ChartValueType.Int32;
darienf 20:6d2af70c92ab 113 }
darienf 20:6d2af70c92ab 114
darienf 20:6d2af70c92ab 115 /// <summary>
darienf 20:6d2af70c92ab 116 /// Clear chart data, reset _xCount, reset _plotPointsToSkip
darienf 20:6d2af70c92ab 117 /// </summary>
darienf 20:6d2af70c92ab 118 public void Clear()
darienf 20:6d2af70c92ab 119 {
darienf 20:6d2af70c92ab 120 _chart.Series[_series1Name].Points.Clear();
darienf 20:6d2af70c92ab 121 _chart.Series[_series2Name].Points.Clear();
darienf 20:6d2af70c92ab 122 _chart.Series[_series3Name].Points.Clear();
darienf 20:6d2af70c92ab 123 // VERIFY: OS24EVK-75 replace _xCount with _xCount1, _xCount2, _xCount3
darienf 20:6d2af70c92ab 124 //_xCount = 0;
darienf 20:6d2af70c92ab 125 _xCount1 = 0;
darienf 20:6d2af70c92ab 126 _xCount2 = 0;
darienf 20:6d2af70c92ab 127 _xCount3 = 0;
darienf 20:6d2af70c92ab 128 _plotPointsToSkip = 0;
darienf 20:6d2af70c92ab 129 // https://jira.maxim-ic.com/browse/OS24EVK-32 replace numPlotTime with menu item Options | Plot Time
darienf 20:6d2af70c92ab 130 _plotPoints = (int)_plotWindowTime * _SampleRate_Hz / SampleAverage_n;
darienf 20:6d2af70c92ab 131 if (_plotPoints > _maxPlotPoints)
darienf 20:6d2af70c92ab 132 {
darienf 20:6d2af70c92ab 133 _plotPointsToSkip = _plotPoints / _maxPlotPoints - 1;
darienf 20:6d2af70c92ab 134 _plotPoints = _maxPlotPoints;
darienf 20:6d2af70c92ab 135 }
darienf 20:6d2af70c92ab 136 }
darienf 20:6d2af70c92ab 137
darienf 20:6d2af70c92ab 138 /// <summary>
darienf 20:6d2af70c92ab 139 /// Replacement for plotXAxisNoLabelsToolStripMenuItem.Checked
darienf 20:6d2af70c92ab 140 /// </summary>
darienf 20:6d2af70c92ab 141 public bool plotXAxisNoLabels {
darienf 20:6d2af70c92ab 142 get { return _plotXAxisNoLabels; }
darienf 20:6d2af70c92ab 143 set
darienf 20:6d2af70c92ab 144 {
darienf 20:6d2af70c92ab 145 _plotXAxisNoLabels = value;
darienf 20:6d2af70c92ab 146 _chart.ChartAreas[_chartArea1Name].AxisX.LabelStyle.Enabled = !_plotXAxisNoLabels;
darienf 20:6d2af70c92ab 147 _chart.ChartAreas[_chartArea2Name].AxisX.LabelStyle.Enabled = !_plotXAxisNoLabels;
darienf 20:6d2af70c92ab 148 _chart.ChartAreas[_chartArea3Name].AxisX.LabelStyle.Enabled = !_plotXAxisNoLabels;
darienf 20:6d2af70c92ab 149 }
darienf 20:6d2af70c92ab 150 }
darienf 20:6d2af70c92ab 151 private bool _plotXAxisNoLabels = true; // VERIFY: default plotXAxisNoLabelsToolStripMenuItem.Checked
darienf 20:6d2af70c92ab 152
darienf 20:6d2af70c92ab 153 /// <summary>
darienf 20:6d2af70c92ab 154 /// Replacement for plotXAxisTimeToolStripMenuItem.Checked
darienf 20:6d2af70c92ab 155 /// </summary>
darienf 20:6d2af70c92ab 156 public bool plotXAxisTime
darienf 20:6d2af70c92ab 157 {
darienf 20:6d2af70c92ab 158 get { return _plotXAxisTime; }
darienf 20:6d2af70c92ab 159 set
darienf 20:6d2af70c92ab 160 {
darienf 20:6d2af70c92ab 161 _plotXAxisTime = value;
darienf 20:6d2af70c92ab 162 }
darienf 20:6d2af70c92ab 163 }
darienf 20:6d2af70c92ab 164 private bool _plotXAxisTime = true; // VERIFY: default plotXAxisTimeToolStripMenuItem.Checked
darienf 20:6d2af70c92ab 165
darienf 20:6d2af70c92ab 166 private int _SampleRate_Hz = 100;
darienf 20:6d2af70c92ab 167 /// <summary>
darienf 20:6d2af70c92ab 168 /// Sample rate. Replacement for int.Parse(cboSampleRate.Text)
darienf 20:6d2af70c92ab 169 /// </summary>
darienf 20:6d2af70c92ab 170 public int SampleRate_Hz
darienf 20:6d2af70c92ab 171 {
darienf 20:6d2af70c92ab 172 get { return _SampleRate_Hz; }
darienf 20:6d2af70c92ab 173 set
darienf 20:6d2af70c92ab 174 {
darienf 20:6d2af70c92ab 175 _SampleRate_Hz = value;
darienf 20:6d2af70c92ab 176
darienf 20:6d2af70c92ab 177 //_AutoScaleEveryNSamples = 4 * _SampleRate_Hz; // 4 seconds assuming sample rate int.Parse(cboSampleRate.Text) == 100 samples per second
darienf 20:6d2af70c92ab 178
darienf 20:6d2af70c92ab 179 // VERIFY: OS24EVK-73 Sample Avg 2 breaks chart scrolling
darienf 20:6d2af70c92ab 180 // update _plotPoints which depends on myMAX30101.SampleAverage_n
darienf 20:6d2af70c92ab 181 _plotPoints = (int)_plotWindowTime * _SampleRate_Hz / SampleAverage_n;
darienf 20:6d2af70c92ab 182 if (_plotPoints > _maxPlotPoints)
darienf 20:6d2af70c92ab 183 {
darienf 20:6d2af70c92ab 184 _plotPointsToSkip = _plotPoints / _maxPlotPoints - 1;
darienf 20:6d2af70c92ab 185 _plotPoints = _maxPlotPoints;
darienf 20:6d2af70c92ab 186 }
darienf 20:6d2af70c92ab 187 }
darienf 20:6d2af70c92ab 188 }
darienf 20:6d2af70c92ab 189
darienf 20:6d2af70c92ab 190 private int _SampleAverage_n = 1;
darienf 20:6d2af70c92ab 191 /// <summary>
darienf 20:6d2af70c92ab 192 /// Replacement for myMAX30101.SampleAverage_n
darienf 20:6d2af70c92ab 193 /// </summary>
darienf 20:6d2af70c92ab 194 public int SampleAverage_n
darienf 20:6d2af70c92ab 195 {
darienf 20:6d2af70c92ab 196 get { return _SampleAverage_n; }
darienf 20:6d2af70c92ab 197 set
darienf 20:6d2af70c92ab 198 {
darienf 20:6d2af70c92ab 199 _SampleAverage_n = value;
darienf 20:6d2af70c92ab 200 // VERIFY: OS24EVK-73 Sample Avg 2 breaks chart scrolling
darienf 20:6d2af70c92ab 201 // update _plotPoints which depends on myMAX30101.SampleAverage_n
darienf 20:6d2af70c92ab 202 _plotPoints = (int)_plotWindowTime * _SampleRate_Hz / SampleAverage_n;
darienf 20:6d2af70c92ab 203 if (_plotPoints > _maxPlotPoints)
darienf 20:6d2af70c92ab 204 {
darienf 20:6d2af70c92ab 205 _plotPointsToSkip = _plotPoints / _maxPlotPoints - 1;
darienf 20:6d2af70c92ab 206 _plotPoints = _maxPlotPoints;
darienf 20:6d2af70c92ab 207 }
darienf 20:6d2af70c92ab 208 }
darienf 20:6d2af70c92ab 209 }
darienf 20:6d2af70c92ab 210
darienf 20:6d2af70c92ab 211 // VERIFY: IMPLEMENT OS24EVK-70 Maxim.MAX30101.MedicalChartHelper._xCount int instead of double?
darienf 20:6d2af70c92ab 212 // VERIFY: OS24EVK-75 replace _xCount with _xCount1, _xCount2, _xCount3
darienf 20:6d2af70c92ab 213 //public double _xCount = 0;
darienf 20:6d2af70c92ab 214 public int _xCount1 = 0;
darienf 20:6d2af70c92ab 215 public int _xCount2 = 0;
darienf 20:6d2af70c92ab 216 public int _xCount3 = 0;
darienf 20:6d2af70c92ab 217
darienf 20:6d2af70c92ab 218 /// <summary>
darienf 20:6d2af70c92ab 219 /// The actual number of points on the plot (&lt;= _maxPlotPoints). The actual number of points may be less than _maxPlotPoints in order to maintain ~numPlotTime.Value of plotted data
darienf 20:6d2af70c92ab 220 /// </summary>
darienf 20:6d2af70c92ab 221 public int _plotPoints = 500;
darienf 20:6d2af70c92ab 222
darienf 20:6d2af70c92ab 223 private int _plotWindowTime = 5; // plot window in seconds
darienf 20:6d2af70c92ab 224 public int plotWindowTime
darienf 20:6d2af70c92ab 225 {
darienf 20:6d2af70c92ab 226 get
darienf 20:6d2af70c92ab 227 {
darienf 20:6d2af70c92ab 228 return _plotWindowTime;
darienf 20:6d2af70c92ab 229 }
darienf 20:6d2af70c92ab 230 set
darienf 20:6d2af70c92ab 231 {
darienf 20:6d2af70c92ab 232 _plotWindowTime = value;
darienf 20:6d2af70c92ab 233 _plotPoints = (int)_plotWindowTime * _SampleRate_Hz / SampleAverage_n;
darienf 20:6d2af70c92ab 234 if (_plotPoints > _maxPlotPoints)
darienf 20:6d2af70c92ab 235 {
darienf 20:6d2af70c92ab 236 _plotPointsToSkip = _plotPoints / _maxPlotPoints - 1;
darienf 20:6d2af70c92ab 237 _plotPoints = _maxPlotPoints;
darienf 20:6d2af70c92ab 238 }
darienf 20:6d2af70c92ab 239 }
darienf 20:6d2af70c92ab 240 }
darienf 20:6d2af70c92ab 241
darienf 20:6d2af70c92ab 242 private int _maxPlotPoints = 2000; // maximum allowed number of points that can be plotted per series. Higher Fs will have more than 500 pts for a given numPlotTime.Value, so the number of samples per point will need to be increased accordingly to give maximum _maxPlotPoints
darienf 20:6d2af70c92ab 243
darienf 20:6d2af70c92ab 244 public int _plotPointsToSkip = 0;
darienf 20:6d2af70c92ab 245
darienf 20:6d2af70c92ab 246 // https://jira.maxim-ic.com/browse/OS24EVK-34 Autoscale tuning parameters
darienf 20:6d2af70c92ab 247 // https://jira.maxim-ic.com/browse/OS24EVK-34 Change _AutoScaleTimeInterval to _AutoScaleEveryNSamples and use sampleNumber to measure time
darienf 20:6d2af70c92ab 248 // https://jira.maxim-ic.com/browse/OS24EVK-42 Autoscale tuning (Larry 2014-11-24) _AutoScaleEveryNSamples
darienf 20:6d2af70c92ab 249 // - Slow the Autoscale update rate every 4 seconds
darienf 20:6d2af70c92ab 250 //private int _AutoScaleEveryNSamples = 400; // 4 seconds assuming sample rate int.Parse(cboSampleRate.Text) == 100 samples per second
darienf 20:6d2af70c92ab 251
darienf 20:6d2af70c92ab 252 // private double _AutoScaleMarginXLeft; // ignore old data at left of graph
darienf 20:6d2af70c92ab 253 // private double _AutoScaleMarginXRight; // ignore new data at right of graph? probably 0 by default
darienf 20:6d2af70c92ab 254
darienf 20:6d2af70c92ab 255 /// <summary>
darienf 20:6d2af70c92ab 256 /// chartMax empty area above dataMax
darienf 20:6d2af70c92ab 257 /// </summary>
darienf 20:6d2af70c92ab 258 private static double _AutoScaleMarginYTop = 0.125;
darienf 20:6d2af70c92ab 259
darienf 20:6d2af70c92ab 260 /// <summary>
darienf 20:6d2af70c92ab 261 /// chartMin empty area below dataMin
darienf 20:6d2af70c92ab 262 /// </summary>
darienf 20:6d2af70c92ab 263 private static double _AutoScaleMarginYBottom = 0.125;
darienf 20:6d2af70c92ab 264
darienf 20:6d2af70c92ab 265 /// <summary>
darienf 20:6d2af70c92ab 266 /// Minimum Y span: allow up to 10 counts per division (i.e. 50 LSBs)
darienf 20:6d2af70c92ab 267 ///
darienf 20:6d2af70c92ab 268 /// (5 * _AutoScaleYmultiples) minimum allowed span of chartMax-chartMin,
darienf 20:6d2af70c92ab 269 /// to avoid focusing on LSB noise
darienf 20:6d2af70c92ab 270 /// </summary>
darienf 20:6d2af70c92ab 271 private static double _AutoScaleMinSpanY = 500;
darienf 20:6d2af70c92ab 272
darienf 20:6d2af70c92ab 273 /// <summary>
darienf 20:6d2af70c92ab 274 /// Y axis: No decimals. Round to nearest multiple of 100.
darienf 20:6d2af70c92ab 275 /// </summary>
darienf 20:6d2af70c92ab 276 private static double _AutoScaleYmultiples = 100;
darienf 20:6d2af70c92ab 277
darienf 20:6d2af70c92ab 278 // Calculated initial default chart limits _AutoscaleInitialChartMaxY .. _AutoscaleInitialChartMinY
darienf 20:6d2af70c92ab 279 int _AutoscaleInitialChartMaxY =
darienf 20:6d2af70c92ab 280 (
darienf 20:6d2af70c92ab 281 (int)
darienf 20:6d2af70c92ab 282 ((_AutoScaleMinSpanY * (1.0 + _AutoScaleMarginYTop + _AutoScaleMarginYBottom)) / _AutoScaleYmultiples)
darienf 20:6d2af70c92ab 283 )
darienf 20:6d2af70c92ab 284 * (int)(_AutoScaleYmultiples);
darienf 20:6d2af70c92ab 285 int _AutoscaleInitialChartMinY = 0;
darienf 20:6d2af70c92ab 286
darienf 20:6d2af70c92ab 287 /// <summary>
darienf 20:6d2af70c92ab 288 /// Append data to chart area 1 series 1 (Optical: Red. Accelerometer: X)
darienf 20:6d2af70c92ab 289 ///
darienf 20:6d2af70c92ab 290 /// @post _xCount1 is updated
darienf 20:6d2af70c92ab 291 /// </summary>
darienf 20:6d2af70c92ab 292 /// <param name="rawIntArrayYData"></param>
darienf 20:6d2af70c92ab 293 /// <param name="firstNewXIndex"></param>
darienf 20:6d2af70c92ab 294 /// <param name="lastXIndex"></param>
darienf 20:6d2af70c92ab 295 public void AppendDataChartArea1(int[] rawIntArrayYData, int firstNewXIndex, int lastXIndex)
darienf 20:6d2af70c92ab 296 {
darienf 20:6d2af70c92ab 297 // AppendDataChartArea(_chartArea1Name, _series1Name, rawIntArrayYData, firstNewXIndex, lastXIndex);
darienf 20:6d2af70c92ab 298 string chartAreaName = _chartArea1Name;
darienf 20:6d2af70c92ab 299 string seriesName = _series1Name;
darienf 20:6d2af70c92ab 300
darienf 20:6d2af70c92ab 301 for (int index = firstNewXIndex; index <= lastXIndex; index++)
darienf 20:6d2af70c92ab 302 {
darienf 20:6d2af70c92ab 303 int yData = rawIntArrayYData[index];
darienf 20:6d2af70c92ab 304 // VERIFY: OS24EVK-75 replace AutoScaleEvaluate dataFormatIs16bit2sComplement with if (DataFormat == DataFormats.Format16bit2sComplement)
darienf 20:6d2af70c92ab 305 if (DataFormat == DataFormats.Format16bit2sComplement)
darienf 20:6d2af70c92ab 306 {
darienf 20:6d2af70c92ab 307 // VERIFY: OS24EVK-57 interpret rawX rawY rawZ as 16-bit 2's complement
darienf 20:6d2af70c92ab 308 if (yData > 0x8000)
darienf 20:6d2af70c92ab 309 {
darienf 20:6d2af70c92ab 310 yData = yData - 0x10000;
darienf 20:6d2af70c92ab 311 }
darienf 20:6d2af70c92ab 312 }
darienf 20:6d2af70c92ab 313 int count = _chart.Series[seriesName].Points.Count;
darienf 20:6d2af70c92ab 314 // VERIFY: OS24EVK-75 replace _xCount with _xCount1, _xCount2, _xCount3
darienf 20:6d2af70c92ab 315 double xCoord = _xCount1 * (_plotPointsToSkip + 1) * SampleAverage_n / (plotXAxisTime ? SampleRate_Hz : 1);
darienf 20:6d2af70c92ab 316
darienf 20:6d2af70c92ab 317 //_chart.Series[seriesName].Points.AddXY(xCoord, yData);
darienf 20:6d2af70c92ab 318 _chart.Series[seriesName].Points.AddY(yData);
darienf 20:6d2af70c92ab 319 _chart.ResetAutoValues();
darienf 20:6d2af70c92ab 320
darienf 20:6d2af70c92ab 321 while (count > _plotPoints)
darienf 20:6d2af70c92ab 322 {
darienf 20:6d2af70c92ab 323 _chart.Series[seriesName].Points.RemoveAt(0);
darienf 20:6d2af70c92ab 324 count = _chart.Series[seriesName].Points.Count;
darienf 20:6d2af70c92ab 325 }
darienf 20:6d2af70c92ab 326 // VERIFY: OS24EVK-75 replace _xCount with _xCount1, _xCount2, _xCount3
darienf 20:6d2af70c92ab 327 _xCount1++;
darienf 20:6d2af70c92ab 328 }
darienf 20:6d2af70c92ab 329 //AutoScaleEvaluate(e.rawRedData, e.sampleNumberOffset, numSamples - 1, _chart, chartAreaName);
darienf 20:6d2af70c92ab 330 }
darienf 20:6d2af70c92ab 331
darienf 20:6d2af70c92ab 332 /// <summary>
darienf 20:6d2af70c92ab 333 /// Append data to chart area 2 series 2 (Optical: IR. Accelerometer: Y)
darienf 20:6d2af70c92ab 334 ///
darienf 20:6d2af70c92ab 335 /// @post _xCount2 is updated
darienf 20:6d2af70c92ab 336 /// </summary>
darienf 20:6d2af70c92ab 337 /// <param name="rawIntArrayYData"></param>
darienf 20:6d2af70c92ab 338 /// <param name="firstNewXIndex"></param>
darienf 20:6d2af70c92ab 339 /// <param name="lastXIndex"></param>
darienf 20:6d2af70c92ab 340 public void AppendDataChartArea2(int[] rawIntArrayYData, int firstNewXIndex, int lastXIndex)
darienf 20:6d2af70c92ab 341 {
darienf 20:6d2af70c92ab 342 // AppendDataChartArea(_chartArea2Name, _series2Name, rawIntArrayYData, firstNewXIndex, lastXIndex);
darienf 20:6d2af70c92ab 343 string chartAreaName = _chartArea2Name;
darienf 20:6d2af70c92ab 344 string seriesName = _series2Name;
darienf 20:6d2af70c92ab 345
darienf 20:6d2af70c92ab 346 for (int index = firstNewXIndex; index <= lastXIndex; index++)
darienf 20:6d2af70c92ab 347 {
darienf 20:6d2af70c92ab 348 int yData = rawIntArrayYData[index];
darienf 20:6d2af70c92ab 349 // VERIFY: OS24EVK-75 replace AutoScaleEvaluate dataFormatIs16bit2sComplement with if (DataFormat == DataFormats.Format16bit2sComplement)
darienf 20:6d2af70c92ab 350 if (DataFormat == DataFormats.Format16bit2sComplement)
darienf 20:6d2af70c92ab 351 {
darienf 20:6d2af70c92ab 352 // VERIFY: OS24EVK-57 interpret rawX rawY rawZ as 16-bit 2's complement
darienf 20:6d2af70c92ab 353 if (yData > 0x8000)
darienf 20:6d2af70c92ab 354 {
darienf 20:6d2af70c92ab 355 yData = yData - 0x10000;
darienf 20:6d2af70c92ab 356 }
darienf 20:6d2af70c92ab 357 }
darienf 20:6d2af70c92ab 358 int count = _chart.Series[seriesName].Points.Count;
darienf 20:6d2af70c92ab 359 // VERIFY: OS24EVK-75 replace _xCount with _xCount1, _xCount2, _xCount3
darienf 20:6d2af70c92ab 360 double xCoord = _xCount2 * (_plotPointsToSkip + 1) * SampleAverage_n / (plotXAxisTime ? SampleRate_Hz : 1);
darienf 20:6d2af70c92ab 361
darienf 20:6d2af70c92ab 362 _chart.Series[seriesName].Points.AddXY(xCoord, yData);
darienf 20:6d2af70c92ab 363 _chart.ResetAutoValues();
darienf 20:6d2af70c92ab 364
darienf 20:6d2af70c92ab 365 while (count > _plotPoints)
darienf 20:6d2af70c92ab 366 {
darienf 20:6d2af70c92ab 367 _chart.Series[seriesName].Points.RemoveAt(0);
darienf 20:6d2af70c92ab 368 count = _chart.Series[seriesName].Points.Count;
darienf 20:6d2af70c92ab 369 }
darienf 20:6d2af70c92ab 370 // VERIFY: OS24EVK-75 replace _xCount with _xCount1, _xCount2, _xCount3
darienf 20:6d2af70c92ab 371 _xCount2++;
darienf 20:6d2af70c92ab 372 }
darienf 20:6d2af70c92ab 373 //AutoScaleEvaluate(e.rawRedData, e.sampleNumberOffset, numSamples - 1, _chart, chartAreaName);
darienf 20:6d2af70c92ab 374 }
darienf 20:6d2af70c92ab 375
darienf 20:6d2af70c92ab 376 /// <summary>
darienf 20:6d2af70c92ab 377 /// Append data to chart area 3 series 3 (Optical: Green. Accelerometer: Z)
darienf 20:6d2af70c92ab 378 ///
darienf 20:6d2af70c92ab 379 /// @post _xCount3 is updated
darienf 20:6d2af70c92ab 380 /// </summary>
darienf 20:6d2af70c92ab 381 /// <param name="rawIntArrayYData"></param>
darienf 20:6d2af70c92ab 382 /// <param name="firstNewXIndex"></param>
darienf 20:6d2af70c92ab 383 /// <param name="lastXIndex"></param>
darienf 20:6d2af70c92ab 384 public void AppendDataChartArea3(int[] rawIntArrayYData, int firstNewXIndex, int lastXIndex)
darienf 20:6d2af70c92ab 385 {
darienf 20:6d2af70c92ab 386 // AppendDataChartArea(_chartArea3Name, _series3Name, rawIntArrayYData, firstNewXIndex, lastXIndex);
darienf 20:6d2af70c92ab 387 string chartAreaName = _chartArea3Name;
darienf 20:6d2af70c92ab 388 string seriesName = _series3Name;
darienf 20:6d2af70c92ab 389
darienf 20:6d2af70c92ab 390 for (int index = firstNewXIndex; index <= lastXIndex; index++)
darienf 20:6d2af70c92ab 391 {
darienf 20:6d2af70c92ab 392 int yData = rawIntArrayYData[index];
darienf 20:6d2af70c92ab 393 // VERIFY: OS24EVK-75 replace AutoScaleEvaluate dataFormatIs16bit2sComplement with if (DataFormat == DataFormats.Format16bit2sComplement)
darienf 20:6d2af70c92ab 394 if (DataFormat == DataFormats.Format16bit2sComplement)
darienf 20:6d2af70c92ab 395 {
darienf 20:6d2af70c92ab 396 // VERIFY: OS24EVK-57 interpret rawX rawY rawZ as 16-bit 2's complement
darienf 20:6d2af70c92ab 397 if (yData > 0x8000)
darienf 20:6d2af70c92ab 398 {
darienf 20:6d2af70c92ab 399 yData = yData - 0x10000;
darienf 20:6d2af70c92ab 400 }
darienf 20:6d2af70c92ab 401 }
darienf 20:6d2af70c92ab 402 int count = _chart.Series[seriesName].Points.Count;
darienf 20:6d2af70c92ab 403 // VERIFY: OS24EVK-75 replace _xCount with _xCount1, _xCount2, _xCount3
darienf 20:6d2af70c92ab 404 double xCoord = _xCount3 * (_plotPointsToSkip + 1) * SampleAverage_n / (plotXAxisTime ? SampleRate_Hz : 1);
darienf 20:6d2af70c92ab 405
darienf 20:6d2af70c92ab 406 _chart.Series[seriesName].Points.AddXY(xCoord, yData);
darienf 20:6d2af70c92ab 407 _chart.ResetAutoValues();
darienf 20:6d2af70c92ab 408
darienf 20:6d2af70c92ab 409 while (count > _plotPoints)
darienf 20:6d2af70c92ab 410 {
darienf 20:6d2af70c92ab 411 _chart.Series[seriesName].Points.RemoveAt(0);
darienf 20:6d2af70c92ab 412 count = _chart.Series[seriesName].Points.Count;
darienf 20:6d2af70c92ab 413 }
darienf 20:6d2af70c92ab 414 // VERIFY: OS24EVK-75 replace _xCount with _xCount1, _xCount2, _xCount3
darienf 20:6d2af70c92ab 415 _xCount3++;
darienf 20:6d2af70c92ab 416 }
darienf 20:6d2af70c92ab 417 //AutoScaleEvaluate(e.rawRedData, e.sampleNumberOffset, numSamples - 1, _chart, chartAreaName);
darienf 20:6d2af70c92ab 418 }
darienf 20:6d2af70c92ab 419
darienf 20:6d2af70c92ab 420 #if PROFILER
darienf 20:6d2af70c92ab 421 //// TODO1: OS24EVK-54 profiling: capture max interval, number of intervals, cumulative interval
darienf 20:6d2af70c92ab 422 System.Diagnostics.Stopwatch _profilingStopwatchAutoScaleEvaluate = new System.Diagnostics.Stopwatch(); // cumulative timing
darienf 20:6d2af70c92ab 423 int _profilingStopwatchAutoScaleEvaluate_NumIntervals;
darienf 20:6d2af70c92ab 424 #endif // PROFILER
darienf 20:6d2af70c92ab 425
darienf 20:6d2af70c92ab 426 public void AutoScaleEvaluate1(int[] rawIntArrayYData, int firstNewXIndex, int lastXIndex)
darienf 20:6d2af70c92ab 427 {
darienf 20:6d2af70c92ab 428 AutoScaleEvaluate(rawIntArrayYData, firstNewXIndex, lastXIndex, _chartArea1Name);
darienf 20:6d2af70c92ab 429 }
darienf 20:6d2af70c92ab 430
darienf 20:6d2af70c92ab 431 public void AutoScaleEvaluate2(int[] rawIntArrayYData, int firstNewXIndex, int lastXIndex)
darienf 20:6d2af70c92ab 432 {
darienf 20:6d2af70c92ab 433 AutoScaleEvaluate(rawIntArrayYData, firstNewXIndex, lastXIndex, _chartArea2Name);
darienf 20:6d2af70c92ab 434 }
darienf 20:6d2af70c92ab 435
darienf 20:6d2af70c92ab 436 public void AutoScaleEvaluate3(int[] rawIntArrayYData, int firstNewXIndex, int lastXIndex)
darienf 20:6d2af70c92ab 437 {
darienf 20:6d2af70c92ab 438 AutoScaleEvaluate(rawIntArrayYData, firstNewXIndex, lastXIndex, _chartArea3Name);
darienf 20:6d2af70c92ab 439 }
darienf 20:6d2af70c92ab 440
darienf 20:6d2af70c92ab 441 // TODO1: OS24EVK-57 OS24EVK-54 AutoScaleEvaluate(int[] rawIntArrayYData,...) Calculate autoscale min/max/SampleVariance from the raw integer data. This should be much faster as it doesn't involve arbitrarily converting into floating-point numbers or excessive indexing.
darienf 20:6d2af70c92ab 442 public void AutoScaleEvaluate(int[] rawIntArrayYData,
darienf 20:6d2af70c92ab 443 int firstNewXIndex,
darienf 20:6d2af70c92ab 444 int lastXIndex,
darienf 20:6d2af70c92ab 445 /*Chart chart,*/ string chartAreaName
darienf 20:6d2af70c92ab 446 // TODO1: OS24EVK-75 replace AutoScaleEvaluate dataFormatIs16bit2sComplement with if (DataFormat == DataFormats.Format16bit2sComplement)
darienf 20:6d2af70c92ab 447 //bool dataFormatIs16bit2sComplement = false
darienf 20:6d2af70c92ab 448 //string seriesName,
darienf 20:6d2af70c92ab 449 //int sampleNumber,
darienf 20:6d2af70c92ab 450 //ref int sampleNumberPreviousAutoscale
darienf 20:6d2af70c92ab 451 )
darienf 20:6d2af70c92ab 452 {
darienf 20:6d2af70c92ab 453 #if PROFILER
darienf 20:6d2af70c92ab 454 // TODO1: OS24EVK-54 profiling: capture max interval, number of intervals, cumulative interval
darienf 20:6d2af70c92ab 455 _profilingStopwatchAutoScaleEvaluate.Start();
darienf 20:6d2af70c92ab 456 _profilingStopwatchAutoScaleEvaluate_NumIntervals = _profilingStopwatchAutoScaleEvaluate_NumIntervals + 1;
darienf 20:6d2af70c92ab 457 #endif // PROFILER
darienf 20:6d2af70c92ab 458
darienf 20:6d2af70c92ab 459 // TODO1: OS24EVK-57 OS24EVK-54 Calculate autoscale min/max/SampleVariance from the raw integer data. This should be much faster as it doesn't involve arbitrarily converting into floating-point numbers or excessive indexing.
darienf 20:6d2af70c92ab 460
darienf 20:6d2af70c92ab 461 // https://jira.maxim-ic.com/browse/OS24EVK-34 AutoScaleEvaluate() use _AutoScaleMarginXLeft; // ignore old data at left of graph
darienf 20:6d2af70c92ab 462 // https://jira.maxim-ic.com/browse/OS24EVK-34 AutoScaleEvaluate() use _AutoScaleMarginXRight; // ignore new data at right of graph? probably 0 by default
darienf 20:6d2af70c92ab 463 //const int firstPointIndex = 0;
darienf 20:6d2af70c92ab 464 // advance the left and right X limits
darienf 20:6d2af70c92ab 465 // https://jira.maxim-ic.com/browse/OS24EVK-48 Graph X axis - Replace hidden chkPlotTime with a menu option plotXAxisTimeToolStripMenuItem.Checked
darienf 20:6d2af70c92ab 466 // https://jira.maxim-ic.com/browse/OS24EVK-47 Graph scrolling needs to be smooth progression like a paper tape chart even when (plotXAxisTimeToolStripMenuItem.Checked is true)
darienf 20:6d2af70c92ab 467 // per Larry Skrenes 2014-11-24: graph scrolling needs to be smooth progression like a paper tape chart.
darienf 20:6d2af70c92ab 468 // When (plotXAxisTimeToolStripMenuItem.Checked is false) graph scrolling is already smooth.
darienf 20:6d2af70c92ab 469 // When (plotXAxisTimeToolStripMenuItem.Checked is true) graph scrolling moves in staccato 1-second steps. Larry hates this.
darienf 20:6d2af70c92ab 470 int xWidth = _plotPoints * (_plotPointsToSkip + 1) * SampleAverage_n / (plotXAxisTime ? SampleRate_Hz : 1);
darienf 20:6d2af70c92ab 471 // VERIFY: OS24EVK-73 Sample Avg 2 breaks chart scrolling -- xLastXIndex = lastXIndex * myMAX30101.SampleAverage_n
darienf 20:6d2af70c92ab 472 int xLastXIndex = lastXIndex * SampleAverage_n / (plotXAxisTime ? SampleRate_Hz : 1);
darienf 20:6d2af70c92ab 473 //int xWidth = _plotPoints * (_plotPointsToSkip + 1) * _cboSampleAvg / (false ? int.Parse(cboSampleRate.Text) : 1);
darienf 20:6d2af70c92ab 474 //int xWidth = _plotPoints * (_plotPointsToSkip + 1) * _cboSampleAvg / (1);
darienf 20:6d2af70c92ab 475 //int xWidth = _plotPoints * (_plotPointsToSkip + 1) * _cboSampleAvg;
darienf 20:6d2af70c92ab 476 // https://jira.maxim-ic.com/browse/OS24EVK-15 Fix plot X axis jumping around (_plotWindowTime)
darienf 20:6d2af70c92ab 477 // 0.0 --> 0 1 2 3 4 5
darienf 20:6d2af70c92ab 478 // 0.0 --> 0_ 1 2 3 4 5
darienf 20:6d2af70c92ab 479 // 0.0 --> 0__1 2 3 4 5
darienf 20:6d2af70c92ab 480 // 0.0 --> 0__1_ 2 3 4 5
darienf 20:6d2af70c92ab 481 // 0.0 --> 0__1__2 3 4 5
darienf 20:6d2af70c92ab 482 // 0.0 --> 0__1__2_ 3 4 5
darienf 20:6d2af70c92ab 483 // 0.0 --> 0__1__2__3 4 5
darienf 20:6d2af70c92ab 484 // 0.0 --> 0__1__2__3_ 4 5
darienf 20:6d2af70c92ab 485 // 0.0 --> 0__1__2__3__4 5
darienf 20:6d2af70c92ab 486 // 0.0 --> 0__1__2__3__4_ 5
darienf 20:6d2af70c92ab 487 // 0.0 --> 0__1__2__3__4__5
darienf 20:6d2af70c92ab 488 // 0.5 --> 1__2__3__4__5_ 6
darienf 20:6d2af70c92ab 489 // 1.0 --> 1__2__3__4__5__6
darienf 20:6d2af70c92ab 490 // 1.5 --> 2__3__4__5__6_ 7
darienf 20:6d2af70c92ab 491 // 2.0 --> 2__3__4__5__6__7
darienf 20:6d2af70c92ab 492 // https://jira.maxim-ic.com/browse/OS24EVK-47 Graph scrolling needs to be smooth progression like a paper tape chart even when (plotXAxisTimeToolStripMenuItem.Checked is true)
darienf 20:6d2af70c92ab 493 // per Larry Skrenes 2014-11-24: graph scrolling needs to be smooth progression like a paper tape chart.
darienf 20:6d2af70c92ab 494 // When (plotXAxisTimeToolStripMenuItem.Checked is false) graph scrolling is already smooth.
darienf 20:6d2af70c92ab 495 // When (plotXAxisTimeToolStripMenuItem.Checked is true) graph scrolling moves in staccato 1-second steps. Larry hates this.
darienf 20:6d2af70c92ab 496 // VERIFY: OS24EVK-76 Autoscale delay time is affected by Sample Average - firstVisiblePointIndex
darienf 20:6d2af70c92ab 497 //int firstVisiblePointIndex = lastXIndex - (_plotPoints * (_plotPointsToSkip + 1) * SampleAverage_n);
darienf 20:6d2af70c92ab 498 int firstVisiblePointIndex = lastXIndex - (_plotPoints * (_plotPointsToSkip + 1));
darienf 20:6d2af70c92ab 499 if (firstVisiblePointIndex < 0) { firstVisiblePointIndex = 0; }
darienf 20:6d2af70c92ab 500 //
darienf 20:6d2af70c92ab 501 // VERIFY: OS24EVK-73 Sample Avg 2 breaks chart scrolling -- xLastXIndex instead of lastXIndex
darienf 20:6d2af70c92ab 502 int firstVisiblePointX = xLastXIndex - xWidth; // (int)chart.Series[0].Points[firstPointIndex].XValue;
darienf 20:6d2af70c92ab 503 if (firstVisiblePointX < 0) { firstVisiblePointX = 0; }
darienf 20:6d2af70c92ab 504 //int firstPointX = (int)System.Math.Ceiling(chart.Series[seriesName].Points[firstPointIndex].XValue);
darienf 20:6d2af70c92ab 505 //if ((chart.Series[seriesName].Points[firstPointIndex].XValue) < 0.1)
darienf 20:6d2af70c92ab 506 //{
darienf 20:6d2af70c92ab 507 // firstPointX = (int)System.Math.Floor(chart.Series[seriesName].Points[firstPointIndex].XValue);
darienf 20:6d2af70c92ab 508 //}
darienf 20:6d2af70c92ab 509 _chart.ChartAreas[chartAreaName].AxisX.Minimum = firstVisiblePointX;
darienf 20:6d2af70c92ab 510 // https://jira.maxim-ic.com/browse/OS24EVK-47 Graph scrolling needs to be smooth progression like a paper tape chart even when (plotXAxisTimeToolStripMenuItem.Checked is true)
darienf 20:6d2af70c92ab 511 //chart.ChartAreas[0].AxisX.Maximum = (int)System.Math.Ceiling((double)firstPointX + xWidth);
darienf 20:6d2af70c92ab 512 _chart.ChartAreas[chartAreaName].AxisX.Maximum = firstVisiblePointX + xWidth;
darienf 20:6d2af70c92ab 513
darienf 20:6d2af70c92ab 514 if (lastXIndex /* chart.Series[seriesName].Points.Count */ < 1)
darienf 20:6d2af70c92ab 515 {
darienf 20:6d2af70c92ab 516 //int iMaxY_chart_default = (_AutoScaleMinSpanY * (1.0 + _AutoScaleMarginYTop + _AutoScaleMarginYBottom)) / _AutoScaleYmultiples;
darienf 20:6d2af70c92ab 517 //int _AutoscaleInitialChartMaxY = iMaxY_chart_default * _AutoScaleYmultiples;
darienf 20:6d2af70c92ab 518 //int _AutoscaleInitialChartMaxY =
darienf 20:6d2af70c92ab 519 // (
darienf 20:6d2af70c92ab 520 // (int)
darienf 20:6d2af70c92ab 521 // ((_AutoScaleMinSpanY * (1.0 + _AutoScaleMarginYTop + _AutoScaleMarginYBottom)) / _AutoScaleYmultiples)
darienf 20:6d2af70c92ab 522 // )
darienf 20:6d2af70c92ab 523 // * (int)(_AutoScaleYmultiples);
darienf 20:6d2af70c92ab 524 //int _AutoscaleInitialChartMinY = 0;
darienf 20:6d2af70c92ab 525 _chart.ChartAreas[chartAreaName].AxisY.Maximum = _AutoscaleInitialChartMaxY;
darienf 20:6d2af70c92ab 526 _chart.ChartAreas[chartAreaName].AxisY.Minimum = _AutoscaleInitialChartMinY;
darienf 20:6d2af70c92ab 527 #if PROFILER
darienf 20:6d2af70c92ab 528 // TODO1: OS24EVK-54 profiling: capture max interval, number of intervals, cumulative interval
darienf 20:6d2af70c92ab 529 _profilingStopwatchAutoScaleEvaluate.Stop();
darienf 20:6d2af70c92ab 530 #endif // PROFILER
darienf 20:6d2af70c92ab 531 return;
darienf 20:6d2af70c92ab 532 }
darienf 20:6d2af70c92ab 533
darienf 20:6d2af70c92ab 534 // // TODO1: OS24EVK-57 OS24EVK-54 scan new data rawIntArrayYData[firstNewIndex .. lastIndex]. As is this code only triggers if the last point is extreme.
darienf 20:6d2af70c92ab 535 // int newestPointIndex = lastXIndex; // chart.Series[seriesName].Points.Count - 1;
darienf 20:6d2af70c92ab 536 // int newestPointY = rawIntArrayYData[lastXIndex]; // (int)chart.Series[seriesName].Points[newestPointIndex].YValues[0];
darienf 20:6d2af70c92ab 537 // if ((newestPointY < chart.ChartAreas[chartAreaName].AxisY.Maximum)
darienf 20:6d2af70c92ab 538 // && (newestPointY > chart.ChartAreas[chartAreaName].AxisY.Minimum)
darienf 20:6d2af70c92ab 539 // )
darienf 20:6d2af70c92ab 540 // {
darienf 20:6d2af70c92ab 541 // // https://jira.maxim-ic.com/browse/OS24EVK-34 always re-evaluate chart when new data exceeds Minimum or Maximum
darienf 20:6d2af70c92ab 542 // // But if we just return at this point, then autoscale will never contract to fit smaller data series.
darienf 20:6d2af70c92ab 543 // //
darienf 20:6d2af70c92ab 544 // // https://jira.maxim-ic.com/browse/OS24EVK-34 Change _AutoScaleTimeInterval to _AutoScaleEveryNSamples and use sampleNumber to measure time
darienf 20:6d2af70c92ab 545 // // https://jira.maxim-ic.com/browse/OS24EVK-34 AutoScaleEvaluate() after every _AutoScaleEveryNSamples, re-scan data and fit chart to data
darienf 20:6d2af70c92ab 546 // // if ( interval has been less than _AutoScaleEveryNSamples ) { return; }
darienf 20:6d2af70c92ab 547 // if ((sampleNumber - sampleNumberPreviousAutoscale) < _AutoScaleEveryNSamples)
darienf 20:6d2af70c92ab 548 // {
darienf 20:6d2af70c92ab 549 //#if PROFILER
darienf 20:6d2af70c92ab 550 // // TODO1: OS24EVK-54 profiling: capture max interval, number of intervals, cumulative interval
darienf 20:6d2af70c92ab 551 // _profilingStopwatchAutoScaleEvaluate.Stop();
darienf 20:6d2af70c92ab 552 //#endif // PROFILER
darienf 20:6d2af70c92ab 553 // return;
darienf 20:6d2af70c92ab 554 // }
darienf 20:6d2af70c92ab 555 // }
darienf 20:6d2af70c92ab 556
darienf 20:6d2af70c92ab 557 int chartMinYValue = (int)_chart.ChartAreas[chartAreaName].AxisY.Minimum; // rawIntArrayYData[firstVisiblePointX] - 1;
darienf 20:6d2af70c92ab 558 int chartMaxYValue = (int)_chart.ChartAreas[chartAreaName].AxisY.Maximum; // rawIntArrayYData[firstVisiblePointX] + 1;
darienf 20:6d2af70c92ab 559 // VERIFY: OS24EVK-73 Sample Avg 2 breaks chart scrolling - firstVisiblePointIndex instead of firstVisiblePointX
darienf 20:6d2af70c92ab 560 int dataMinYValue = rawIntArrayYData[firstVisiblePointIndex] - 1;
darienf 20:6d2af70c92ab 561 int dataMaxYValue = rawIntArrayYData[firstVisiblePointIndex] + 1;
darienf 20:6d2af70c92ab 562 for (int index = firstVisiblePointIndex; index <= lastXIndex; index++)
darienf 20:6d2af70c92ab 563 {
darienf 20:6d2af70c92ab 564 int Y = rawIntArrayYData[index];
darienf 20:6d2af70c92ab 565 // VERIFY: OS24EVK-57 OS24EVK-54 improve chart throughput: AutoScaleEvaluate() dataFormatIs16bit2sComplement=true for Accelerometer X Y Z data
darienf 20:6d2af70c92ab 566 // VERIFY: OS24EVK-75 replace AutoScaleEvaluate dataFormatIs16bit2sComplement with if (DataFormat == DataFormats.Format16bit2sComplement)
darienf 20:6d2af70c92ab 567 if (DataFormat == DataFormats.Format16bit2sComplement)
darienf 20:6d2af70c92ab 568 {
darienf 20:6d2af70c92ab 569 // VERIFY: OS24EVK-57 interpret rawX rawY rawZ as 16-bit 2's complement
darienf 20:6d2af70c92ab 570 if (Y > 0x8000)
darienf 20:6d2af70c92ab 571 {
darienf 20:6d2af70c92ab 572 Y = Y - 0x10000;
darienf 20:6d2af70c92ab 573 }
darienf 20:6d2af70c92ab 574 }
darienf 20:6d2af70c92ab 575 //else
darienf 20:6d2af70c92ab 576 //{
darienf 20:6d2af70c92ab 577 // Y = Y + 0; // debug breakpoint for code path verification
darienf 20:6d2af70c92ab 578 //}
darienf 20:6d2af70c92ab 579 if (dataMinYValue > Y)
darienf 20:6d2af70c92ab 580 {
darienf 20:6d2af70c92ab 581 dataMinYValue = Y;
darienf 20:6d2af70c92ab 582 }
darienf 20:6d2af70c92ab 583 if (dataMaxYValue < Y)
darienf 20:6d2af70c92ab 584 {
darienf 20:6d2af70c92ab 585 dataMaxYValue = Y;
darienf 20:6d2af70c92ab 586 }
darienf 20:6d2af70c92ab 587 }
darienf 20:6d2af70c92ab 588 // rawIntArrayYData[firstIndex..lastIndex] spans the range from dataMinYValue .. dataMaxYValue
darienf 20:6d2af70c92ab 589 double dataSpanY = (dataMaxYValue - dataMinYValue);
darienf 20:6d2af70c92ab 590 double dataCenterY = (dataMaxYValue + dataMinYValue) / 2;
darienf 20:6d2af70c92ab 591
darienf 20:6d2af70c92ab 592 bool isAllDataVisible = (
darienf 20:6d2af70c92ab 593 (chartMinYValue < dataMaxYValue) && (dataMaxYValue < chartMaxYValue)
darienf 20:6d2af70c92ab 594 &&
darienf 20:6d2af70c92ab 595 (chartMinYValue < dataMinYValue) && (dataMinYValue < chartMaxYValue)
darienf 20:6d2af70c92ab 596 );
darienf 20:6d2af70c92ab 597 if (isAllDataVisible)
darienf 20:6d2af70c92ab 598 {
darienf 20:6d2af70c92ab 599 // all data is within the chart limits, but is the chart sufficiently zoomed in?
darienf 20:6d2af70c92ab 600 double relativeDataSpan = (double)dataSpanY / (chartMaxYValue - chartMinYValue);
darienf 20:6d2af70c92ab 601 if (relativeDataSpan > 0.65)
darienf 20:6d2af70c92ab 602 {
darienf 20:6d2af70c92ab 603 // the current chart scale is good enough, do not update
darienf 20:6d2af70c92ab 604 #if PROFILER
darienf 20:6d2af70c92ab 605 // TODO1: OS24EVK-54 profiling: capture max interval, number of intervals, cumulative interval
darienf 20:6d2af70c92ab 606 _profilingStopwatchAutoScaleEvaluate.Stop();
darienf 20:6d2af70c92ab 607 #endif // PROFILER
darienf 20:6d2af70c92ab 608 return;
darienf 20:6d2af70c92ab 609 }
darienf 20:6d2af70c92ab 610 }
darienf 20:6d2af70c92ab 611 // https://jira.maxim-ic.com/browse/OS24EVK-34 AutoScaleEvaluate() use _AutoScaleMinSpanY; // minimum allowed span of chartMax-chartMin, to avoid focusing on LSB noise
darienf 20:6d2af70c92ab 612 if (dataSpanY < _AutoScaleMinSpanY)
darienf 20:6d2af70c92ab 613 {
darienf 20:6d2af70c92ab 614 // https://jira.maxim-ic.com/browse/OS24EVK-34 Autoscale - Minimum span (in case dataMax-dataMin is too small) to avoid focusing on LSB noise
darienf 20:6d2af70c92ab 615 dataSpanY = _AutoScaleMinSpanY;
darienf 20:6d2af70c92ab 616 }
darienf 20:6d2af70c92ab 617 else
darienf 20:6d2af70c92ab 618 {
darienf 20:6d2af70c92ab 619 // https://jira.maxim-ic.com/browse/OS24EVK-34 Autoscale - Apply a Top margin - Bottom margin = 12% .. 88% when mapping Chart max-min to Data max-min
darienf 20:6d2af70c92ab 620 // https://jira.maxim-ic.com/browse/OS24EVK-34 AutoScaleEvaluate() use _AutoScaleMarginYTop; // chartMax empty area above dataMax
darienf 20:6d2af70c92ab 621 // https://jira.maxim-ic.com/browse/OS24EVK-34 AutoScaleEvaluate() use _AutoScaleMarginYBottom; // chartMin empty area below dataMin
darienf 20:6d2af70c92ab 622 // dataSpanY = dataSpanY * 1.25;
darienf 20:6d2af70c92ab 623 // dataSpanY = dataSpanY * (1.0 + 0.125 + 0.125);
darienf 20:6d2af70c92ab 624 dataSpanY = dataSpanY * (1.0 + _AutoScaleMarginYTop + _AutoScaleMarginYBottom);
darienf 20:6d2af70c92ab 625
darienf 20:6d2af70c92ab 626 // https://jira.maxim-ic.com/browse/OS24EVK-42 Autoscale tuning (Larry 2014-11-24) Round to nearest multiple of 100.
darienf 20:6d2af70c92ab 627 // - Y axis: No decimals. Round to nearest multiple of 100.
darienf 20:6d2af70c92ab 628 // private double _AutoScaleYmultiples = 100;
darienf 20:6d2af70c92ab 629 //int tempdataSpanY = (int)(dataSpanY / _AutoScaleYmultiples);
darienf 20:6d2af70c92ab 630 //dataSpanY = tempdataSpanY * _AutoScaleYmultiples;
darienf 20:6d2af70c92ab 631 //dataSpanY = (double)((int)dataSpanY / _AutoScaleYmultiples) * _AutoScaleYmultiples;
darienf 20:6d2af70c92ab 632 //dataCenterY = (double)((int)dataCenterY / _AutoScaleYmultiples) * _AutoScaleYmultiples;
darienf 20:6d2af70c92ab 633 }
darienf 20:6d2af70c92ab 634 // https://jira.maxim-ic.com/browse/OS24EVK-34 Autoscale - There are 5 vertical divisions; keep the minor axis tick values integer
darienf 20:6d2af70c92ab 635 //int YaxisDivisions = (int)System.Math.Ceiling(dataSpanY / 6.0);
darienf 20:6d2af70c92ab 636 //dataSpanY = YaxisDivisions * 6;
darienf 20:6d2af70c92ab 637 // https://jira.maxim-ic.com/browse/OS24EVK-42 Autoscale tuning (Larry 2014-11-24) Round to nearest multiple of 100.
darienf 20:6d2af70c92ab 638 // - Y axis: No decimals. Round to nearest multiple of 100.
darienf 20:6d2af70c92ab 639 // private double _AutoScaleYmultiples = 100;
darienf 20:6d2af70c92ab 640 //int tempdataCenterY = (int)(dataCenterY / (_AutoScaleYmultiples*2));
darienf 20:6d2af70c92ab 641 //dataCenterY = tempdataCenterY * (_AutoScaleYmultiples*2);
darienf 20:6d2af70c92ab 642
darienf 20:6d2af70c92ab 643 // chart.ChartAreas[0].AxisY.Maximum = dataMaxYValue;
darienf 20:6d2af70c92ab 644 // chart.ChartAreas[0].AxisY.Minimum = dataMinYValue;
darienf 20:6d2af70c92ab 645 //chart.ChartAreas[0].AxisY.Maximum = System.Math.Ceiling(dataCenterY + (dataSpanY / 2)); // dataMaxYValue;
darienf 20:6d2af70c92ab 646 //chart.ChartAreas[0].AxisY.Minimum = System.Math.Floor(dataCenterY - (dataSpanY / 2)); // dataMinYValue;
darienf 20:6d2af70c92ab 647 // https://jira.maxim-ic.com/browse/OS24EVK-42 Autoscale tuning (Larry 2014-11-24) Round to nearest multiple of 100.
darienf 20:6d2af70c92ab 648 // - Y axis: No decimals. Round to nearest multiple of 100.
darienf 20:6d2af70c92ab 649 // private double _AutoScaleYmultiples = 100;
darienf 20:6d2af70c92ab 650 int iMaxY_target = (int)((dataCenterY + (dataSpanY / 2)) / _AutoScaleYmultiples);
darienf 20:6d2af70c92ab 651 int iMinY_target = (int)((dataCenterY - (dataSpanY / 2)) / _AutoScaleYmultiples);
darienf 20:6d2af70c92ab 652
darienf 20:6d2af70c92ab 653 double dampingConstantK = 0.2;
darienf 20:6d2af70c92ab 654 int iMaxY_damped = iMaxY_target;
darienf 20:6d2af70c92ab 655 int iMinY_damped = iMinY_target;
darienf 20:6d2af70c92ab 656 if (double.IsNaN(_chart.ChartAreas[chartAreaName].AxisY.Maximum) == false)
darienf 20:6d2af70c92ab 657 {
darienf 20:6d2af70c92ab 658 int iMaxY_chart = (int)(_chart.ChartAreas[chartAreaName].AxisY.Maximum / _AutoScaleYmultiples);
darienf 20:6d2af70c92ab 659 int iMinY_chart = (int)(_chart.ChartAreas[chartAreaName].AxisY.Minimum / _AutoScaleYmultiples);
darienf 20:6d2af70c92ab 660 iMaxY_damped = (int)((dampingConstantK * (double)iMaxY_target) + ((1 - dampingConstantK) * (double)iMaxY_chart) + 0.5);
darienf 20:6d2af70c92ab 661 iMinY_damped = (int)((dampingConstantK * (double)iMinY_target) + ((1 - dampingConstantK) * (double)iMinY_chart));
darienf 20:6d2af70c92ab 662 }
darienf 20:6d2af70c92ab 663 double maxY = iMaxY_target * _AutoScaleYmultiples;
darienf 20:6d2af70c92ab 664 double minY = iMinY_target * _AutoScaleYmultiples;
darienf 20:6d2af70c92ab 665 _chart.ChartAreas[chartAreaName].AxisY.Maximum = maxY;
darienf 20:6d2af70c92ab 666 _chart.ChartAreas[chartAreaName].AxisY.Minimum = minY;
darienf 20:6d2af70c92ab 667 // if chart.ChartAreas[chartAreaName].AxisY.Maximum is NaN then just assign maxY
darienf 20:6d2af70c92ab 668 //if (double.IsNaN(chart.ChartAreas[chartAreaName].AxisY.Maximum))
darienf 20:6d2af70c92ab 669 //{
darienf 20:6d2af70c92ab 670 // chart.ChartAreas[chartAreaName].AxisY.Maximum = maxY;
darienf 20:6d2af70c92ab 671 // chart.ChartAreas[chartAreaName].AxisY.Minimum = minY;
darienf 20:6d2af70c92ab 672 //}
darienf 20:6d2af70c92ab 673 //else
darienf 20:6d2af70c92ab 674 //{
darienf 20:6d2af70c92ab 675 // chart.ChartAreas[chartAreaName].AxisY.Maximum = (dampingConstantK * maxY) + ((1 - dampingConstantK) * chart.ChartAreas[chartAreaName].AxisY.Maximum);
darienf 20:6d2af70c92ab 676 // chart.ChartAreas[chartAreaName].AxisY.Minimum = (dampingConstantK * minY) + ((1 - dampingConstantK) * chart.ChartAreas[chartAreaName].AxisY.Minimum);
darienf 20:6d2af70c92ab 677 //}
darienf 20:6d2af70c92ab 678
darienf 20:6d2af70c92ab 679 #if PROFILER
darienf 20:6d2af70c92ab 680 // TODO1: OS24EVK-54 profiling: capture max interval, number of intervals, cumulative interval
darienf 20:6d2af70c92ab 681 _profilingStopwatchAutoScaleEvaluate.Stop();
darienf 20:6d2af70c92ab 682 #endif // PROFILER
darienf 20:6d2af70c92ab 683 }
darienf 20:6d2af70c92ab 684
darienf 20:6d2af70c92ab 685 /// <summary>
darienf 20:6d2af70c92ab 686 /// Raw data format
darienf 20:6d2af70c92ab 687 /// </summary>
darienf 20:6d2af70c92ab 688 public DataFormats DataFormat = DataFormats.FormatUnsigned;
darienf 20:6d2af70c92ab 689
darienf 20:6d2af70c92ab 690 public enum DataFormats
darienf 20:6d2af70c92ab 691 {
darienf 20:6d2af70c92ab 692 /// <summary>
darienf 20:6d2af70c92ab 693 /// Interpret raw data as unsigned values
darienf 20:6d2af70c92ab 694 /// </summary>
darienf 20:6d2af70c92ab 695 FormatUnsigned,
darienf 20:6d2af70c92ab 696
darienf 20:6d2af70c92ab 697 /// <summary>
darienf 20:6d2af70c92ab 698 /// Interpret raw data as 16-bit, signed 2's complement values
darienf 20:6d2af70c92ab 699 /// </summary>
darienf 20:6d2af70c92ab 700 Format16bit2sComplement,
darienf 20:6d2af70c92ab 701 }
darienf 20:6d2af70c92ab 702
darienf 20:6d2af70c92ab 703 } // public class MedicalChartHelper
darienf 20:6d2af70c92ab 704
darienf 20:6d2af70c92ab 705 } // namespace Maxim.MAX30101