![](/media/cache/img/default_profile.jpg.50x50_q85.jpg)
repo time
Dependencies: mbed MAX14720 MAX30205 USBDevice
Diff: HspGuiSourceV301/HSPGui/Presenter/RawFileLogPresenter.cs
- Revision:
- 20:6d2af70c92ab
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/HspGuiSourceV301/HSPGui/Presenter/RawFileLogPresenter.cs Tue Apr 06 06:41:40 2021 +0000 @@ -0,0 +1,477 @@ +/******************************************************************************* +* 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 HealthSensorPlatform.View; +using HealthSensorPlatform.Model; +using HealthSensorPlatform.CustomControls; + +using RPCSupport; +using RPCSupport.Streaming; + +namespace HealthSensorPlatform.Presenter +{ + public class RawFileLogPresenter + { + IRawFileLogView ecgLog; + IRawFileLogView bioZLog; + IRawFileLogView rToRLog; + IRawFileLogView paceLog; + IFormView formView; + IEcgView ecgView; + + bool fileLog = false; // user enabled file logging + bool streaming = false; // streaming data or flash log data, only save data from streaming + //bool flashLog = false; + + IDataLoggingView dataLogView; + + PaceData paceData; + + RToRCalculator rToRCalculator; + bool rToRFirst = true; // received first RR value for offset correction + + //bool ecgLeadOff; // DC Lead off data for ECG or BioZ Channel + DCLeadOff leadOff; // lead off status bits + ACLeadOff acLeadOff; // bioz lead off status bits + + int count = 0; + int bioZCount = 0; + //int ppgCount = 0; + //int accelCount = 0; + + string logFileDirectory = null; + + View.FileLogView.FileLogHeader fileLogHeader = new View.FileLogView.FileLogHeader(); + + public RawFileLogPresenter(IRawFileLogView ecg, IRawFileLogView bioZ, IRawFileLogView rToR, IRawFileLogView pace, IFormView formView, RPCClient rpcClient, IEcgView ecgView, IDataLoggingView dataLogView) + { + this.ecgLog = ecg; + this.bioZLog = bioZ; + this.rToRLog = rToR; + this.paceLog = pace; + this.formView = formView; + this.ecgView = ecgView; + + this.dataLogView = dataLogView; + + formView.FileLogEnable += new EventHandler<EnableEventArgs>(OnLogFileEnable); + ecgView.StreamingStartStop += new EventHandler<StreamingStartStopEventArgs>(OnStreamStart); + //dataLogView.LogDownloadStart += new EventHandler<EventArgs>(OnLogDownloadStart); + rpcClient.streaming.PartialArrayIntAvailable += new EventHandler<PartialArrayIntAvailableEventArgs>(OnStreamData); + } + + public void OnLogFileEnable(object sender, EnableEventArgs e) + { + fileLog = e.Enable; + + if (e.Enable) + { + bool result = false; + IRawFileLogView log = ecgLog; + string filePrefixName = String.Empty; + + switch(e.Stream) + { + case StreamType.Ecg: + log = ecgLog; + filePrefixName = "hsp-ecg"; + break; + case StreamType.RToR: + log = rToRLog; + filePrefixName = "hsp-rtor"; + break; + case StreamType.Pace: + log = paceLog; + filePrefixName = "hsp-pace"; + break; + case StreamType.BioZ: + log = bioZLog; + filePrefixName = "hsp-bioz"; + break; + } + + if (logFileDirectory != null) // Set starting directory to be the same as the last + log.FileDirectory = logFileDirectory; + + result = log.SelectCSVFile(filePrefixName); + log.Enable = result; + + if (result) // Save directory if success + logFileDirectory = log.FileDirectory; + + formView.LogFileItem(e.Stream, result); + } + else + { + switch (e.Stream) + { + case StreamType.Ecg: + ecgLog.Enable = false; + break; + case StreamType.RToR: + rToRLog.Enable = false; + break; + case StreamType.Pace: + paceLog.Enable = false; + break; + case StreamType.BioZ: + bioZLog.Enable = false; + break; + } + + formView.LogFileItem(e.Stream, false); + } + } + + public void OnStreamStart(object sender, StreamingStartStopEventArgs e) + { + streaming = e.state; + count = 0; + bioZCount = 0; + + if (e.state) + { + leadOff = new DCLeadOff { NegativeHigh = false, NegativeLow = false, PostiveHigh = false, PostiveLow = false }; + acLeadOff = new ACLeadOff { BioZOverRange = false, BioZUnderRange = false }; + + if (ecgLog != null && ecgLog.Enable && ecgView.EnableECG) + { + //ecgLog.WriteLine("% " + ecgView.HspSetting.EcgSettingString()); // TODO: What is the requirement for headers + ecgLog.WriteLine(fileLogHeader.Ecg); + } + + if (bioZLog != null && bioZLog.Enable && ecgView.EnableBioZ) + { + //ecgLog.WriteLine("% " + ecgView.HspSetting.BioZSettingString()); + bioZLog.WriteLine(fileLogHeader.BioZ); + } + + if (rToRLog != null && rToRLog.Enable && ecgView.EnableRToR) + { + rToRLog.WriteLine(fileLogHeader.RToR); + rToRCalculator = new RToRCalculator(ecgView.MasterClockField, ecgView.EcgArgs.Rate, ecgView.EcgArgs.Dlpf, ecgView.RToRArgs.Wndw); + } + + if (paceLog != null && paceLog.Enable && ecgView.EnablePace) + { + paceLog.WriteLine(fileLogHeader.Pace); + } + } + else + { + if (ecgLog != null) + ecgLog.Enable = false; + + if (bioZLog != null) + bioZLog.Enable = false; + + if (rToRLog != null) + rToRLog.Enable = false; + + if (paceLog != null) + paceLog.Enable = false; + } + } + + void fileLogStop() + { + fileLog = false; + stop(); + + } + + void stop() + { + if (ecgLog != null && ecgLog.Enable) + ecgLog.Enable = false; + + if (rToRLog != null && rToRLog.Enable) + rToRLog.Enable = false; + + if (paceLog != null && paceLog.Enable) + paceLog.Enable = false; + + if (bioZLog != null && bioZLog.Enable) + bioZLog.Enable = false; + + formView.LogFileItem(StreamType.Ecg, false); + formView.LogFileItem(StreamType.RToR, false); + formView.LogFileItem(StreamType.Pace, false); + formView.LogFileItem(StreamType.BioZ, false); + } + + private void OnStreamData(object sender, PartialArrayIntAvailableEventArgs e) + { + if (streaming) + { + switch (e.reportID) + { + case PartialArrayIntAvailableEventArgs.PACKET_MAX30001_ECG: + if (ecgLog.Enable) + ProcessEcg(e.array1); + break; + case PartialArrayIntAvailableEventArgs.PACKET_MAX30001_BIOZ: + if (bioZLog.Enable) + ProcessBioZ(e.array1); + break; + case PartialArrayIntAvailableEventArgs.PACKET_MAX30001_PACE: + if (paceLog.Enable) + paceData = new PaceData(e.array1); + break; + case PartialArrayIntAvailableEventArgs.PACKET_MAX30001_RTOR: + if (rToRLog.Enable) + ProcessRToR(e.array1[0]); + break; + case PartialArrayIntAvailableEventArgs.PACKET_END_OF_STREAM: + fileLogStop(); + break; + case PartialArrayIntAvailableEventArgs.PACKET_MAX30001_LEADOFF_DC: + ProcessLeadOff(e.array1[0]); + break; + case PartialArrayIntAvailableEventArgs.PACKET_MAX30001_LEADOFF_AC: + ProcessACLeadOff(e.array1[0]); + break; + } + } + } + + void ProcessBioZ(int[] rawData) + { + BioZFifo[] bioZFifo; + double sampleRate = ecgView.SampleRateBioZ; + double[] time = new double[rawData.Length]; + + bioZFifo = ConvertBioZ(rawData); + + for (int i = 0; i < time.Length; i++) + { + time[i] = bioZCount / sampleRate; + bioZCount++; + } + + if (ecgView.EnableDCLeadOff == false && ecgView.EnableBioZOverUnderRange == true) + bioZLog.DisplayBioZ(time, bioZFifo, acLeadOff); + else if (ecgView.EnableDCLeadOff == true && ecgView.EnableEcgDCLeadOff == false && ecgView.EnableBioZOverUnderRange == false) // && ecgLeadOff == false) + bioZLog.DisplayBioZ(time, bioZFifo, leadOff); + else if (ecgView.EnableDCLeadOff == true && ecgView.EnableEcgDCLeadOff == false && ecgView.EnableBioZOverUnderRange == true) + bioZLog.DisplayBioZ(time, bioZFifo, leadOff, acLeadOff); + else if ((ecgView.EnableDCLeadOff == false && ecgView.EnableBioZOverUnderRange == false) || + ecgView.EnableEcgDCLeadOff == true && ecgView.EnableEcgDCLeadOff == true && ecgView.EnableBioZOverUnderRange == false) + bioZLog.DisplayBioZ(time, bioZFifo); + } + + public BioZFifo[] ConvertBioZ(int[] data) + { + BioZFifo[] impedance = new BioZFifo[data.Length]; + 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; + } + + void ProcessEcg(int[] rawData) + { + EcgFifo[] ecgFifo; + double ecgRate = ecgView.SampleRateEcg; + double[] ecgVoltage = new double[rawData.Length]; + double[] timeSecond = new double[rawData.Length]; + + 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++; + } + + if (ecgView.EnableDCLeadOff && ecgView.EnableEcgDCLeadOff) + ecgLog.DisplayEcg(timeSecond, ecgFifo, leadOff); + else + ecgLog.DisplayEcg(timeSecond, ecgFifo); + + } + + public EcgFifo[] ConvertEcg(int[] data) + { + EcgFifo[] voltage = new EcgFifo[data.Length]; + 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 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 ProcessLeadOff(int data) + { + /* + if (bitShiftMask(data, 8)) // ECG lead off + ecgLeadOff = true; + else if (bitShiftMask(data, 9)) // BioZ lead off + ecgLeadOff = false; + */ + + leadOff = new DCLeadOff() + { + PostiveHigh = bitShiftMask(data, 3), + PostiveLow = bitShiftMask(data, 2), + NegativeHigh = bitShiftMask(data, 1), + NegativeLow = bitShiftMask(data, 0) + }; + + } + + void ProcessACLeadOff(int data) + { + acLeadOff = new ACLeadOff() + { + BioZUnderRange = bitShiftMask(data, 1), + BioZOverRange = bitShiftMask(data, 0), + }; + } + + private static bool bitShiftMask(int data, int index) + { + int state; + int mask = 1 << index; + state = ((data & mask) == mask) ? 1 : 0; + return state == 1; + } + + void ProcessRToR(int data) + { + if (rToRFirst) + { + rToRLog.DisplayRToR(data, rToRCalculator.Corrected(data, true) / 1000); + rToRFirst = false; + } + else + rToRLog.DisplayRToR(data, rToRCalculator.Corrected(data, false) / 1000); + } + } +}