This is the latest working repository used in our demo video for the Maxim to display temperature readings on Bluetooth

Dependencies:   USBDevice

Committer:
darienf
Date:
Sun May 02 23:09:04 2021 +0000
Revision:
5:bc128a16232f
Parent:
3:36de8b9e4b1a
This is the program that was last used, that has the working temperature and some comments

Who changed what in which revision?

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