repo time
Dependencies: mbed MAX14720 MAX30205 USBDevice
HspGuiSourceV301/HSPGui/CustomControls/OpticalView.cs
- Committer:
- darienf
- Date:
- 2021-04-06
- Revision:
- 20:6d2af70c92ab
File content as of revision 20:6d2af70c92ab:
/******************************************************************************* * Copyright (C) 2016 Maxim Integrated Products, Inc., All rights Reserved. * * This software is protected by copyright laws of the United States and * of foreign countries. This material may also be protected by patent laws * and technology transfer regulations of the United States and of foreign * countries. This software is furnished under a license agreement and/or a * nondisclosure agreement and may only be used or reproduced in accordance * with the terms of those agreements. Dissemination of this information to * any party or parties not specified in the license agreement and/or * nondisclosure agreement is expressly prohibited. * * The above copyright notice and this permission notice shall be included * in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. * IN NO EVENT SHALL MAXIM INTEGRATED BE LIABLE FOR ANY CLAIM, DAMAGES * OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR * OTHER DEALINGS IN THE SOFTWARE. * * Except as contained in this notice, the name of Maxim Integrated * Products, Inc. shall not be used except as stated in the Maxim Integrated * Products, Inc. Branding Policy. * * The mere transfer of this software does not imply any licenses * of trade secrets, proprietary technology, copyrights, patents, * trademarks, maskwork rights, or any other form of intellectual * property whatsoever. Maxim Integrated Products, Inc. retains all * ownership rights. ******************************************************************************* */ #define USE_MEDICALCHARTHELPER //#define CES_DEMO using System; using System.Collections.Generic; using System.Collections; using System.ComponentModel; using System.Drawing; using System.Data; using System.Linq; using System.Text; using System.Windows.Forms; using RPCSupport.Streaming; using Maxim.Charting; using System.Threading; using HealthSensorPlatform.View; using System.Windows.Forms.DataVisualization.Charting; // DEBUG using System.IO; namespace HealthSensorPlatform.CustomControls { public partial class OpticalView : UserControl, IDeviceView, IOpticalAlgorithmView { /* Constant Fields */ static string StartString = "Start Monitor"; static string StopString = "Stop Monitor"; const int ChartTime = 4; // 4 seconds /* Fields */ private bool connected = false; private bool streaming = false; private RPCSupport.RPCClient rpcClient; //private MedicalChartHelper medicalChartLED; //private MedicalChartHelper medicalChartAccel; private AutoScaleCalculator fifo1Calc; private AutoScaleCalculator fifo2Calc; private AutoScaleCalculator fifo3Calc; eStreamMode streamMode; byte fifo_waterlevel_mark = 0x0f; byte sample_avg; byte sample_rate; byte pulse_width; byte red_led_current; byte ir_led_current; byte green_led_current; byte slot_1; byte slot_2; byte slot_3; byte slot_4; // Heart Rate Sensor int points; // Number of x-axis points for a plot int sampleRate; // Human readable sample rate int sampleAverage; // Human readable number of average points // LIS2HD int sampleRateAccel; int pointsAccel; EventHandler<PartialArrayIntAvailableEventArgs> appendChart; // Allows event to be unregistered Dictionary<string, MedianFilter> intervalDict = new Dictionary<string, MedianFilter>(); Dictionary<string, AutoScaleCalculator> chartScaleDict = new Dictionary<string, AutoScaleCalculator>(); List<Control> streamingControls = new List<Control>(); #if CES_DEMO // Algorithm Data Queue red; Queue ir; Queue led; // struct to run algorithms AlgorithmMobileBU.AlgorithmConfiguration config; AlgorithmMobileBU.AlgorithmOutput output; int algoCounter = 0; #endif // DEBUG //StreamWriter file = new StreamWriter("hsp_output_data.csv"); /* Constructors */ public OpticalView() { InitializeComponent(); OpticalSensorInitControls(); cboSampleRate.SelectedIndexChanged += new EventHandler(cboSRPWLed_SelectedIndexChanged); cboPulseWidth.SelectedIndexChanged += new EventHandler(cboSRPWLed_SelectedIndexChanged); cboRedLED.SelectedIndexChanged += new EventHandler(cboSRPWLed_SelectedIndexChanged); cboIRLED.SelectedIndexChanged += new EventHandler(cboSRPWLed_SelectedIndexChanged); cboGreenLED.SelectedIndexChanged += new EventHandler(cboSRPWLed_SelectedIndexChanged); cboSampleRate.SelectedIndexChanged += new EventHandler(cboSampleRate_SelectedIndexChanged); cboSampleAvg.SelectedIndexChanged += new EventHandler(cboSampleRate_SelectedIndexChanged); streamingControls.AddRange(new Control[] { panel1, maximGroupBoxSettings, maximGroupBoxLEDcurrents, maximGroupBoxLEDTimingSlots, btnDefaults }); #if CES_DEMO // Algorithm red = new Queue(); ir = new Queue(); led = new Queue(); config = new AlgorithmMobileBU.AlgorithmConfiguration(); output = new AlgorithmMobileBU.AlgorithmOutput(); config.snrNoFingerThreshold = -9; config.acLowPerfusionThreshold = 950; config.isAGC = 0; //updateAlgorithm(); lblHeartRate.Visible = true; lblHeartRateText.Visible = true; #endif } /* Delegates */ //public delegate void StreamingStartStopEventHandler(StreamingStartStopEventArgs e); /* Events */ //public event StreamingStartStopEventHandler StreamingStartStop; public event EventHandler<StreamingStartStopEventArgs> StreamingStartStop; /* Enums */ public enum eStreamMode { eHR, eSPO2, eMulti } /* Properties */ public RPCSupport.RPCClient RPCClient { set { rpcClient = value; appendChart = new EventHandler<PartialArrayIntAvailableEventArgs>(On_AppendChart); rpcClient.streaming.PartialArrayIntAvailable += appendChart; } } public bool Connected { get { return connected; } set { connected = value; if (connected == false) if (btnMonitoring.Text == StopString) streamingStartStop(); } } public int AccelSampleRate { get { return sampleRateAccel; } } public int OpticalSampleRate { get { return sampleRate / sampleAverage; } } public eStreamMode ModeConfiguration { get { return streamMode; } } /* Methods */ // Clean up streaming public void Close() { rpcClient.streaming.PartialArrayIntAvailable -= appendChart; // Stop event from firing // Disable streaming if enabled if (btnMonitoring.Text == StopString) streamingStartStop(); } public void DisplayAlgorithmResult(double heartRateBPM, bool heartRateBPMValid, double heartRateBPMSignalStrength, double spO2Percent, bool spO2PercentValid, double spO2PercentSignalStrength) { // 100Hz only - TODO if (OpticalSampleRate != 100) return; if (heartRateBPMValid) { lblHeartRate.ForeColor = SystemColors.ControlText; lblHeartRate.Text = string.Format("{0}", heartRateBPM * (sampleRate / 100.0)); // 100.0 for default sample rate } else { lblHeartRate.ForeColor = Color.Gray; } if (spO2PercentValid) { lblSpO2.ForeColor = SystemColors.ControlText; lblSpO2.Text = string.Format("{0:0.0}", spO2Percent); } else { lblSpO2.ForeColor = Color.Gray; } } public void DisplayAlgorithmReset() { lblHeartRate.Text = "----"; lblHeartRate.ForeColor = Color.Gray; lblSpO2.Text = "----"; lblSpO2.ForeColor = Color.Gray; } System.Windows.Forms.Timer timer = new System.Windows.Forms.Timer(); void InitGraphs() { /* medicalChartLED = new MedicalChartHelper(chartLED, "ChartArea1Red", "SeriesRed", "Red ADC Code", "ChartArea2IR", "SeriesIR", "IR ADC Code", "ChartArea3Green", "SeriesGreen", "Green ADC Code", MedicalChartHelper.DataFormats.FormatUnsigned); medicalChartLED._plotPoints = 200; medicalChartAccel = new MedicalChartHelper(chartAccel, "ChartArea4AccelX", "SeriesAccelX", "Accelerometer X", "ChartArea5AccelY", "SeriesAccelY", "Accelerometer Y", "ChartArea6AccelZ", "SeriesAccelZ", "Accelerometer Z", MedicalChartHelper.DataFormats.Format16bit2sComplement); */ MedianFilter fifo1 = new MedianFilter(10); MedianFilter fifo2 = new MedianFilter(10); MedianFilter fifo3 = new MedianFilter(10); MedianFilter accelx = new MedianFilter(10); MedianFilter accely = new MedianFilter(10); MedianFilter accelz = new MedianFilter(10); intervalDict.Add("ChartArea1Red", fifo1); intervalDict.Add("ChartArea2IR", fifo2); intervalDict.Add("ChartArea3Green", fifo3); intervalDict.Add("ChartArea4AccelX", accelx); intervalDict.Add("ChartArea5AccelY", accely); intervalDict.Add("ChartArea6AccelZ", accelz); fifo1Calc = new AutoScaleCalculator(0, 90000); fifo1Calc.Minimum = 0; fifo1Calc.Maximum = Math.Pow(2, 18); fifo1Calc.Intervals = 5; fifo1Calc.MinimumRange = 200; fifo1Calc.ScaleTrigger = 0.8; fifo1Calc.RescaleTargetRange = 0.25; chartScaleDict.Add("ChartArea1Red", fifo1Calc); fifo2Calc = new AutoScaleCalculator(0, 90000); fifo2Calc.Minimum = 0; fifo2Calc.Maximum = Math.Pow(2, 18); fifo2Calc.Intervals = 5; fifo2Calc.MinimumRange = 200; fifo2Calc.ScaleTrigger = 0.8; fifo2Calc.RescaleTargetRange = 0.25; chartScaleDict.Add("ChartArea2IR", fifo2Calc); fifo3Calc = new AutoScaleCalculator(0, 90000); fifo3Calc.Minimum = 0; fifo3Calc.Maximum = Math.Pow(2, 18); fifo3Calc.Intervals = 5; fifo3Calc.MinimumRange = 200; fifo3Calc.ScaleTrigger = 0.8; fifo3Calc.RescaleTargetRange = 0.25; chartScaleDict.Add("ChartArea3Green", fifo2Calc); AutoScaleCalculator accelXCalc = new AutoScaleCalculator(-10000, 10000); accelXCalc.Minimum = -32768; accelXCalc.Maximum = 32767; accelXCalc.Intervals = 5; accelXCalc.MinimumRange = 500; accelXCalc.ScaleTrigger = 0.8; accelXCalc.RescaleTargetRange = 0.25; chartScaleDict.Add("ChartArea4AccelX", accelXCalc); AutoScaleCalculator accelYCalc = new AutoScaleCalculator(-10000, 10000); accelYCalc.Minimum = -32768; accelYCalc.Maximum = 32767; accelYCalc.Intervals = 5; accelYCalc.MinimumRange = 500; accelYCalc.ScaleTrigger = 0.8; accelYCalc.RescaleTargetRange = 0.25; chartScaleDict.Add("ChartArea5AccelY", accelYCalc); AutoScaleCalculator accelZCalc = new AutoScaleCalculator(-10000, 10000); accelZCalc.Minimum = -32768; accelZCalc.Maximum = 32767; accelZCalc.Intervals = 5; accelZCalc.MinimumRange = 500; accelZCalc.ScaleTrigger = 0.8; accelZCalc.RescaleTargetRange = 0.25; chartScaleDict.Add("ChartArea6AccelZ", accelZCalc); /* Random rand = new Random(); int[] data1 = new int[10]; int[] data2 = new int[10]; int[] data3 = new int[10]; double[] data1_ = new double[10]; double[] data2_ = new double[10]; double[] data3_ = new double[10]; for (int i = 0; i < data1.Length; i++) { data1[i] = rand.Next(10); data2[i] = rand.Next(10); data3[i] = rand.Next(10); data1_[i] = rand.NextDouble() * 10; data2_[i] = rand.NextDouble() * 10; data3_[i] = rand.NextDouble() * 10; } */ /* int[] dummyData = new int[] { 0 }; medicalChartLED.AppendDataChartArea1(dummyData, 0, 0); medicalChartLED.AppendDataChartArea2(dummyData, 0, 0); medicalChartLED.AppendDataChartArea3(dummyData, 0, 0); //medicalChartAccel._xCount1++; //medicalChartAccel._xCount2++; //medicalChartAccel._xCount3++; medicalChartAccel.AppendDataChartArea1(dummyData, 0, 0); medicalChartAccel.AppendDataChartArea2(dummyData, 0, 0); medicalChartAccel.AppendDataChartArea3(dummyData, 0, 0); */ setupGraphs(); chartLED.ChartAreas["ChartArea3Green"].Visible = false; timer.Interval = 50; //timer.Tick += new EventHandler(timer_Tick); } void setupGraphs() { chartLED.SuspendLayout(); chartAccel.SuspendLayout(); /* Initialize Empty Chart Grid */ chartLED.Series["SeriesRed"].Points.Clear(); chartLED.Series["SeriesIR"].Points.Clear(); chartLED.Series["SeriesGreen"].Points.Clear(); chartAccel.Series["SeriesAccelX"].Points.Clear(); chartAccel.Series["SeriesAccelY"].Points.Clear(); chartAccel.Series["SeriesAccelZ"].Points.Clear(); // Clear labels chartLED.ChartAreas["ChartArea1Red"].AxisX.CustomLabels.Clear(); chartLED.ChartAreas["ChartArea2IR"].AxisX.CustomLabels.Clear(); chartLED.ChartAreas["ChartArea3Green"].AxisX.CustomLabels.Clear(); chartAccel.ChartAreas["ChartArea4AccelX"].AxisX.CustomLabels.Clear(); chartAccel.ChartAreas["ChartArea5AccelY"].AxisX.CustomLabels.Clear(); chartAccel.ChartAreas["ChartArea6AccelZ"].AxisX.CustomLabels.Clear(); chartLED.Series["SeriesRed"].Points.Add(0); chartLED.Series["SeriesIR"].Points.Add(0); chartLED.Series["SeriesGreen"].Points.Add(0); chartAccel.Series["SeriesAccelX"].Points.Add(0); chartAccel.Series["SeriesAccelY"].Points.Add(0); chartAccel.Series["SeriesAccelZ"].Points.Add(0); // Initialize Plots for OS24 chartLED.ChartAreas["ChartArea1Red"].AxisX.Interval = points / ChartTime; chartLED.ChartAreas["ChartArea2IR"].AxisX.Interval = points / ChartTime; chartLED.ChartAreas["ChartArea3Green"].AxisX.Interval = points / ChartTime; chartLED.ChartAreas["ChartArea1Red"].AxisX.Maximum = points; chartLED.ChartAreas["ChartArea2IR"].AxisX.Maximum = points; chartLED.ChartAreas["ChartArea3Green"].AxisX.Maximum = points; chartLED.ChartAreas["ChartArea1Red"].AxisY.Maximum = 90000; chartLED.ChartAreas["ChartArea1Red"].AxisY.Minimum = 0; chartLED.ChartAreas["ChartArea2IR"].AxisY.Maximum = 90000; chartLED.ChartAreas["ChartArea2IR"].AxisY.Minimum = 0; chartLED.ChartAreas["ChartArea3Green"].AxisY.Maximum = 90000; chartLED.ChartAreas["ChartArea3Green"].AxisY.Minimum = 0; // Initialize Plots for LIS2HD sampleRateAccel = sampleRate; //pointsAccel = (sampleRateAccel / sampleAverage) * ChartTime; pointsAccel = (sampleRateAccel) * ChartTime; chartAccel.ChartAreas["ChartArea4AccelX"].AxisX.Interval = pointsAccel / ChartTime; chartAccel.ChartAreas["ChartArea5AccelY"].AxisX.Interval = pointsAccel / ChartTime; chartAccel.ChartAreas["ChartArea6AccelZ"].AxisX.Interval = pointsAccel / ChartTime; chartAccel.ChartAreas["ChartArea4AccelX"].AxisX.Maximum = pointsAccel; chartAccel.ChartAreas["ChartArea5AccelY"].AxisX.Maximum = pointsAccel; chartAccel.ChartAreas["ChartArea6AccelZ"].AxisX.Maximum = pointsAccel; chartAccel.ChartAreas["ChartArea4AccelX"].AxisY.Minimum = -10000; chartAccel.ChartAreas["ChartArea5AccelY"].AxisY.Minimum = -10000; chartAccel.ChartAreas["ChartArea6AccelZ"].AxisY.Minimum = -10000; chartAccel.ChartAreas["ChartArea4AccelX"].AxisY.Maximum = 10000; chartAccel.ChartAreas["ChartArea5AccelY"].AxisY.Maximum = 10000; chartAccel.ChartAreas["ChartArea6AccelZ"].AxisY.Maximum = 10000; // Set X-axis labels in seconds for (int i = 0; i < ChartTime + 1; i++) { chartLED.ChartAreas["ChartArea1Red"].AxisX.CustomLabels.Add( (sampleRate / sampleAverage) * (2 * i - 1) / 2, (sampleRate / sampleAverage) * (2 * i + 1) / 2, i.ToString()); chartLED.ChartAreas["ChartArea2IR"].AxisX.CustomLabels.Add( (sampleRate / sampleAverage) * (2 * i - 1) / 2, (sampleRate / sampleAverage) * (2 * i + 1) / 2, i.ToString()); chartLED.ChartAreas["ChartArea3Green"].AxisX.CustomLabels.Add( (sampleRate / sampleAverage) * (2 * i - 1) / 2, (sampleRate / sampleAverage) * (2 * i + 1) / 2, i.ToString()); chartAccel.ChartAreas["ChartArea4AccelX"].AxisX.CustomLabels.Add(sampleRateAccel * (2 * i - 1) / 2, sampleRateAccel * (2 * i + 1) / 2, i.ToString()); chartAccel.ChartAreas["ChartArea5AccelY"].AxisX.CustomLabels.Add(sampleRateAccel * (2 * i - 1) / 2, sampleRateAccel * (2 * i + 1) / 2, i.ToString()); chartAccel.ChartAreas["ChartArea6AccelZ"].AxisX.CustomLabels.Add(sampleRateAccel * (2 * i - 1) / 2, sampleRateAccel * (2 * i + 1) / 2, i.ToString()); } chartLED.ResumeLayout(); chartLED.Invalidate(); chartAccel.ResumeLayout(); chartAccel.Invalidate(); } void timer_Tick(object sender, EventArgs e) { /*Single tmpData = dynamicData[0].Y; for (int i = 0; i < dynamicData.Length - 1; i++) { dynamicData[i].Y = dynamicData[i + 1].Y; } dynamicData[dynamicData.Length - 1].Y = tmpData;*/ //PointF[] newData = GenerateDynamicData(100, 5); //UpdateLedChart("SeriesRed", newData); //UpdateLedChart("SeriesIR", newData); //UpdateLedChart("SeriesGreen", newData); } /*private void UpdateLedChart(string seriesName, PointF[] data) { int count; chartLED.SuspendLayout(); //chartLED.Series[seriesName].Points.Clear(); foreach (PointF pt in data) { //chartLED.Series[seriesName].Points.AddXY(pt.X, pt.Y); chartLED.Series[seriesName].Points.Add(pt.Y); } for (count = chartLED.Series[seriesName].Points.Count; count > points; count = chartLED.Series[seriesName].Points.Count) { chartLED.Series[seriesName].Points.RemoveAt(0); } chartLED.ResumeLayout(); chartLED.Invalidate(); }*/ private void UpdateXYZChart(string seriesName, int[] data) { int datapoint; int count; chartAccel.SuspendLayout(); //chartLED.Series[seriesName].Points.Clear(); foreach (int val in data) { //chartLED.Series[seriesName].Points.AddXY(pt.X, pt.Y); datapoint = val; // Two's complement conversion if (val > 0x8000) { datapoint = datapoint - 0x10000; } chartAccel.Series[seriesName].Points.Add(datapoint); } for (count = chartAccel.Series[seriesName].Points.Count; count > pointsAccel; count = chartAccel.Series[seriesName].Points.Count) { chartAccel.Series[seriesName].Points.RemoveAt(0); } //chartAccel.ResetAutoValues(); chartAccel.ResumeLayout(); chartAccel.Invalidate(); } private void UpdateLedChart(string seriesName, int[] data) { int[] dataPoints = new int[points]; int j = 0; int count; chartLED.SuspendLayout(); //chartLED.Series[seriesName].Points.Clear(); // Update Chart with new data if (data.Length > points) j = data.Length - points; for (; j < data.Length; j++ ) { chartLED.Series[seriesName].Points.Add(data[j]); #if CES_DEMO if (seriesName.CompareTo("SeriesRed") == 0) red.Enqueue(data[j]); else if (seriesName.CompareTo("SeriesIR") == 0) ir.Enqueue(data[j]); else if (seriesName.CompareTo("SeriesGreen") == 0) led.Enqueue(data[j]); #endif } for (count = chartLED.Series[seriesName].Points.Count; count > points; count = chartLED.Series[seriesName].Points.Count) { chartLED.Series[seriesName].Points.RemoveAt(0); } } private void UpdateLedChartScale(string chartName, string seriesName) { int min = Int32.MaxValue, max = 0; int i, j; int[] data = new int[points]; int graphCount; //int maxRound, minRound; graphCount = chartLED.Series[seriesName].Points.Count; for (j = 0; j < graphCount; j++ ) { data[j] = (int)chartLED.Series[seriesName].Points[j].YValues[0]; } //if (data.Length < sampleRate) // return; // not enough data to draw graph /*else*/ if (graphCount < (sampleRate / sampleAverage) * 2) // look back 2 seconds { i = 0; } else // look back 2 seconds { i = graphCount - (sampleRate / sampleAverage) * 2; } min = data[i]; max = data[i]; for (; i < graphCount; i++ ) { if (data[i] < min && data[i] != 0) min = data[i]; else if (data[i] > max) max = data[i]; } /* MedianFilter filter; intervalDict.TryGetValue(chartName, out filter); var minMax = chartInterval(min, max, chartLED.ChartAreas[chartName].AxisY.Minimum, chartLED.ChartAreas[chartName].AxisY.Maximum, filter); minRound = minMax.Item1; maxRound = minMax.Item2; // For no finger on sensor if (max < 500) max = 500; */ AutoScaleCalculator chartScale; chartScaleDict.TryGetValue(chartName, out chartScale); var minMax = chartScale.Interval(min, max); // Round to nearest 100 with averaging //chartLED.ChartAreas[chartName].AxisY.Maximum = (chartLED.ChartAreas[chartName].AxisY.Maximum + (max / 100 + 1) * 100)/2; //chartLED.ChartAreas[chartName].AxisY.Maximum = (((int)chartLED.ChartAreas[chartName].AxisY.Maximum + max)/200 + 1) * 100; //chartLED.ChartAreas[chartName].AxisY.Minimum = (chartLED.ChartAreas[chartName].AxisY.Minimum + (min / 100 - 1) * 100)/2; //chartLED.ChartAreas[chartName].AxisY.Minimum = (((int)chartLED.ChartAreas[chartName].AxisY.Minimum + min)/200 - 1) * 100; chartLED.ChartAreas[chartName].AxisY.Minimum = minMax.Item1; chartLED.ChartAreas[chartName].AxisY.Maximum = minMax.Item2; chartLED.ResumeLayout(); chartLED.Invalidate(); } private void UpdateXYZChartScale(string chartName, string seriesName) { int min = Int32.MaxValue, max = Int32.MinValue; int i, j; int maxRound, minRound; int graphCount; int[] data = new int[pointsAccel]; graphCount = chartAccel.Series[seriesName].Points.Count; // Save all points from plot to data[] for (j = 0; j < graphCount; j++) { data[j] = (int)chartAccel.Series[seriesName].Points[j].YValues[0]; } if (graphCount < (sampleRateAccel / sampleAverage) * 2) // look back 3 seconds i = 0; else i = graphCount - (sampleRateAccel / sampleAverage) * 2; // Find min/max min = data[i]; max = data[i]; for (; i < graphCount; i++) { if (data[i] < min) min = data[i]; else if (data[i] > max) max = data[i]; } /* // Sane defaults if (min == max) { min = 0; max = 100; } MedianFilter filter; intervalDict.TryGetValue(chartName, out filter); var minMax = chartInterval(min, max, chartAccel.ChartAreas[chartName].AxisY.Minimum, chartAccel.ChartAreas[chartName].AxisY.Maximum, filter); minRound = minMax.Item1; maxRound = minMax.Item2; if (maxRound > 32768) maxRound = 33000; if (minRound < -32768) minRound = -33000; */ AutoScaleCalculator chartScale; chartScaleDict.TryGetValue(chartName, out chartScale); var minMax = chartScale.Interval(min, max); minRound = (int)minMax.Item1; maxRound = (int)minMax.Item2; // Set the Min and Max for Y Axis //chartAccel.ChartAreas[chartName].AxisY.Maximum = (((int)chartAccel.ChartAreas[chartName].AxisY.Maximum + max) / 200 + 1) * 100; //chartAccel.ChartAreas[chartName].AxisY.Minimum = (((int)chartAccel.ChartAreas[chartName].AxisY.Minimum + min) / 200 - 1) * 100; chartAccel.ChartAreas[chartName].AxisY.Maximum = maxRound; chartAccel.ChartAreas[chartName].AxisY.Minimum = minRound; //System.Diagnostics.Debug.Print(chartName + " count: " + graphCount + " minRound: " + minRound + " maxRound: " + maxRound + " min: " + min + " max: " + max); chartAccel.ResumeLayout(); chartAccel.Invalidate(); } /*private PointF[] dynamicData; private static PointF[] GenerateDynamicData(double amplitude, int sampleCount) { Random rnd = new Random(); PointF[] points = new PointF[sampleCount]; points[0] = new PointF(0, (Single)(amplitude * rnd.NextDouble())); for (int i = 1; i < sampleCount; i++) { points[i] = new PointF(i, (Single)(points[i - 1].Y + (amplitude / 10 * (0.5 - rnd.NextDouble())))); if (points[i].Y > amplitude) points[i].Y = (Single)amplitude; } return (points); }*/ /// <summary> /// Calculate rounded minimum and maximum for chart data /// </summary> /// <param name="minimum"></param> /// <param name="maximum"></param> /// <param name="chartInterval"></param> /// <returns></returns> private static Tuple<int, int> chartInterval(int minimum, int maximum, double chartMinimum, double chartMaximum, MedianFilter chartInterval) { int delta, interval, roundInterval, roundCenter; double mag, magPower, magMostSigDigit; int center; // 10% and 90% range int chartRange = (int)(chartMaximum - chartMinimum); int dataRange = maximum - minimum; if (minimum < chartMinimum + chartRange * 0.10 || maximum > chartMaximum - chartRange * 0.10 || dataRange > 0.9 * chartRange || dataRange < 0.1 * chartRange) { delta = (int)((maximum - minimum)); interval = delta * (100 / 35) / 5; // delta * 4 = 25% * 4 = 100 (full chart scale) center = minimum + (maximum - minimum) / 2; mag = Math.Floor(Math.Log10(interval)); magPower = Math.Pow(10, mag); magMostSigDigit = (int)(interval / (double)magPower + 0.5); if (magMostSigDigit >= 5.0) magMostSigDigit = 10; else if (magMostSigDigit >= 2.0) magMostSigDigit = 5; else if (magMostSigDigit >= 1.0) magMostSigDigit = 2; roundInterval = (int)(magMostSigDigit * magPower); if (roundInterval < 100) { roundInterval = 100; } roundInterval = chartInterval.Filter(roundInterval); roundCenter = ((center + 100) / 200) * 200; int roundMin = (int)(roundCenter - roundInterval * 2.5); int roundMax = (int)(roundCenter + roundInterval * 2.5); if (roundInterval > 104858) { roundMin = 0; roundMax = 550000; } /*else { if (roundMin < 0) // || roundMax > 524288) { roundMin = 0; roundMax = roundInterval * 5; } }*/ return new Tuple<int, int>(roundMin, roundMax); } return new Tuple<int, int>((int)chartMinimum, (int)chartMaximum); } void OpticalSensorInitControls() { cboMode.Items.AddRange(new string[] { "HR","SPO2","LED" }); cboSampleRate.Items.AddRange(new string[] { "50", "100", "200", "400" }); cboSampleAvg.Items.AddRange(new string[] { "1", "2", "4", "8", "16", "32" }); cboPulseWidth.Items.AddRange(new string[] { "69", "118", "215", "411" }); cboADCFSrangenA.Items.AddRange(new string[] { "2048", "4096", "8192", "16384" }); double slopeInt = 25.0 / 128.0; for (int index = 0; index < 256; index++) { string str = index < 0x0F ? (0.2 * index).ToString() : (slopeInt * index + slopeInt).ToString(); cboIRLED.Items.Add(str); cboRedLED.Items.Add(str); cboGreenLED.Items.Add(str); cboPilotPA.Items.Add(str); } OpticalSensorDefaults(); calculateAvgCurrent(); InitGraphs(); } public void OpticalSensorDefaults() { cboMode.SelectedItem = "SPO2"; cboRedLED.SelectedItem = "10.15625"; cboIRLED.SelectedItem = "10.15625"; cboGreenLED.SelectedItem = "10.15625"; cboSampleRate.SelectedItem = "100"; cboPulseWidth.SelectedItem = "411"; cboSampleAvg.SelectedItem = "1"; cboADCFSrangenA.SelectedItem = "8192"; cboLEDslot1.SelectedIndex = 1; cboLEDslot2.SelectedIndex = 2; cboLEDslot3.SelectedIndex = 3; cboLEDslot4.SelectedIndex = 0; // Used to generate the initial chart grid sample_rate = (byte)cboSampleRate.SelectedIndex; sample_avg = (byte)cboSampleAvg.SelectedIndex; sampleRate = (1 << sample_rate) * 50; // (1 << sample_rate) = 2^sample_rate sampleAverage = (1 << sample_avg); // 2^sample_avg points = (sampleRate / sampleAverage) * ChartTime; // 4 seconds, 2^sample_av sampleRateAccel = sampleRate; pointsAccel = (sampleRateAccel / sampleAverage) * ChartTime; } private void cbo_IndexChanged(object sender, EventArgs e) { if ((MaximStyle.MaximComboBox)sender == cboMode) { bool HRmode = (cboMode.Text == "HR"); bool SPO2mode = (cboMode.Text == "SPO2"); bool multiLEDmode = (cboMode.Text == "LED"); //labelcboRedLED.Enabled = myMAX30101.valid_Red; //cboRedLED.Enabled = myMAX30101.valid_Red; bool valid_IR = (multiLEDmode || SPO2mode); bool valid_Green = multiLEDmode; bool valid_Red = multiLEDmode; labelcboIRLED.Enabled = valid_IR; cboIRLED.Enabled = valid_IR; labelcboGreenLED.Enabled = valid_Green; cboGreenLED.Enabled = valid_Green; cboLEDslot1.Enabled = multiLEDmode; cboLEDslot2.Enabled = multiLEDmode; cboLEDslot3.Enabled = multiLEDmode; cboLEDslot4.Enabled = multiLEDmode; { // Hide unused LED channel graphs // | mode | ChartArea1Red | ChartArea2IR | ChartArea3Green | // | LED | visible | visible | visible | MAX30101 only // | SPO2 | visible | visible | hidden | // | HR | visible | hidden | hidden | MAX30101: Red only. MAX30100: IR only. valid_Red = true; valid_IR = multiLEDmode || SPO2mode; valid_Green = multiLEDmode; chartLED.ChartAreas["ChartArea1Red"].Visible = valid_Red; chartLED.ChartAreas["ChartArea2IR"].Visible = valid_IR; chartLED.ChartAreas["ChartArea3Green"].Visible = valid_Green; } } } public void On_AppendChart(object sender, PartialArrayIntAvailableEventArgs e) { if (streaming) { if ((e.reportID & 0xF0) == PartialArrayIntAvailableEventArgs.PACKET_MAX30101) { if (e.array1.Length != 0) { UpdateLedChart("SeriesRed", e.array1); UpdateLedChartScale("ChartArea1Red", "SeriesRed"); } if (e.array2.Length != 0) { UpdateLedChart("SeriesIR", e.array2); UpdateLedChartScale("ChartArea2IR", "SeriesIR"); } if (e.array3.Length != 0) { UpdateLedChart("SeriesGreen", e.array3); UpdateLedChartScale("ChartArea3Green", "SeriesGreen"); } } if (e.reportID == PartialArrayIntAvailableEventArgs.PACKET_LIS2DH) { if (e.array1.Length != 0) { UpdateXYZChart("SeriesAccelX", e.array1); UpdateXYZChartScale("ChartArea4AccelX", "SeriesAccelX"); } if (e.array2.Length != 0) { UpdateXYZChart("SeriesAccelY", e.array2); UpdateXYZChartScale("ChartArea5AccelY", "SeriesAccelY"); } if (e.array3.Length != 0) { UpdateXYZChart("SeriesAccelZ", e.array3); UpdateXYZChartScale("ChartArea6AccelZ", "SeriesAccelZ"); } #if CES_DEMO if (sampleRate == 100) updateAlgorithm(); #endif } } } #if CES_DEMO private void updateAlgorithm() { int rtn = 0; while (red.Count != 0 ) //&& ir.Couunt != 0 && led.Count != 0) { StringBuilder outputData = new StringBuilder(); int redVal = (Int32)red.Dequeue(); // Always dequeue red, checked in while loop int irVal = ir.Count > 0 ? (Int32)ir.Dequeue() : 1; int ledVal = led.Count > 0 ? (Int32)led.Dequeue() : 1; switch (streamMode) { case eStreamMode.eSPO2: rtn = AlgorithmMobileBU.runAlgorithm_win(irVal, redVal, 1, // ir, r, led 25, sampleRate, ++algoCounter, 0, 0, 0, ref config, ref output); break; case eStreamMode.eHR: // TODO: Not functioning rtn = AlgorithmMobileBU.runAlgorithm_win(1, redVal, 1, // ir, r, led 25, sampleRate, ++algoCounter, 0, 0, 0, ref config, ref output); break; case eStreamMode.eMulti: rtn = AlgorithmMobileBU.runAlgorithm_win(irVal, redVal, ledVal, // ir, r, led 25, sampleRate, ++algoCounter, 0, 0, 0, ref config, ref output); break; } //outputData.Append(DateTime.Now.ToString("HH:mm:ss.ffff")); //outputData.Append(","); //outputData.Append(irVal); //outputData.Append(","); //outputData.Append(redVal); //outputData.Append(","); //outputData.Append(0); // Can be green //outputData.Append(","); //outputData.Append(0); // Ax //outputData.Append(","); //outputData.Append(0); // Ay //outputData.Append(","); //outputData.Append(0); // Az //outputData.Append(","); //file.WriteLine(outputData.ToString()); // Raw data output } lblHeartRate.Text = Math.Round(output.hr).ToString(); //redIn.ToString(); lblRespRate.Text = output.resp_rate.ToString("N"); lblSpO2.Text = output.R == 0 ? "0" : (-45.060 * output.R * output.R + 30.354 * output.R + 94.845).ToString("N");//output.spo2.ToString("N"); lblHeartRateVariation.Text = output.hrv.ToString("N"); lblPulseInterval.Text = output.pulse_interval.ToString("N"); } #endif private void OpticalView_Load(object sender, EventArgs e) { //OpticalSensorInitControls(); //OpticalSensorDefaults(); } private void streamingStartStop() { if (cboMode.Text == "HR") streamMode = eStreamMode.eHR; if (cboMode.Text == "SPO2") streamMode = eStreamMode.eSPO2; if (cboMode.Text == "LED") streamMode = eStreamMode.eMulti; if (Connected && btnMonitoring.Text == StartString) { streaming = true; sample_rate = (byte)cboSampleRate.SelectedIndex; sample_avg = (byte)cboSampleAvg.SelectedIndex; pulse_width = (byte)cboPulseWidth.SelectedIndex; red_led_current = (byte)cboRedLED.SelectedIndex; ir_led_current = (byte)cboIRLED.SelectedIndex; green_led_current = (byte)cboGreenLED.SelectedIndex; slot_1 = (byte)cboLEDslot1.SelectedIndex; slot_2 = (byte)cboLEDslot2.SelectedIndex; slot_3 = (byte)cboLEDslot3.SelectedIndex; slot_4 = (byte)cboLEDslot4.SelectedIndex; sampleRate = Int32.Parse((string)cboSampleRate.SelectedItem); sampleAverage = Int32.Parse((string)cboSampleAvg.SelectedItem); points = (sampleRate / sampleAverage ) * ChartTime; foreach (Control c in streamingControls) c.Enabled = false; #if CES_DEMO // For Algorithm sampleRate = (1 << sample_rate) * 50; // (1 << sample_rate) = 2^sample_rate sampleAverage = (1 << sample_avg); // 2^sample_avg points = (sampleRate / sampleAverage) * ChartTime; // 4 seconds, 2^sample_av sampleRateAccel = 10; // TODO, update with real value from Jerry pointsAccel = (sampleRateAccel / sampleAverage) * ChartTime; // Reset algorithm counter algoCounter = 0; if (sampleRate == 100) { lblHeartRateText.Enabled = true; lblHeartRate.Enabled = true; lblSpO2.Enabled = true; lblSpO2Text.Enabled = true; } else { lblHeartRateText.Enabled = false; lblHeartRate.Enabled = false; lblSpO2.Enabled = false; lblSpO2Text.Enabled = false; } #endif // Setup grid setupGraphs(); switch (streamMode) { case eStreamMode.eHR: rpcClient.MAX30101.HRmode_init(fifo_waterlevel_mark, sample_avg, sample_rate, pulse_width, red_led_current, (byte)(sample_rate + 4)); break; case eStreamMode.eMulti: rpcClient.MAX30101.Multimode_init(fifo_waterlevel_mark, sample_avg, sample_rate, pulse_width, red_led_current, ir_led_current, green_led_current, slot_1, slot_2, slot_3, slot_4, (byte)(sample_rate + 4)); break; case eStreamMode.eSPO2: rpcClient.MAX30101.SpO2mode_init(fifo_waterlevel_mark, sample_avg, sample_rate, pulse_width, red_led_current, ir_led_current, (byte)(sample_rate + 4)); break; } btnMonitoring.Text = StopString; if (StreamingStartStop != null) StreamingStartStop(this, new StreamingStartStopEventArgs() { state = true }); } else if (btnMonitoring.Text == StopString) { streaming = false; switch (streamMode) { case eStreamMode.eHR: rpcClient.MAX30101.HRmode_stop(connected); break; case eStreamMode.eMulti: rpcClient.MAX30101.Multimode_stop(connected); break; case eStreamMode.eSPO2: rpcClient.MAX30101.SpO2mode_stop(connected); break; } //rpcClient.MAX30101.StopStreaming(); btnMonitoring.Text = StartString; foreach (Control c in streamingControls) c.Enabled = true; if (StreamingStartStop != null) StreamingStartStop(this, new StreamingStartStopEventArgs() { state = false }); } } private void btnMonitoring_Click(object sender, EventArgs e) { //if (Connected) streamingStartStop(); } private void button1_Click(object sender, EventArgs e) { timer.Start(); } private void button1_Click_1(object sender, EventArgs e) { /* Debug*/ //int count = chartLED.Series["SeriesRed"].Points.Count; //int count2 = chartAccel.Series["SeriesAccelX"].Points.Count; UpdateXYZChartScale("ChartArea6AccelZ", "SeriesAccelZ"); } private void btnDefaults_Click(object sender, EventArgs e) { OpticalSensorDefaults(); } private void cboSRPWLed_SelectedIndexChanged(object sender, EventArgs e) { calculateAvgCurrent(); } private void calculateAvgCurrent() { double ratio = double.Parse(cboPulseWidth.Text) * 1e-6 * double.Parse(cboSampleRate.Text); string _noDataString = "---"; if (ratio <= 1) { lblAvgIRLEDcurrent.Text = String.Format("{0:0.00}", double.Parse(cboIRLED.Text) * ratio); lblAvgRedLEDcurrent.Text = String.Format("{0:0.00}", double.Parse(cboRedLED.Text) * ratio); // https://jira.maxim-ic.com/browse/OS24EVK-17 Green LED: Multi-LED Mode timeslice; FIFO Read Red, IR, Green lblAvgGreenLEDcurrent.Text = String.Format("{0:0.00}", double.Parse(cboGreenLED.Text) * ratio); // https://jira.maxim-ic.com/browse/OS24EVK-32 update lblAvgPilotPALEDcurrent from cboPilotPA similar to lblAvgGreenLEDcurrent //lblAvgPilotPALEDcurrent.Text = String.Format("{0:0.00}", double.Parse(cboPilotPA.Text) * ratio); } else { lblAvgIRLEDcurrent.Text = _noDataString; lblAvgRedLEDcurrent.Text = _noDataString; // https://jira.maxim-ic.com/browse/OS24EVK-17 Green LED: Multi-LED Mode timeslice; FIFO Read Red, IR, Green lblAvgGreenLEDcurrent.Text = _noDataString; // https://jira.maxim-ic.com/browse/OS24EVK-32 update lblAvgPilotPALEDcurrent from cboPilotPA similar to lblAvgGreenLEDcurrent lblAvgPilotPALEDcurrent.Text = _noDataString; } } void cboSampleRate_SelectedIndexChanged(object sender, EventArgs e) { if ((string)cboSampleRate.SelectedItem == "100" && (string)cboSampleAvg.SelectedItem == "1") grpAlgorithm.Enabled = true; else grpAlgorithm.Enabled = false; sampleRate = Int32.Parse((string)cboSampleRate.SelectedItem); sampleAverage = Int32.Parse((string)cboSampleAvg.SelectedItem); } public string SettingsString() { StringBuilder strBuilder = new StringBuilder(); strBuilder.Append("% "); strBuilder.Append("Mode = "); strBuilder.Append(cboMode.SelectedItem); strBuilder.Append(Environment.NewLine); strBuilder.Append("% "); strBuilder.Append("Sample Rate (Hz) = "); strBuilder.Append(cboSampleRate.SelectedItem); strBuilder.Append(" Sample Average = "); strBuilder.Append(cboSampleAvg.SelectedItem); strBuilder.Append(" Pulse Width (us) = "); strBuilder.Append(cboPulseWidth.SelectedItem); strBuilder.Append(" ADC Full Scale Range (nA) = "); strBuilder.Append(cboADCFSrangenA.SelectedItem); strBuilder.Append(Environment.NewLine); strBuilder.Append("% "); strBuilder.Append(" LED Red (mA) = "); strBuilder.Append(cboRedLED.SelectedItem); strBuilder.Append(" LED IR (mA) = "); strBuilder.Append(cboIRLED.SelectedItem); strBuilder.Append(" LED Green (mA) = "); strBuilder.Append(cboGreenLED.SelectedItem); strBuilder.Append(Environment.NewLine); strBuilder.Append("% "); strBuilder.Append("LED Slot 1 = "); strBuilder.Append(cboLEDslot1.SelectedIndex); strBuilder.Append(" LED Slot 2 = "); strBuilder.Append(cboLEDslot2.SelectedIndex); strBuilder.Append(" LED Slot 3 = "); strBuilder.Append(cboLEDslot3.SelectedIndex); strBuilder.Append(" LED Slot 4 = "); strBuilder.Append(cboLEDslot4.SelectedIndex); return strBuilder.ToString(); } public string AccelSettingString() { return "% Sample Rate (Hz) = " + AccelSampleRate; } public InitArgs.HRModeInitStart GetHRModeArgs() { InitArgs.HRModeInitStart args = new InitArgs.HRModeInitStart(); args.FifoWaterlevelMark = fifo_waterlevel_mark; args.SampleRate = (byte)cboSampleRate.SelectedIndex; args.SampleAverage = (byte)cboSampleAvg.SelectedIndex; args.PulseWidth = (byte)cboPulseWidth.SelectedIndex; args.RedLedCurrent = (byte)cboRedLED.SelectedIndex; return args; } public InitArgs.SpO2HRModeInitStart GetSpO2HRModeArgs() { InitArgs.SpO2HRModeInitStart args = new InitArgs.SpO2HRModeInitStart(); args.FifoWaterlevelMark = fifo_waterlevel_mark; args.SampleRate = (byte)cboSampleRate.SelectedIndex; args.SampleAverage = (byte)cboSampleAvg.SelectedIndex; args.PulseWidth = (byte)cboPulseWidth.SelectedIndex; args.RedLedCurrent = (byte)cboRedLED.SelectedIndex; args.IRLedCurrent = (byte)cboIRLED.SelectedIndex; return args; } public InitArgs.MultiModeInitStart GetMultiModeArgs() { InitArgs.MultiModeInitStart args = new InitArgs.MultiModeInitStart(); args.FifoWaterlevelMark = fifo_waterlevel_mark; args.SampleRate = (byte)cboSampleRate.SelectedIndex; args.SampleAverage = (byte)cboSampleAvg.SelectedIndex; args.PulseWidth = (byte)cboPulseWidth.SelectedIndex; args.RedLedCurrent = (byte)cboRedLED.SelectedIndex; args.IRLedCurrent = (byte)cboIRLED.SelectedIndex; args.GreenLedCurrent = (byte)cboGreenLED.SelectedIndex; args.Slot1 = (byte)cboLEDslot1.SelectedIndex; args.Slot2 = (byte)cboLEDslot2.SelectedIndex; args.Slot3 = (byte)cboLEDslot3.SelectedIndex; args.Slot4 = (byte)cboLEDslot4.SelectedIndex; return args; } public void SetHRModeArgs(InitArgs.HRModeInitStart args) { cboSampleRate.SelectedIndex = args.SampleRate; cboSampleAvg.SelectedIndex = args.SampleAverage; cboPulseWidth.SelectedIndex = args.PulseWidth; cboRedLED.SelectedIndex = args.RedLedCurrent; } public void SetSpO2HRModeArgs(InitArgs.SpO2HRModeInitStart args) { cboSampleRate.SelectedIndex = args.SampleRate; cboSampleAvg.SelectedIndex = args.SampleAverage; cboPulseWidth.SelectedIndex = args.PulseWidth; cboRedLED.SelectedIndex = args.RedLedCurrent; cboIRLED.SelectedIndex = args.IRLedCurrent; } public void SetMultiModeArgs(InitArgs.MultiModeInitStart args) { cboSampleRate.SelectedIndex = args.SampleRate; cboSampleAvg.SelectedIndex = args.SampleAverage; cboPulseWidth.SelectedIndex = args.PulseWidth; cboRedLED.SelectedIndex = args.RedLedCurrent; cboIRLED.SelectedIndex = args.IRLedCurrent; cboGreenLED.SelectedIndex = args.GreenLedCurrent; cboLEDslot1.SelectedIndex = args.Slot1; cboLEDslot2.SelectedIndex = args.Slot2; cboLEDslot3.SelectedIndex = args.Slot3; cboLEDslot4.SelectedIndex = args.Slot4; } class MedianFilter { Queue<int> items; int depth; public MedianFilter(int depth) { items = new Queue<int>(depth + 1); this.depth = depth; } public int Filter(int current) { int last; items.Enqueue(current); if (items.Count > depth) { items.Dequeue(); } last = items.Peek(); if (items.Count < depth) { return current; } else { int count; int[] arrayItems = items.ToArray(); int maxCount = 0; int maxKey = 0; Dictionary<int, int> valueOccurance = new Dictionary<int, int>(); for (int i = 0; i < arrayItems.Length; i++) { bool exists = valueOccurance.TryGetValue(arrayItems[i], out count); if (exists) { count++; valueOccurance.Remove(arrayItems[i]); valueOccurance.Add(arrayItems[i], count); } else { valueOccurance.Add(arrayItems[i], 1); } } foreach (KeyValuePair<int, int> item in valueOccurance) { if (item.Key > maxCount) { maxKey = item.Key; } } return maxKey; } } } /* public class StreamingStartStopEventArgs : EventArgs { public bool state { get; set; } }*/ } }