Darien Figueroa / Mbed OS Final_Program

Dependencies:   USBDevice

Embed: (wiki syntax)

« Back to documentation index

Show/hide line numbers MedicalChartHelper.cs Source File

MedicalChartHelper.cs

00001 //using System;
00002 //using System.Collections;
00003 //using System.Collections.Generic;
00004 //using System.ComponentModel;
00005 //using System.Linq;
00006 //using System.Text;
00007 //using System.Windows.Forms;
00008 //using Microsoft.Win32.SafeHandles;
00009 
00010 namespace Maxim.MAX30101
00011 {
00012 #pragma warning disable 1574
00013     /// <summary>
00014     /// MedicalChartHelper class, an invisible "helper" class, 
00015     /// helps manage an existing standard chart object.
00016     /// 
00017     /// Initializes the standard 
00018     /// System.Windows.Forms.DataVisualization.Charting.Chart chart
00019     /// with a maximum of 3 chart areas vertically aligned with each other.
00020     ///
00021     /// When data is appended to the chart, the chart series data
00022     /// is initially aligned with the left. Once the X axis reaches the
00023     /// right side of the screen, the chart begins scrolling so that
00024     /// newest data is aligned to the right edge.
00025     ///
00026     /// </summary>
00027 #pragma warning restore 1574
00028     public class MedicalChartHelper
00029     {
00030         private System.Windows.Forms.DataVisualization.Charting.Chart _chart;
00031         private string _chartArea1Name;
00032         private string _chartArea2Name;
00033         private string _chartArea3Name;
00034         private string _series1Name;
00035         private string _series2Name;
00036         private string _series3Name;
00037         private string _chartArea1AxisYTitle;
00038         private string _chartArea2AxisYTitle;
00039         private string _chartArea3AxisYTitle;
00040         
00041         /// <summary>
00042         /// Constructor 
00043         /// </summary>
00044         /// <param name="chart"></param>
00045         /// <param name="chartArea1Name"></param>
00046         /// <param name="series1Name"></param>
00047         /// <param name="chartArea1AxisYTitle"></param>
00048         /// <param name="chartArea2Name"></param>
00049         /// <param name="series2Name"></param>
00050         /// <param name="chartArea2AxisYTitle"></param>
00051         /// <param name="chartArea3Name"></param>
00052         /// <param name="series3Name"></param>
00053         /// <param name="chartArea3AxisYTitle"></param>
00054         public MedicalChartHelper(System.Windows.Forms.DataVisualization.Charting.Chart chart,
00055             string chartArea1Name = "ChartArea1Red", string series1Name = "SeriesRed", string chartArea1AxisYTitle = "Red ADC Code",
00056             string chartArea2Name = "ChartArea2IR", string series2Name = "SeriesIR", string chartArea2AxisYTitle = "IR ADC Code",
00057             string chartArea3Name = "ChartArea3Green", string series3Name = "SeriesGreen", string chartArea3AxisYTitle = "Green ADC Code",
00058             DataFormats  dataFormat = DataFormats .FormatUnsigned
00059         )
00060         {
00061             // implementation: see InitCharting()
00062             // default X axis length should be 10 seconds of data
00063             _chart = chart;
00064             _chartArea1Name = chartArea1Name;
00065             _chartArea2Name = chartArea2Name;
00066             _chartArea3Name = chartArea3Name;
00067             _series1Name = series1Name;
00068             _series2Name = series2Name;
00069             _series3Name = series3Name;
00070             _chartArea1AxisYTitle = chartArea1AxisYTitle;
00071             _chartArea2AxisYTitle = chartArea2AxisYTitle;
00072             _chartArea3AxisYTitle = chartArea3AxisYTitle;
00073 
00074             DataFormat = dataFormat;
00075 
00076             // VERIFY: OS24EVK-75 replace _xCount with _xCount1, _xCount2, _xCount3
00077             //_xCount = 0;
00078             _xCount1 = 0;
00079             _xCount2 = 0;
00080             _xCount3 = 0;
00081 
00082             _chart.Titles[0].Visible = false;
00083 
00084             _chart.ChartAreas[_chartArea2Name].AlignWithChartArea = _chartArea1Name;
00085             _chart.ChartAreas[_chartArea3Name].AlignWithChartArea = _chartArea1Name;
00086 
00087             _chart.ChartAreas[_chartArea1Name].AxisY.Title = chartArea1AxisYTitle;
00088             _chart.ChartAreas[_chartArea2Name].AxisY.Title = chartArea2AxisYTitle;
00089             _chart.ChartAreas[_chartArea3Name].AxisY.Title = chartArea3AxisYTitle;
00090 
00091             //_chart.ChartAreas[_chartArea1Name].AxisX.LabelStyle.Enabled = !_plotXAxisNoLabels;
00092             //_chart.ChartAreas[_chartArea2Name].AxisX.LabelStyle.Enabled = !_plotXAxisNoLabels;
00093             //_chart.ChartAreas[_chartArea3Name].AxisX.LabelStyle.Enabled = !_plotXAxisNoLabels;
00094 
00095             _chart.ChartAreas[_chartArea1Name].Visible = true; // valid_Red;
00096             _chart.ChartAreas[_chartArea2Name].Visible = true; // valid_IR;
00097             _chart.ChartAreas[_chartArea3Name].Visible = true; // valid_Green;
00098 
00099             _chart.Series[_series1Name].MarkerSize = 1; // Charting.Series.MarkerSize=0 makes no points visible
00100             _chart.Series[_series1Name].BorderWidth = 1; // Charting.Series.BorderWidth is actually the line thickness
00101             _chart.Series[_series1Name].XValueType = System.Windows.Forms.DataVisualization.Charting.ChartValueType.UInt32;
00102             _chart.Series[_series1Name].YValueType = System.Windows.Forms.DataVisualization.Charting.ChartValueType.Int32;
00103 
00104             _chart.Series[_series2Name].MarkerSize = 1; // Charting.Series.MarkerSize=0 makes no points visible
00105             _chart.Series[_series2Name].BorderWidth = 1; // Charting.Series.BorderWidth is actually the line thickness
00106             _chart.Series[_series2Name].XValueType = System.Windows.Forms.DataVisualization.Charting.ChartValueType.UInt32;
00107             _chart.Series[_series2Name].YValueType = System.Windows.Forms.DataVisualization.Charting.ChartValueType.Int32;
00108 
00109             _chart.Series[_series3Name].MarkerSize = 1; // Charting.Series.MarkerSize=0 makes no points visible
00110             _chart.Series[_series3Name].BorderWidth = 1; // Charting.Series.BorderWidth is actually the line thickness
00111             _chart.Series[_series3Name].XValueType = System.Windows.Forms.DataVisualization.Charting.ChartValueType.UInt32;
00112             _chart.Series[_series3Name].YValueType = System.Windows.Forms.DataVisualization.Charting.ChartValueType.Int32;
00113         }
00114 
00115         /// <summary>
00116         /// Clear chart data, reset _xCount, reset _plotPointsToSkip
00117         /// </summary>
00118         public void Clear()
00119         {
00120             _chart.Series[_series1Name].Points.Clear();
00121             _chart.Series[_series2Name].Points.Clear();
00122             _chart.Series[_series3Name].Points.Clear();
00123             // VERIFY: OS24EVK-75 replace _xCount with _xCount1, _xCount2, _xCount3
00124             //_xCount = 0;
00125             _xCount1 = 0;
00126             _xCount2 = 0;
00127             _xCount3 = 0;
00128             _plotPointsToSkip = 0;
00129             // https://jira.maxim-ic.com/browse/OS24EVK-32 replace numPlotTime with menu item Options | Plot Time
00130             _plotPoints = (int)_plotWindowTime * _SampleRate_Hz / SampleAverage_n;
00131             if (_plotPoints > _maxPlotPoints)
00132             {
00133                 _plotPointsToSkip = _plotPoints / _maxPlotPoints - 1;
00134                 _plotPoints = _maxPlotPoints;
00135             }
00136         }
00137 
00138         /// <summary>
00139         /// Replacement for plotXAxisNoLabelsToolStripMenuItem.Checked
00140         /// </summary>
00141         public bool plotXAxisNoLabels { 
00142             get { return _plotXAxisNoLabels; }
00143             set
00144             {
00145                 _plotXAxisNoLabels = value;
00146                 _chart.ChartAreas[_chartArea1Name].AxisX.LabelStyle.Enabled = !_plotXAxisNoLabels;
00147                 _chart.ChartAreas[_chartArea2Name].AxisX.LabelStyle.Enabled = !_plotXAxisNoLabels;
00148                 _chart.ChartAreas[_chartArea3Name].AxisX.LabelStyle.Enabled = !_plotXAxisNoLabels;
00149             }
00150         }
00151         private bool _plotXAxisNoLabels = true; // VERIFY: default plotXAxisNoLabelsToolStripMenuItem.Checked
00152 
00153         /// <summary>
00154         /// Replacement for plotXAxisTimeToolStripMenuItem.Checked
00155         /// </summary>
00156         public bool plotXAxisTime
00157         {
00158             get { return _plotXAxisTime; }
00159             set
00160             {
00161                 _plotXAxisTime = value;
00162             }
00163         }
00164         private bool _plotXAxisTime = true; // VERIFY: default plotXAxisTimeToolStripMenuItem.Checked
00165 
00166         private int _SampleRate_Hz = 100;
00167         /// <summary>
00168         /// Sample rate. Replacement for int.Parse(cboSampleRate.Text)
00169         /// </summary>
00170         public int SampleRate_Hz
00171         {
00172             get { return _SampleRate_Hz; } 
00173             set 
00174             { 
00175                 _SampleRate_Hz = value;
00176 
00177                 //_AutoScaleEveryNSamples = 4 * _SampleRate_Hz; // 4 seconds assuming sample rate int.Parse(cboSampleRate.Text) == 100 samples per second
00178 
00179                 // VERIFY: OS24EVK-73 Sample Avg 2 breaks chart scrolling
00180                 // update _plotPoints which depends on myMAX30101.SampleAverage_n
00181                 _plotPoints = (int)_plotWindowTime * _SampleRate_Hz / SampleAverage_n;
00182                 if (_plotPoints > _maxPlotPoints)
00183                 {
00184                     _plotPointsToSkip = _plotPoints / _maxPlotPoints - 1;
00185                     _plotPoints = _maxPlotPoints;
00186                 }
00187             } 
00188         }
00189 
00190         private int _SampleAverage_n = 1;
00191         /// <summary>
00192         /// Replacement for myMAX30101.SampleAverage_n
00193         /// </summary>
00194         public int SampleAverage_n
00195         {
00196             get { return _SampleAverage_n; }
00197             set
00198             {
00199                 _SampleAverage_n = value;
00200                 // VERIFY: OS24EVK-73 Sample Avg 2 breaks chart scrolling
00201                 // update _plotPoints which depends on myMAX30101.SampleAverage_n
00202                 _plotPoints = (int)_plotWindowTime * _SampleRate_Hz / SampleAverage_n;
00203                 if (_plotPoints > _maxPlotPoints)
00204                 {
00205                     _plotPointsToSkip = _plotPoints / _maxPlotPoints - 1;
00206                     _plotPoints = _maxPlotPoints;
00207                 }
00208             }
00209         }
00210 
00211         // VERIFY: IMPLEMENT OS24EVK-70 Maxim.MAX30101.MedicalChartHelper._xCount int instead of double?
00212         // VERIFY: OS24EVK-75 replace _xCount with _xCount1, _xCount2, _xCount3
00213         //public double _xCount = 0;
00214         public int _xCount1 = 0;
00215         public int _xCount2 = 0;
00216         public int _xCount3 = 0;
00217 
00218         /// <summary>
00219         /// 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
00220         /// </summary>
00221         public int _plotPoints = 500;
00222 
00223         private int _plotWindowTime = 5;    // plot window in seconds
00224         public int plotWindowTime 
00225         {
00226             get 
00227             {
00228                 return _plotWindowTime;
00229             }
00230             set 
00231             {
00232                 _plotWindowTime = value;
00233                 _plotPoints = (int)_plotWindowTime * _SampleRate_Hz / SampleAverage_n;
00234                 if (_plotPoints > _maxPlotPoints)
00235                 {
00236                     _plotPointsToSkip = _plotPoints / _maxPlotPoints - 1;
00237                     _plotPoints = _maxPlotPoints;
00238                 }
00239             }
00240         }
00241 
00242         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
00243 
00244         public int _plotPointsToSkip = 0;
00245 
00246         // https://jira.maxim-ic.com/browse/OS24EVK-34 Autoscale tuning parameters
00247         // https://jira.maxim-ic.com/browse/OS24EVK-34 Change _AutoScaleTimeInterval to _AutoScaleEveryNSamples and use sampleNumber to measure time
00248         // https://jira.maxim-ic.com/browse/OS24EVK-42 Autoscale tuning (Larry 2014-11-24) _AutoScaleEveryNSamples
00249         // - Slow the Autoscale update rate every 4 seconds
00250         //private int _AutoScaleEveryNSamples = 400; // 4 seconds assuming sample rate int.Parse(cboSampleRate.Text) == 100 samples per second
00251 
00252         // private double _AutoScaleMarginXLeft; // ignore old data at left of graph
00253         // private double _AutoScaleMarginXRight; // ignore new data at right of graph? probably 0 by default
00254         
00255         /// <summary>
00256         /// chartMax empty area above dataMax
00257         /// </summary>
00258         private static double _AutoScaleMarginYTop = 0.125;
00259         
00260         /// <summary>
00261         /// chartMin empty area below dataMin
00262         /// </summary>
00263         private static double _AutoScaleMarginYBottom = 0.125;
00264         
00265         /// <summary>
00266         /// Minimum Y span: allow up to 10 counts per division (i.e. 50 LSBs)
00267         /// 
00268         /// (5 * _AutoScaleYmultiples) minimum allowed span of chartMax-chartMin, 
00269         /// to avoid focusing on LSB noise
00270         /// </summary>
00271         private static double _AutoScaleMinSpanY = 500;
00272 
00273         /// <summary>
00274         /// Y axis: No decimals. Round to nearest multiple of 100.
00275         /// </summary>
00276         private static double _AutoScaleYmultiples = 100;
00277 
00278         // Calculated initial default chart limits _AutoscaleInitialChartMaxY .. _AutoscaleInitialChartMinY
00279         int _AutoscaleInitialChartMaxY =
00280             (
00281                 (int)
00282                     ((_AutoScaleMinSpanY * (1.0 + _AutoScaleMarginYTop + _AutoScaleMarginYBottom)) / _AutoScaleYmultiples)
00283             )
00284             * (int)(_AutoScaleYmultiples);
00285         int _AutoscaleInitialChartMinY = 0;
00286 
00287         /// <summary>
00288         /// Append data to chart area 1 series 1 (Optical: Red. Accelerometer: X)
00289         ///
00290         /// @post _xCount1 is updated
00291         /// </summary>
00292         /// <param name="rawIntArrayYData"></param>
00293         /// <param name="firstNewXIndex"></param>
00294         /// <param name="lastXIndex"></param>
00295         public void AppendDataChartArea1(int[] rawIntArrayYData, int firstNewXIndex, int lastXIndex)
00296         {
00297             // AppendDataChartArea(_chartArea1Name, _series1Name, rawIntArrayYData, firstNewXIndex, lastXIndex);
00298             string chartAreaName = _chartArea1Name;
00299             string seriesName = _series1Name;
00300             
00301             for (int index = firstNewXIndex; index <= lastXIndex; index++)
00302             {
00303                 int yData = rawIntArrayYData[index];
00304                 // VERIFY: OS24EVK-75 replace AutoScaleEvaluate dataFormatIs16bit2sComplement with if (DataFormat == DataFormats.Format16bit2sComplement) 
00305                 if (DataFormat == DataFormats .Format16bit2sComplement)
00306                 {
00307                     // VERIFY: OS24EVK-57 interpret rawX rawY rawZ as 16-bit 2's complement
00308                     if (yData > 0x8000)
00309                     {
00310                         yData = yData - 0x10000;
00311                     }
00312                 }
00313                 int count = _chart.Series[seriesName].Points.Count;
00314                 // VERIFY: OS24EVK-75 replace _xCount with _xCount1, _xCount2, _xCount3
00315                 double xCoord = _xCount1 * (_plotPointsToSkip + 1) * SampleAverage_n / (plotXAxisTime ? SampleRate_Hz : 1);
00316                 
00317                 //_chart.Series[seriesName].Points.AddXY(xCoord, yData);
00318                 _chart.Series[seriesName].Points.AddY(yData);
00319                 _chart.ResetAutoValues();
00320 
00321                 while (count > _plotPoints)
00322                 {
00323                     _chart.Series[seriesName].Points.RemoveAt(0);
00324                     count = _chart.Series[seriesName].Points.Count;
00325                 }
00326                 // VERIFY: OS24EVK-75 replace _xCount with _xCount1, _xCount2, _xCount3
00327                 _xCount1++;
00328             }
00329             //AutoScaleEvaluate(e.rawRedData, e.sampleNumberOffset, numSamples - 1, _chart, chartAreaName);
00330         }
00331 
00332         /// <summary>
00333         /// Append data to chart area 2 series 2 (Optical: IR. Accelerometer: Y)
00334         ///
00335         /// @post _xCount2 is updated
00336         /// </summary>
00337         /// <param name="rawIntArrayYData"></param>
00338         /// <param name="firstNewXIndex"></param>
00339         /// <param name="lastXIndex"></param>
00340         public void AppendDataChartArea2(int[] rawIntArrayYData, int firstNewXIndex, int lastXIndex)
00341         {
00342             // AppendDataChartArea(_chartArea2Name, _series2Name, rawIntArrayYData, firstNewXIndex, lastXIndex);
00343             string chartAreaName = _chartArea2Name;
00344             string seriesName = _series2Name;
00345             
00346             for (int index = firstNewXIndex; index <= lastXIndex; index++)
00347             {
00348                 int yData = rawIntArrayYData[index];
00349                 // VERIFY: OS24EVK-75 replace AutoScaleEvaluate dataFormatIs16bit2sComplement with if (DataFormat == DataFormats.Format16bit2sComplement) 
00350                 if (DataFormat == DataFormats .Format16bit2sComplement)
00351                 {
00352                     // VERIFY: OS24EVK-57 interpret rawX rawY rawZ as 16-bit 2's complement
00353                     if (yData > 0x8000)
00354                     {
00355                         yData = yData - 0x10000;
00356                     }
00357                 }
00358                 int count = _chart.Series[seriesName].Points.Count;
00359                 // VERIFY: OS24EVK-75 replace _xCount with _xCount1, _xCount2, _xCount3
00360                 double xCoord = _xCount2 * (_plotPointsToSkip + 1) * SampleAverage_n / (plotXAxisTime ? SampleRate_Hz : 1);
00361                 
00362                 _chart.Series[seriesName].Points.AddXY(xCoord, yData);
00363                 _chart.ResetAutoValues();
00364 
00365                 while (count > _plotPoints)
00366                 {
00367                     _chart.Series[seriesName].Points.RemoveAt(0);
00368                     count = _chart.Series[seriesName].Points.Count;
00369                 }
00370                 // VERIFY: OS24EVK-75 replace _xCount with _xCount1, _xCount2, _xCount3
00371                 _xCount2++;
00372             }
00373             //AutoScaleEvaluate(e.rawRedData, e.sampleNumberOffset, numSamples - 1, _chart, chartAreaName);
00374         }
00375 
00376         /// <summary>
00377         /// Append data to chart area 3 series 3 (Optical: Green. Accelerometer: Z)
00378         ///
00379         /// @post _xCount3 is updated
00380         /// </summary>
00381         /// <param name="rawIntArrayYData"></param>
00382         /// <param name="firstNewXIndex"></param>
00383         /// <param name="lastXIndex"></param>
00384         public void AppendDataChartArea3(int[] rawIntArrayYData, int firstNewXIndex, int lastXIndex)
00385         {
00386             // AppendDataChartArea(_chartArea3Name, _series3Name, rawIntArrayYData, firstNewXIndex, lastXIndex);
00387             string chartAreaName = _chartArea3Name;
00388             string seriesName = _series3Name;
00389             
00390             for (int index = firstNewXIndex; index <= lastXIndex; index++)
00391             {
00392                 int yData = rawIntArrayYData[index];
00393                 // VERIFY: OS24EVK-75 replace AutoScaleEvaluate dataFormatIs16bit2sComplement with if (DataFormat == DataFormats.Format16bit2sComplement) 
00394                 if (DataFormat == DataFormats .Format16bit2sComplement)
00395                 {
00396                     // VERIFY: OS24EVK-57 interpret rawX rawY rawZ as 16-bit 2's complement
00397                     if (yData > 0x8000)
00398                     {
00399                         yData = yData - 0x10000;
00400                     }
00401                 }
00402                 int count = _chart.Series[seriesName].Points.Count;
00403                 // VERIFY: OS24EVK-75 replace _xCount with _xCount1, _xCount2, _xCount3
00404                 double xCoord = _xCount3 * (_plotPointsToSkip + 1) * SampleAverage_n / (plotXAxisTime ? SampleRate_Hz : 1);
00405                 
00406                 _chart.Series[seriesName].Points.AddXY(xCoord, yData);
00407                 _chart.ResetAutoValues();
00408 
00409                 while (count > _plotPoints)
00410                 {
00411                     _chart.Series[seriesName].Points.RemoveAt(0);
00412                     count = _chart.Series[seriesName].Points.Count;
00413                 }
00414                 // VERIFY: OS24EVK-75 replace _xCount with _xCount1, _xCount2, _xCount3
00415                 _xCount3++;
00416             }
00417             //AutoScaleEvaluate(e.rawRedData, e.sampleNumberOffset, numSamples - 1, _chart, chartAreaName);
00418         }
00419 
00420 #if PROFILER
00421         //// TODO1: OS24EVK-54 profiling: capture max interval, number of intervals, cumulative interval
00422         System.Diagnostics.Stopwatch _profilingStopwatchAutoScaleEvaluate = new System.Diagnostics.Stopwatch(); // cumulative timing
00423         int _profilingStopwatchAutoScaleEvaluate_NumIntervals;
00424 #endif // PROFILER
00425 
00426         public void AutoScaleEvaluate1(int[] rawIntArrayYData, int firstNewXIndex, int lastXIndex)
00427         {
00428             AutoScaleEvaluate(rawIntArrayYData, firstNewXIndex, lastXIndex, _chartArea1Name);
00429         }
00430 
00431         public void AutoScaleEvaluate2(int[] rawIntArrayYData, int firstNewXIndex, int lastXIndex)
00432         {
00433             AutoScaleEvaluate(rawIntArrayYData, firstNewXIndex, lastXIndex, _chartArea2Name);
00434         }
00435 
00436         public void AutoScaleEvaluate3(int[] rawIntArrayYData, int firstNewXIndex, int lastXIndex)
00437         {
00438             AutoScaleEvaluate(rawIntArrayYData, firstNewXIndex, lastXIndex, _chartArea3Name);
00439         }
00440 
00441         // 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.
00442         public void AutoScaleEvaluate(int[] rawIntArrayYData,
00443                 int firstNewXIndex,
00444                 int lastXIndex,
00445                 /*Chart chart,*/ string chartAreaName
00446                 // TODO1: OS24EVK-75 replace AutoScaleEvaluate dataFormatIs16bit2sComplement with if (DataFormat == DataFormats.Format16bit2sComplement) 
00447                 //bool dataFormatIs16bit2sComplement = false
00448             //string seriesName, 
00449             //int sampleNumber, 
00450             //ref int sampleNumberPreviousAutoscale
00451             )
00452         {
00453 #if PROFILER
00454             // TODO1: OS24EVK-54 profiling: capture max interval, number of intervals, cumulative interval
00455             _profilingStopwatchAutoScaleEvaluate.Start();
00456             _profilingStopwatchAutoScaleEvaluate_NumIntervals = _profilingStopwatchAutoScaleEvaluate_NumIntervals + 1;
00457 #endif // PROFILER
00458 
00459             // 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.
00460 
00461             // https://jira.maxim-ic.com/browse/OS24EVK-34 AutoScaleEvaluate() use _AutoScaleMarginXLeft; // ignore old data at left of graph
00462             // https://jira.maxim-ic.com/browse/OS24EVK-34 AutoScaleEvaluate() use _AutoScaleMarginXRight; // ignore new data at right of graph? probably 0 by default
00463             //const int firstPointIndex = 0;
00464             // advance the left and right X limits
00465             // https://jira.maxim-ic.com/browse/OS24EVK-48 Graph X axis - Replace hidden chkPlotTime with a menu option plotXAxisTimeToolStripMenuItem.Checked
00466             // 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) 
00467             // per Larry Skrenes 2014-11-24: graph scrolling needs to be smooth progression like a paper tape chart.
00468             // When (plotXAxisTimeToolStripMenuItem.Checked is false) graph scrolling is already smooth.
00469             // When (plotXAxisTimeToolStripMenuItem.Checked is true) graph scrolling moves in staccato 1-second steps. Larry hates this.
00470             int xWidth = _plotPoints * (_plotPointsToSkip + 1) * SampleAverage_n / (plotXAxisTime ? SampleRate_Hz : 1);
00471             // VERIFY: OS24EVK-73 Sample Avg 2 breaks chart scrolling -- xLastXIndex = lastXIndex * myMAX30101.SampleAverage_n
00472             int xLastXIndex = lastXIndex * SampleAverage_n / (plotXAxisTime ? SampleRate_Hz : 1);
00473             //int xWidth = _plotPoints * (_plotPointsToSkip + 1) * _cboSampleAvg / (false ? int.Parse(cboSampleRate.Text) : 1);
00474             //int xWidth = _plotPoints * (_plotPointsToSkip + 1) * _cboSampleAvg / (1);
00475             //int xWidth = _plotPoints * (_plotPointsToSkip + 1) * _cboSampleAvg;
00476             // https://jira.maxim-ic.com/browse/OS24EVK-15 Fix plot X axis jumping around (_plotWindowTime)
00477             // 0.0 --> 0  1  2  3  4  5
00478             // 0.0 --> 0_ 1  2  3  4  5
00479             // 0.0 --> 0__1  2  3  4  5
00480             // 0.0 --> 0__1_ 2  3  4  5
00481             // 0.0 --> 0__1__2  3  4  5
00482             // 0.0 --> 0__1__2_ 3  4  5
00483             // 0.0 --> 0__1__2__3  4  5
00484             // 0.0 --> 0__1__2__3_ 4  5
00485             // 0.0 --> 0__1__2__3__4  5
00486             // 0.0 --> 0__1__2__3__4_ 5
00487             // 0.0 --> 0__1__2__3__4__5
00488             // 0.5 -->    1__2__3__4__5_ 6
00489             // 1.0 -->    1__2__3__4__5__6
00490             // 1.5 -->       2__3__4__5__6_ 7
00491             // 2.0 -->       2__3__4__5__6__7
00492             // 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) 
00493             // per Larry Skrenes 2014-11-24: graph scrolling needs to be smooth progression like a paper tape chart.
00494             // When (plotXAxisTimeToolStripMenuItem.Checked is false) graph scrolling is already smooth.
00495             // When (plotXAxisTimeToolStripMenuItem.Checked is true) graph scrolling moves in staccato 1-second steps. Larry hates this.
00496             // VERIFY: OS24EVK-76 Autoscale delay time is affected by Sample Average - firstVisiblePointIndex
00497             //int firstVisiblePointIndex = lastXIndex - (_plotPoints * (_plotPointsToSkip + 1) * SampleAverage_n);
00498             int firstVisiblePointIndex = lastXIndex - (_plotPoints * (_plotPointsToSkip + 1));
00499             if (firstVisiblePointIndex < 0) { firstVisiblePointIndex = 0; }
00500             //
00501             // VERIFY: OS24EVK-73 Sample Avg 2 breaks chart scrolling -- xLastXIndex instead of lastXIndex
00502             int firstVisiblePointX = xLastXIndex - xWidth; // (int)chart.Series[0].Points[firstPointIndex].XValue;
00503             if (firstVisiblePointX < 0) { firstVisiblePointX = 0; }
00504             //int firstPointX = (int)System.Math.Ceiling(chart.Series[seriesName].Points[firstPointIndex].XValue);
00505             //if ((chart.Series[seriesName].Points[firstPointIndex].XValue) < 0.1)
00506             //{
00507             //    firstPointX = (int)System.Math.Floor(chart.Series[seriesName].Points[firstPointIndex].XValue);
00508             //}
00509             _chart.ChartAreas[chartAreaName].AxisX.Minimum = firstVisiblePointX;
00510             // 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) 
00511             //chart.ChartAreas[0].AxisX.Maximum = (int)System.Math.Ceiling((double)firstPointX + xWidth);
00512             _chart.ChartAreas[chartAreaName].AxisX.Maximum = firstVisiblePointX + xWidth;
00513 
00514             if (lastXIndex /* chart.Series[seriesName].Points.Count */ < 1)
00515             {
00516                 //int iMaxY_chart_default = (_AutoScaleMinSpanY * (1.0 + _AutoScaleMarginYTop + _AutoScaleMarginYBottom)) / _AutoScaleYmultiples;
00517                 //int _AutoscaleInitialChartMaxY = iMaxY_chart_default * _AutoScaleYmultiples;
00518                 //int _AutoscaleInitialChartMaxY =
00519                 //    (
00520                 //        (int)
00521                 //            ((_AutoScaleMinSpanY * (1.0 + _AutoScaleMarginYTop + _AutoScaleMarginYBottom)) / _AutoScaleYmultiples)
00522                 //    )
00523                 //    * (int)(_AutoScaleYmultiples);
00524                 //int _AutoscaleInitialChartMinY = 0;
00525                 _chart.ChartAreas[chartAreaName].AxisY.Maximum = _AutoscaleInitialChartMaxY;
00526                 _chart.ChartAreas[chartAreaName].AxisY.Minimum = _AutoscaleInitialChartMinY;
00527 #if PROFILER
00528                 // TODO1: OS24EVK-54 profiling: capture max interval, number of intervals, cumulative interval
00529                 _profilingStopwatchAutoScaleEvaluate.Stop();
00530 #endif // PROFILER
00531                 return;
00532             }
00533 
00534             //            // TODO1: OS24EVK-57 OS24EVK-54 scan new data rawIntArrayYData[firstNewIndex .. lastIndex]. As is this code only triggers if the last point is extreme.
00535             //            int newestPointIndex = lastXIndex; // chart.Series[seriesName].Points.Count - 1;
00536             //            int newestPointY = rawIntArrayYData[lastXIndex]; // (int)chart.Series[seriesName].Points[newestPointIndex].YValues[0];
00537             //            if ((newestPointY < chart.ChartAreas[chartAreaName].AxisY.Maximum)
00538             //                && (newestPointY > chart.ChartAreas[chartAreaName].AxisY.Minimum)
00539             //                )
00540             //            {
00541             //                // https://jira.maxim-ic.com/browse/OS24EVK-34 always re-evaluate chart when new data exceeds Minimum or Maximum
00542             //                // But if we just return at this point, then autoscale will never contract to fit smaller data series.
00543             //                //
00544             //                // https://jira.maxim-ic.com/browse/OS24EVK-34 Change _AutoScaleTimeInterval to _AutoScaleEveryNSamples and use sampleNumber to measure time
00545             //                // https://jira.maxim-ic.com/browse/OS24EVK-34 AutoScaleEvaluate() after every _AutoScaleEveryNSamples, re-scan data and fit chart to data
00546             //                // if ( interval has been less than _AutoScaleEveryNSamples ) { return; }
00547             //                if ((sampleNumber - sampleNumberPreviousAutoscale) < _AutoScaleEveryNSamples)
00548             //                {
00549             //#if PROFILER
00550             //                    // TODO1: OS24EVK-54 profiling: capture max interval, number of intervals, cumulative interval
00551             //                    _profilingStopwatchAutoScaleEvaluate.Stop();
00552             //#endif // PROFILER
00553             //                    return;
00554             //                }
00555             //            }
00556 
00557             int chartMinYValue = (int)_chart.ChartAreas[chartAreaName].AxisY.Minimum; // rawIntArrayYData[firstVisiblePointX] - 1;
00558             int chartMaxYValue = (int)_chart.ChartAreas[chartAreaName].AxisY.Maximum; // rawIntArrayYData[firstVisiblePointX] + 1;
00559             // VERIFY: OS24EVK-73 Sample Avg 2 breaks chart scrolling - firstVisiblePointIndex instead of firstVisiblePointX
00560             int dataMinYValue = rawIntArrayYData[firstVisiblePointIndex] - 1;
00561             int dataMaxYValue = rawIntArrayYData[firstVisiblePointIndex] + 1;
00562             for (int index = firstVisiblePointIndex; index <= lastXIndex; index++)
00563             {
00564                 int Y = rawIntArrayYData[index];
00565                 // VERIFY: OS24EVK-57 OS24EVK-54 improve chart throughput: AutoScaleEvaluate() dataFormatIs16bit2sComplement=true for Accelerometer X Y Z data
00566                 // VERIFY: OS24EVK-75 replace AutoScaleEvaluate dataFormatIs16bit2sComplement with if (DataFormat == DataFormats.Format16bit2sComplement) 
00567                 if (DataFormat == DataFormats.Format16bit2sComplement)
00568                 {
00569                     // VERIFY: OS24EVK-57 interpret rawX rawY rawZ as 16-bit 2's complement
00570                     if (Y > 0x8000)
00571                     {
00572                         Y = Y - 0x10000;
00573                     }
00574                 }
00575                 //else
00576                 //{
00577                 //    Y = Y + 0; // debug breakpoint for code path verification
00578                 //}
00579                 if (dataMinYValue > Y)
00580                 {
00581                     dataMinYValue = Y;
00582                 }
00583                 if (dataMaxYValue < Y)
00584                 {
00585                     dataMaxYValue = Y;
00586                 }
00587             }
00588             // rawIntArrayYData[firstIndex..lastIndex] spans the range from dataMinYValue .. dataMaxYValue
00589             double dataSpanY = (dataMaxYValue - dataMinYValue);
00590             double dataCenterY = (dataMaxYValue + dataMinYValue) / 2;
00591 
00592             bool isAllDataVisible = (
00593                 (chartMinYValue < dataMaxYValue) && (dataMaxYValue < chartMaxYValue)
00594                 &&
00595                 (chartMinYValue < dataMinYValue) && (dataMinYValue < chartMaxYValue)
00596                 );
00597             if (isAllDataVisible)
00598             {
00599                 // all data is within the chart limits, but is the chart sufficiently zoomed in?
00600                 double relativeDataSpan = (double)dataSpanY / (chartMaxYValue - chartMinYValue);
00601                 if (relativeDataSpan > 0.65)
00602                 {
00603                     // the current chart scale is good enough, do not update
00604 #if PROFILER
00605                     // TODO1: OS24EVK-54 profiling: capture max interval, number of intervals, cumulative interval
00606                     _profilingStopwatchAutoScaleEvaluate.Stop();
00607 #endif // PROFILER
00608                     return;
00609                 }
00610             }
00611             // https://jira.maxim-ic.com/browse/OS24EVK-34 AutoScaleEvaluate() use _AutoScaleMinSpanY; // minimum allowed span of chartMax-chartMin, to avoid focusing on LSB noise
00612             if (dataSpanY < _AutoScaleMinSpanY)
00613             {
00614                 // https://jira.maxim-ic.com/browse/OS24EVK-34 Autoscale - Minimum span (in case dataMax-dataMin is too small) to avoid focusing on LSB noise
00615                 dataSpanY = _AutoScaleMinSpanY;
00616             }
00617             else
00618             {
00619                 // 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
00620                 // https://jira.maxim-ic.com/browse/OS24EVK-34 AutoScaleEvaluate() use _AutoScaleMarginYTop; // chartMax empty area above dataMax
00621                 // https://jira.maxim-ic.com/browse/OS24EVK-34 AutoScaleEvaluate() use _AutoScaleMarginYBottom; // chartMin empty area below dataMin
00622                 // dataSpanY = dataSpanY * 1.25;
00623                 // dataSpanY = dataSpanY * (1.0 + 0.125 + 0.125);
00624                 dataSpanY = dataSpanY * (1.0 + _AutoScaleMarginYTop + _AutoScaleMarginYBottom);
00625 
00626                 // https://jira.maxim-ic.com/browse/OS24EVK-42 Autoscale tuning (Larry 2014-11-24) Round to nearest multiple of 100.
00627                 // - Y axis: No decimals. Round to nearest multiple of 100.
00628                 // private double _AutoScaleYmultiples = 100;
00629                 //int tempdataSpanY = (int)(dataSpanY / _AutoScaleYmultiples);
00630                 //dataSpanY = tempdataSpanY * _AutoScaleYmultiples;
00631                 //dataSpanY = (double)((int)dataSpanY / _AutoScaleYmultiples) * _AutoScaleYmultiples;
00632                 //dataCenterY = (double)((int)dataCenterY / _AutoScaleYmultiples) * _AutoScaleYmultiples;
00633             }
00634             // https://jira.maxim-ic.com/browse/OS24EVK-34 Autoscale - There are 5 vertical divisions; keep the minor axis tick values integer
00635             //int YaxisDivisions = (int)System.Math.Ceiling(dataSpanY / 6.0);
00636             //dataSpanY = YaxisDivisions * 6;
00637             // https://jira.maxim-ic.com/browse/OS24EVK-42 Autoscale tuning (Larry 2014-11-24) Round to nearest multiple of 100.
00638             // - Y axis: No decimals. Round to nearest multiple of 100.
00639             // private double _AutoScaleYmultiples = 100;
00640             //int tempdataCenterY = (int)(dataCenterY / (_AutoScaleYmultiples*2));
00641             //dataCenterY = tempdataCenterY * (_AutoScaleYmultiples*2);
00642 
00643             // chart.ChartAreas[0].AxisY.Maximum = dataMaxYValue;
00644             // chart.ChartAreas[0].AxisY.Minimum = dataMinYValue;
00645             //chart.ChartAreas[0].AxisY.Maximum = System.Math.Ceiling(dataCenterY + (dataSpanY / 2)); // dataMaxYValue;
00646             //chart.ChartAreas[0].AxisY.Minimum = System.Math.Floor(dataCenterY - (dataSpanY / 2)); // dataMinYValue;
00647             // https://jira.maxim-ic.com/browse/OS24EVK-42 Autoscale tuning (Larry 2014-11-24) Round to nearest multiple of 100.
00648             // - Y axis: No decimals. Round to nearest multiple of 100.
00649             // private double _AutoScaleYmultiples = 100;
00650             int iMaxY_target = (int)((dataCenterY + (dataSpanY / 2)) / _AutoScaleYmultiples);
00651             int iMinY_target = (int)((dataCenterY - (dataSpanY / 2)) / _AutoScaleYmultiples);
00652 
00653             double dampingConstantK = 0.2;
00654             int iMaxY_damped = iMaxY_target;
00655             int iMinY_damped = iMinY_target;
00656             if (double.IsNaN(_chart.ChartAreas[chartAreaName].AxisY.Maximum) == false)
00657             {
00658                 int iMaxY_chart = (int)(_chart.ChartAreas[chartAreaName].AxisY.Maximum / _AutoScaleYmultiples);
00659                 int iMinY_chart = (int)(_chart.ChartAreas[chartAreaName].AxisY.Minimum / _AutoScaleYmultiples);
00660                 iMaxY_damped = (int)((dampingConstantK * (double)iMaxY_target) + ((1 - dampingConstantK) * (double)iMaxY_chart) + 0.5);
00661                 iMinY_damped = (int)((dampingConstantK * (double)iMinY_target) + ((1 - dampingConstantK) * (double)iMinY_chart));
00662             }
00663             double maxY = iMaxY_target * _AutoScaleYmultiples;
00664             double minY = iMinY_target * _AutoScaleYmultiples;
00665             _chart.ChartAreas[chartAreaName].AxisY.Maximum = maxY;
00666             _chart.ChartAreas[chartAreaName].AxisY.Minimum = minY;
00667             // if chart.ChartAreas[chartAreaName].AxisY.Maximum  is NaN then just assign maxY
00668             //if (double.IsNaN(chart.ChartAreas[chartAreaName].AxisY.Maximum))
00669             //{
00670             //    chart.ChartAreas[chartAreaName].AxisY.Maximum = maxY;
00671             //    chart.ChartAreas[chartAreaName].AxisY.Minimum = minY;
00672             //}
00673             //else
00674             //{
00675             //    chart.ChartAreas[chartAreaName].AxisY.Maximum = (dampingConstantK * maxY) + ((1 - dampingConstantK) * chart.ChartAreas[chartAreaName].AxisY.Maximum);
00676             //    chart.ChartAreas[chartAreaName].AxisY.Minimum = (dampingConstantK * minY) + ((1 - dampingConstantK) * chart.ChartAreas[chartAreaName].AxisY.Minimum);
00677             //}
00678 
00679 #if PROFILER
00680             // TODO1: OS24EVK-54 profiling: capture max interval, number of intervals, cumulative interval
00681             _profilingStopwatchAutoScaleEvaluate.Stop();
00682 #endif // PROFILER
00683         }
00684 
00685         /// <summary>
00686         /// Raw data format
00687         /// </summary>
00688         public DataFormats  DataFormat = DataFormats .FormatUnsigned;
00689 
00690         public enum DataFormats 
00691         {
00692             /// <summary>
00693             /// Interpret raw data as unsigned values
00694             /// </summary>
00695             FormatUnsigned,
00696 
00697             /// <summary>
00698             /// Interpret raw data as 16-bit, signed 2's complement values
00699             /// </summary>
00700             Format16bit2sComplement,
00701         }
00702 
00703     } // public class MedicalChartHelper
00704     
00705 } // namespace Maxim.MAX30101