repo time
Dependencies: mbed MAX14720 MAX30205 USBDevice
Diff: HspGuiSourceV301/HSPGui/CustomControls/OpticalView.cs
- Revision:
- 20:6d2af70c92ab
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/HspGuiSourceV301/HSPGui/CustomControls/OpticalView.cs Tue Apr 06 06:41:40 2021 +0000 @@ -0,0 +1,1366 @@ +/******************************************************************************* +* 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; } + }*/ + + } +}