repo time
Dependencies: mbed MAX14720 MAX30205 USBDevice
Diff: HspGuiSourceV301/HSPGui/Presenter/DataLogPresenter.cs
- Revision:
- 20:6d2af70c92ab
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/HspGuiSourceV301/HSPGui/Presenter/DataLogPresenter.cs Tue Apr 06 06:41:40 2021 +0000 @@ -0,0 +1,711 @@ +/******************************************************************************* +* 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. +******************************************************************************* +*/ + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +using RPCSupport.Streaming; +using HealthSensorPlatform.Model; +using HealthSensorPlatform.View; +using HealthSensorPlatform.CustomControls; + +namespace HealthSensorPlatform.Presenter +{ + class DataLogPresenter + { + IDataLogModel model; + DataLoggingView view; + IRawFileLogView temperatureLog1; + IRawFileLogView temperatureLog2; + IRawFileLogView pressureLog; + + IRawFileLogView accelerometerLog; + IRawFileLogView opticalLog; + + IRawFileLogView ecgLog; + IRawFileLogView bioZLog; + IRawFileLogView paceLog; + IRawFileLogView rToRLog; + + IFormView formView; + + StatusForm statusForm; + + EcgView ecgView; // Needed to obtain some state information, should be decoupled later and use from data log model + OpticalView opticalView; + + int count = 0; + int bioZCount = 0; + int ppgCount = 0; + int accelCount = 0; + PaceData paceData; + RToRCalculator rToRCalculator; + bool rToRFirst = true; + + string logFileDirectory = null; + + View.FileLogView.FileLogHeader fileLogHeader = new View.FileLogView.FileLogHeader(); + + public DataLogPresenter(IFormView formView, EcgView ecgView, OpticalView opticalView, IDataLogModel model, DataLoggingView view, + IRawFileLogView temperatureLog1, IRawFileLogView temperatureLog2, IRawFileLogView pressureLog, + IRawFileLogView accelerometerLog, IRawFileLogView opticalLog, + IRawFileLogView ecgLog, IRawFileLogView bioZLog, IRawFileLogView paceLog, IRawFileLogView rToRLog) + { + this.model = model; + this.view = view; + + this.formView = formView; + this.ecgView = ecgView; + this.opticalView = opticalView; + + this.temperatureLog1 = temperatureLog1; + this.temperatureLog2 = temperatureLog2; + this.pressureLog = pressureLog; + + this.accelerometerLog = accelerometerLog; + this.opticalLog = opticalLog; + + this.ecgLog = ecgLog; + this.bioZLog = bioZLog; + this.paceLog = paceLog; + this.rToRLog = rToRLog; + + view.LogDownloadStart += new EventHandler<EventArgs>(OnLogDownloadStart); + view.MissionWrite += new EventHandler<EventArgs>(OnMissionWrite); + view.MissionRead += new EventHandler<EventArgs>(OnMissionRead); + view.MissionErase += new EventHandler<EventArgs>(OnMissionErase); + model.LogData += new EventHandler<PartialArrayIntAvailableEventArgs>(OnLogData); + } + + public void ProcessTemperature1(int[] data) + { + double[] calcTemp; + + calcTemp = CalculateTemperature(data); + temperatureLog1.DisplayTemperature(data, calcTemp); + } + + public void ProcessTemperature2(int[] data) + { + double[] calcTemp; + + calcTemp = CalculateTemperature(data); + temperatureLog2.DisplayTemperature(data, calcTemp); + } + + public double[] CalculateTemperature(int[] data) + { + double[] calcTemp = new double[data.Length]; + int rawCode; + + for (int i = 0; i < data.Length; i++ ) + { + rawCode = data[i]; + + if (rawCode > 0x7fff) + rawCode -= 0x10000; + + calcTemp[i] = rawCode / Math.Pow(2, 8); + } + + return calcTemp; + } + + public void ProcessPressure(int[] data) + { + int[] rawTemp = new int[data.Length/2]; + int[] rawPress = new int[data.Length/2]; + double[] calcTemp, calcPress; + + for (int i = 0, j = 0; i < data.Length; i = i + 2, j++) + { + rawTemp[j] = data[i]; + rawPress[j] = data[i + 1]; + } + + var result = CalculatePressure(rawTemp, rawPress); + calcTemp = result.Item1; + calcPress = result.Item2; + + pressureLog.DisplayPressure(rawTemp, calcTemp, rawPress, calcPress); + } + + public Tuple<double[], double[]> CalculatePressure(int[] temperature, int[] pressure) + { + double[] calcTemp = new double[temperature.Length]; + double[] calcPress = new double[pressure.Length]; + + for (int i = 0; i < temperature.Length; i++ ) + { + calcTemp[i] = temperature[i] / 10.0; + } + + for (int i = 0; i < pressure.Length; i++ ) + { + calcPress[i] = pressure[i] / 10; + } + + return new Tuple<double[],double[]>(calcTemp, calcPress); + } + + private void OnLogDownloadStart(object sender, EventArgs e) + { + + if (view.Connected) + { + count = 0; + bioZCount = 0; + ppgCount = 0; + accelCount = 0; + + missionRead(); + + if (model.MissionSettings.Enable) + { + + if (selectFile()) + { + string missionString; + StringBuilder sb = new StringBuilder(); + foreach (string str in model.MissionString()) + { + sb.Append("% "); + sb.Append(str); + sb.Append(Environment.NewLine); + } + missionString = sb.ToString().Trim(Environment.NewLine.ToCharArray()); + + + if (temperatureLog1 != null && temperatureLog1.Enable) + { + temperatureLog1.WriteLine(missionString); + temperatureLog1.WriteLine(fileLogHeader.Temperature1); + } + + if (temperatureLog2 != null && temperatureLog2.Enable) + { + temperatureLog2.WriteLine(missionString); + temperatureLog2.WriteLine(fileLogHeader.Temperature2); + } + + if (pressureLog != null && pressureLog.Enable) + { + pressureLog.WriteLine(missionString); + pressureLog.WriteLine(fileLogHeader.Pressure); + } + + if (accelerometerLog != null && accelerometerLog.Enable) + { + accelerometerLog.WriteLine(missionString); + accelerometerLog.WriteLine(fileLogHeader.Accelerometer); + } + + if (opticalLog != null && opticalLog.Enable) + { + opticalLog.WriteLine(missionString); + opticalLog.WriteLine(fileLogHeader.Optical); + } + + if (ecgLog != null && ecgLog.Enable) + { + ecgLog.WriteLine(missionString); + ecgLog.WriteLine(fileLogHeader.Ecg); + } + + if (bioZLog != null && bioZLog.Enable) + { + bioZLog.WriteLine(missionString); + bioZLog.WriteLine(fileLogHeader.BioZ); + } + + if (rToRLog != null && rToRLog.Enable && ecgLog.Enable) + { + rToRLog.WriteLine(missionString); + rToRCalculator = new RToRCalculator(ecgView.MasterClockField, view.EcgArgs.Rate, view.EcgArgs.Dlpf, view.RToRArgs.Wndw); + rToRLog.WriteLine(fileLogHeader.RToR); + } + else if (rToRLog != null && rToRLog.Enable) + { + rToRLog.WriteLine(missionString); + rToRCalculator = null; + rToRLog.WriteLine(fileLogHeader.RToR); + } + + if (paceLog != null && paceLog.Enable) + { + paceLog.WriteLine(missionString); + paceLog.WriteLine(fileLogHeader.Pace); + } + + statusForm = new StatusForm(); + statusForm.Text = "Flash Download"; + statusForm.Message = "Please wait while your data is saved to your file."; + statusForm.Show(); + + model.Start(); + } + } + } + } + + private void LogDownloadStop() + { + if (temperatureLog1 != null && temperatureLog1.Enable) + temperatureLog1.Enable = false; + + if (temperatureLog2 != null && temperatureLog2.Enable) + temperatureLog2.Enable = false; + + if (pressureLog != null && pressureLog.Enable) + pressureLog.Enable = false; + + if (accelerometerLog != null && accelerometerLog.Enable) + accelerometerLog.Enable = false; + + if (opticalLog != null && opticalLog.Enable) + opticalLog.Enable = false; + + if (ecgLog != null && ecgLog.Enable) + ecgLog.Enable = false; + + if (bioZLog != null && bioZLog.Enable) + bioZLog.Enable = false; + + if (rToRLog != null && rToRLog.Enable) + rToRLog.Enable = false; + + if (paceLog != null && paceLog.Enable) + paceLog.Enable = false; + + formView.MessageInfo("File save complete"); + statusForm.Hide(); + } + + private void OnLogData(object sender, PartialArrayIntAvailableEventArgs e) + { + switch(e.reportID) + { + case PartialArrayIntAvailableEventArgs.PACKET_BMP280_PRESSURE: + if (pressureLog != null && pressureLog.Enable) + ProcessPressure(e.array1); + break; + case PartialArrayIntAvailableEventArgs.PACKET_MAX31725_TEMP1: + if (temperatureLog1 != null && temperatureLog1.Enable) + ProcessTemperature1(e.array1); + break; + case PartialArrayIntAvailableEventArgs.PACKET_MAX31725_TEMP2: + if (temperatureLog2 != null && temperatureLog2.Enable) + ProcessTemperature2(e.array1); + break; + + case PartialArrayIntAvailableEventArgs.PACKET_MAX30001_ECG: + if (ecgLog != null && ecgLog.Enable) + ProcessEcg(e.array1); + break; + case PartialArrayIntAvailableEventArgs.PACKET_MAX30001_BIOZ: + if (bioZLog != null && bioZLog.Enable) + ProcessBioZ(e.array1); + break; + case PartialArrayIntAvailableEventArgs.PACKET_MAX30001_PACE: + if (paceLog != null && paceLog.Enable) + paceData = new PaceData(e.array1); + break; + case PartialArrayIntAvailableEventArgs.PACKET_MAX30001_RTOR: + if (rToRLog != null && rToRLog.Enable) + ProcessRToR(e.array1[0]); + break; + + case PartialArrayIntAvailableEventArgs.PACKET_LIS2DH: + case PartialArrayIntAvailableEventArgs.PACKET_LSM6DS3_ACCEL: + if (accelerometerLog != null && accelerometerLog.Enable) + ProcessAccelerometer(e.array1, e.array2, e.array3); + break; + case PartialArrayIntAvailableEventArgs.PACKET_END_OF_STREAM: + LogDownloadStop(); + break; + } + + if ((e.reportID & 0xF0) == PartialArrayIntAvailableEventArgs.PACKET_MAX30101) + { + if (opticalLog != null && opticalLog.Enable) + ProcessOptical(e.array1, e.array2, e.array3); + + } + } + + private void OnMissionWrite(object sender, EventArgs e) + { + if (view.Connected) + { + if (view.EnableAccelerometer || view.EnableBioz || view.EnableEcg || view.EnableOpticalHR + || view.EnableOpticalMulti || view.EnableOpticalSpO2 || view.EnablePace + || view.EnablePressure || view.EnableRToR || view.EnableTemperature1 || view.EnableTemperature2) + { + if (view.ValidateGuiElements()) + { + statusForm = new StatusForm(); + statusForm.Message = "Please wait while your flash log is cleared and new parameters are written. Do not unplug the USB connection or press start button on the board until write is complete."; + statusForm.Show(); + formView.MessageInfo("Writing in progress..."); + model.MissionErase(); + model.EraseWrittenSectors(); + model.MissionStartDefinition(); + view.ProcessGuiElements(); + //rpcClient.DataLogging.Test(); + model.MissionWrite(); + statusForm.Hide(); + formView.MessageInfo("Write parameters complete"); + } + else + formView.MessageInfo("Incorrect logging parameters"); + } + else + { + formView.MessageInfo("No devices selected"); + } + } + } + + private void OnMissionRead(object sender, EventArgs e) + { + if (view.Connected) + { + missionRead(); + } + } + + private void OnMissionErase(object sender, EventArgs e) + { + if (view.Connected) + { + model.MissionErase(); + + formView.MessageInfo("Erase parameters complete"); + } + } + + private void OnLogFileEnable(object sender, EnableEventArgs e) + { + // Not used + } + + private bool selectFile() + { + bool result = false; + + Mission settings = model.MissionSettings; + + if (settings.EnableEcg) + result |= selectLogFileName(ecgLog, "hsp-log-ecg"); + + if (settings.EnablePace) + result |= selectLogFileName(paceLog, "hsp-log-pace"); + + if (settings.EnableBioZ) + result |= selectLogFileName(bioZLog, "hsp-log-bioz"); + + if (settings.EnableRToR) + result |= selectLogFileName(rToRLog, "hsp-log-rtor"); + + if (settings.EnableOpticalHR || settings.EnableOpticalMulti || settings.EnableOpticalSpO2) + result |= selectLogFileName(opticalLog, "hsp-log-optical"); + + if (settings.EnableTemperature1) + result |= selectLogFileName(temperatureLog1, "hsp-log-temperature1"); + + if (settings.EnableTemperature2) + result |= selectLogFileName(temperatureLog2, "hsp-log-temperature2"); + + if (settings.EnablePressure) + result |= selectLogFileName(pressureLog, "hsp-log-barometer"); + + if (settings.EnableAccelerometer) + result |= selectLogFileName(accelerometerLog, "hsp-log-accelerometer"); + + return result; + } + + bool selectLogFileName(IRawFileLogView log, string name) + { + bool result; + + if (logFileDirectory != null) + log.FileDirectory = logFileDirectory; + + result = log.SelectCSVFile(name); + log.Enable = result; + + if (result) + logFileDirectory = log.FileDirectory; + + return result; + } + + private void missionRead() + { + model.MissionRead(); + view.UpdateGuiElements(model.MissionSettings); + if (model.MissionSettings.Enable) + formView.MessageInfo("Read complete"); + else + formView.MessageInfo("No parameters defined"); + } + + void ProcessBioZ(int[] rawData) + { + BioZFifo[] bioZFifo; + double[] time = new double[rawData.Length]; + double sampleRate = ecgView.SampleRateBioZ; + + bioZFifo = ConvertBioZ(rawData); + + for (int i = 0; i < time.Length; i++ ) + { + time[i] = bioZCount / (double)sampleRate; + bioZCount++; + } + + bioZLog.DisplayBioZ(time, bioZFifo); + } + + public BioZFifo[] ConvertBioZ(int[] data) + { + BioZFifo[] impedance = new BioZFifo[data.Length]; + //EcgView.ChartInfo chartInfo = BioZInfo(); + EcgView.ChartInfo chartInfo = ecgView.BioZInfo; + + int dataShift; + + for (int i = 0; i < data.Length; i++) + { + dataShift = data[i] >> chartInfo.Shift; + + // Two's Complement Conversions + if (dataShift > chartInfo.Threshold) + { + dataShift -= chartInfo.Offset; + } + + // 1.9734 = 1/2^19 * 1e-6 + impedance[i].Data = dataShift * 1.9073486328125 / + (chartInfo.Gain * ((chartInfo.CurrentGenerator == 0) ? 1 : chartInfo.CurrentGenerator)); + impedance[i].Code = data[i]; + impedance[i].BioZData = dataShift; + impedance[i].BTag = data[i] & 0x07; + } + + return impedance; + } + + public EcgView.ChartInfo BioZInfo() + { + EcgView.ChartInfo info = new EcgView.ChartInfo(); + int[] currentGen = new int[] {1, 8, 16, 32, 48, 64, 80, 96}; + int[] gain = new int[] {20, 40, 80, 160}; + + info.Shift = 4; + info.Offset = 0x100000; + info.Threshold = 0x100000; + info.Gain = gain[model.MissionSettings.BioZArgs[10]]; + info.CurrentGenerator = currentGen[model.MissionSettings.BioZArgs[15]]; + + return info; + } + + void ProcessEcg(int[] rawData) + { + EcgFifo[] ecgFifo; + double[] ecgVoltage = new double[rawData.Length]; + double[] timeSecond = new double[rawData.Length]; + double ecgRate = ecgView.SampleRateEcg; + + ecgFifo = ConvertEcg(rawData); + + for (int i = 0; i < ecgFifo.Length; i++) + { + timeSecond[i] = count / ecgRate; + + // Look for Pace Events + if (paceLog.Enable) + { + //for (int j = 0; j < ecgFifo.Length; j++) + //{ + if (ecgFifo[i].PTag != 7) + { + PaceData.PaceRegister paceRegister = paceData.PaceGroup(ecgFifo[i].PTag); + List<double> timeMillsecondPace = new List<double>(); + List<PaceData.PaceEdge> paceEdges = new List<PaceData.PaceEdge>(); + + for (int k = 0; k < 6; k++) + { + PaceData.PaceEdge edge = paceRegister.Edge[k]; + + timeMillsecondPace.Add(count / ecgRate + ConvertPace(edge.Data)); + paceEdges.Add(edge); + + if (edge.Last == true) + break; + } + + paceLog.DisplayPace(timeMillsecondPace.ToArray(), paceEdges.ToArray()); + System.Diagnostics.Debug.Print("ECG PTag = " + ecgFifo[i].PTag); + } + //} + } + + count++; + } + + ecgLog.DisplayEcg(timeSecond, ecgFifo); + + } + + public EcgFifo[] ConvertEcg(int[] data) + { + EcgFifo[] voltage = new EcgFifo[data.Length]; + //EcgView.ChartInfo chartInfo = EcgInfo(); + EcgView.ChartInfo chartInfo = ecgView.EcgInfo; + + int dataShift; + + for (int i = 0; i < data.Length; i++) + { + dataShift = data[i] >> chartInfo.Shift; + + // Two's Complement Conversions + if (dataShift > chartInfo.Threshold) + { + dataShift -= chartInfo.Offset; + } + + voltage[i].Data = 1000 * 7.62939453125e-6 * dataShift / chartInfo.Gain; + voltage[i].EcgData = dataShift; + voltage[i].Code = data[i]; + voltage[i].PTag = data[i] & 0x07; + voltage[i].ETag = (data[i] >> 3) & 0x07; + } + + return voltage; + } + + public EcgView.ChartInfo EcgInfo() + { + EcgView.ChartInfo info = new EcgView.ChartInfo(); + int[] gain = new int[] {20, 40, 80, 160}; + + info.Shift = 6; + info.Threshold = 0x1ffff; + info.Offset = 0x40000; + + info.Gain = gain[model.MissionSettings.EcgArgs[10]]; + + return info; + } + + public double ConvertRToR(int data) + { + return ecgView.TimeResolution * 1000 * data * 512; + } + + public double ConvertPace(int data) + { + return data * ecgView.TimeResolution; + } + + public string PaceRegisterGroupToString(PaceData.PaceRegister paceRegister) + { + StringBuilder paceRegisterLogBuilder = new StringBuilder(); + for (int j = 0; j < 6; j++) + { + paceRegisterLogBuilder.Append(paceRegister.Edge[j].Data / (2 * ecgView.MasterClockFrequency)); + paceRegisterLogBuilder.Append(", "); + paceRegisterLogBuilder.Append(paceRegister.Edge[j].Polarity ? 'R' : 'F'); + paceRegisterLogBuilder.Append(", "); + paceRegisterLogBuilder.Append(paceRegister.Edge[j].Last ? 'Y' : 'N'); + paceRegisterLogBuilder.Append(", "); + } + + return paceRegisterLogBuilder.ToString(); + } + + void ProcessRToR(int data) + { + if (rToRCalculator != null) + { + if (rToRFirst) + { + rToRLog.DisplayRToR(data, rToRCalculator.Corrected(data, true) / 1000); + rToRFirst = false; + } + else + rToRLog.DisplayRToR(data, rToRCalculator.Corrected(data, false) / 1000); + } + else + { + rToRLog.DisplayRToR(data, 0); + } + } + + public void ProcessOptical(int[] red, int[] ir, int[] green) + { + int sampleRate = opticalView.OpticalSampleRate; + double[] time = new double[red.Length]; + + for (int i = 0; i < time.Length; i++ ) + { + time[i] = ppgCount / (double)sampleRate; + ppgCount++; + } + + opticalLog.DisplayPpg(time, new int[][] { red, ir, green }); + } + + public void ProcessAccelerometer(int[] x, int[] y, int[] z) + { + int sampleRate = view.AccelSampleRate; + double[] time = new double[x.Length]; + + for (int i = 0; i < time.Length; i++) + { + time[i] = accelCount / (double)sampleRate; + accelCount++; + } + + accelerometerLog.DisplayXYZ(time, new int[][] { x, y, z }); + } + } +}