/// <summary> /// Starts sensing and streaming. Skips starting sensing if Adaptive running. /// </summary> /// <param name="theSummit">Summit System</param> /// <param name="senseConfig">Sense Model</param> /// <param name="showErrorMessage">True if you want a popup message on errors or false if show no error popup</param> /// <returns>True if success and false if unsuccessful</returns> public bool StartSensingAndStreaming(SummitSystem theSummit, SenseModel senseConfig, bool showErrorMessage) { APIReturnInfo bufferReturnInfo; try { _log.Info("Start Sensing and Streaming"); //This checks to see if sensing is already enabled. //If it is, then skip write sensing state and just start streaming SensingState state; theSummit.ReadSensingState(out state); if (!state.State.ToString().Contains("DetectionLd0") && !state.State.ToString().Contains("DetectionLd1")) { _log.Info("Detection is off. Turn sensing on with LD0/LD1 off"); // Start sensing //LDO is false because if it is in adaptive then it will bypass this. If not in adaptive, don't need it. bufferReturnInfo = theSummit.WriteSensingState(ConfigConversions.TDSenseStatesConvert( senseConfig.SenseOptions.TimeDomain, senseConfig.SenseOptions.FFT, senseConfig.SenseOptions.Power, false, false, senseConfig.SenseOptions.AdaptiveState, senseConfig.SenseOptions.LoopRecording, senseConfig.SenseOptions.Unused), ConfigConversions.FFTChannelConvert(senseConfig)); if (!CheckForReturnError(bufferReturnInfo, "Write Sensing State", showErrorMessage)) { return(false); } } else { _log.Warn("Detection is on. SKIP SENSING AND START STREAMING!"); } // Start streaming bufferReturnInfo = theSummit.WriteSensingEnableStreams( senseConfig.StreamEnables.TimeDomain, senseConfig.StreamEnables.FFT, senseConfig.StreamEnables.Power, senseConfig.StreamEnables.AdaptiveTherapy, senseConfig.StreamEnables.AdaptiveState, senseConfig.StreamEnables.Accelerometry, senseConfig.StreamEnables.TimeStamp, senseConfig.StreamEnables.EventMarker); if (!CheckForReturnError(bufferReturnInfo, "Stream Enables", showErrorMessage)) { return(false); } } catch (Exception error) { _log.Error(error); return(false); } return(true); }
/// <summary> /// Gets the TDSampleRate for the TimeDomain Channel /// Calls TD SampleRateConvert from ConfigConversions class /// Checks for disabled time domain channels and returns proper sample rate or disabled for disabled channels /// </summary> /// <param name="sampleRateIsEnabled">Either true or false depending on value for Time Domain Channel IsEnabled value from config file</param> /// <param name="localModel">Sense model</param> /// <returns>If the sampleRateIsEnabled variable is set to false, then it returns the TdSampleRates.Disabled. Otherwise it returns the correct TdSampleRates variable for the corresponding TD sample rate from the config file</returns> private TdSampleRates GetTDSampleRate(bool sampleRateIsEnabled, SenseModel localModel) { TdSampleRates the_sample_rate = ConfigConversions.TDSampleRateConvert(localModel.Sense.TDSampleRate); if (!sampleRateIsEnabled) { the_sample_rate = TdSampleRates.Disabled; } return(the_sample_rate); }
/// <summary> /// Calculates the fft bins. /// This code is taken from the config ui training code to calculate fft bins /// </summary> /// <param name="fftSize">Size of the FFT.</param> /// <param name="timeRate">The time rate.</param> /// <returns>a list of fft bin boundaries</returns> public List <double> CalculateFFTBins(FftSizes fftSize, TdSampleRates timeRate) { int numBins = 0; switch (fftSize) { case FftSizes.Size0064: numBins = 64 / 2; break; case FftSizes.Size0256: numBins = 256 / 2; break; case FftSizes.Size1024: numBins = 1024 / 2; break; default: ConfigConversions.DisplayErrorMessageAndClose("Could not convert FFT Size for FFT bins"); break; } int rate = 0; switch (timeRate) { case TdSampleRates.Sample0250Hz: rate = 250; break; case TdSampleRates.Sample0500Hz: rate = 500; break; case TdSampleRates.Sample1000Hz: rate = 1000; break; default: ConfigConversions.DisplayErrorMessageAndClose("Could not convert TDSampleRate for FFT bins"); break; } double binWidth = rate / 2.0 / numBins; for (int i = 0; i < numBins; i++) { bins.Add(i * binWidth); } return(bins); }
/// <summary> /// Gets the power band bins and actual hz values. Program shuts down if can't compute /// </summary> /// <param name="localModel">SenseModel</param> /// <returns>List of PowerBandModel class</returns> public List <PowerBandModel> GetPowerBands(SenseModel localModel) { List <PowerBandModel> powerBandsList = new List <PowerBandModel>(); try { CalculateFFTBins(ConfigConversions.FftSizesConvert(localModel.Sense.FFT.FftSize), ConfigConversions.TDSampleRateConvert(localModel.Sense.TDSampleRate)); binWidth = bins[1] - bins[0]; CalculateUpperBins(); CalculateLowerBins(); } catch (Exception e) { HandleException(e); } for (int i = 0; i < 8;) { try { PowerBandModel powerBandModel = new PowerBandModel(); //Gets the lower index value and upper index value based from the config file value. powerBandModel.lowerIndexBand0 = GetLowerIndex(localModel.Sense.PowerBands[i].ChannelPowerBand[0]); powerBandModel.upperIndexBand0 = GetUpperIndex(localModel.Sense.PowerBands[i].ChannelPowerBand[1]); //This checks to make sure that the upper power bin index is bigger than the lower; cannot have upper index that is less than lower index //if the upper index value is not bigger than the lower, then the upper index is set to lower index for error handling powerBandModel.upperIndexBand0 = CheckThatUpperPowerBandGreaterThanLowerPowerBand(powerBandModel.lowerIndexBand0, powerBandModel.upperIndexBand0); //Actual lower and upper power values are calculated from the users estimated power values from the config file //This is done in the CalculatePowerBins class. Below saves the actual lower and upper power values in each array to 2 decimal places. //These arrays are instantiated in MainViewModel.cs powerBandModel.lowerActualValueHzBand0 = Math.Round(ActualLowerPowerValue, 2); powerBandModel.UpperActualValueHzBand0 = Math.Round(ActualUpperPowerValue, 2); //increment i so that the next power band can be set. There are 8 (0-7). There are 4 power channels (0-3); i++; //Repeat this step for next power band powerBandModel.lowerIndexBand1 = GetLowerIndex(localModel.Sense.PowerBands[i].ChannelPowerBand[0]); powerBandModel.upperIndexBand1 = GetUpperIndex(localModel.Sense.PowerBands[i].ChannelPowerBand[1]); powerBandModel.upperIndexBand1 = CheckThatUpperPowerBandGreaterThanLowerPowerBand(powerBandModel.lowerIndexBand1, powerBandModel.upperIndexBand1); powerBandModel.lowerActualValueHzBand1 = Math.Round(ActualLowerPowerValue, 2); powerBandModel.upperActualValueHzBand1 = Math.Round(ActualUpperPowerValue, 2); i++; powerBandsList.Add(powerBandModel); } catch (Exception e) { HandleException(e); } } return(powerBandsList); }
/// <summary> /// Connects to CTM starting from the 0th value of the list of CTM's and works its way to the Nth. This considered the right in bilateral /// </summary> /// <param name="theSummitManager">Summit manager</param> /// <param name="theSummit">Summit System</param> /// <param name="senseModel">Model for sense from config file</param> /// <param name="appModel">Model for application from config file</param> /// <param name="_log">Caliburn Micro Logger</param> /// <returns>true if connected or false if not connected</returns> public override bool ConnectCTM(SummitManager theSummitManager, ref SummitSystem theSummit, SenseModel senseModel, AppModel appModel, ILog _log) { _log.Info("Checking USB for unbonded CTMs. Please make sure they are powered on."); theSummitManager?.GetUsbTelemetry(); // Retrieve a list of known and bonded telemetry List <InstrumentInfo> knownTelemetry = theSummitManager?.GetKnownTelemetry(); // Check if any CTMs are currently bonded, poll the USB if not so that the user can be prompted to plug in a CTM over USB if (knownTelemetry?.Count == 0) { do { // Inform user we will loop until a CTM is found on USBs _log.Warn("No bonded CTMs found, please plug a CTM in via USB..."); Thread.Sleep(2000); // Bond with any CTMs plugged in over USB knownTelemetry = theSummitManager?.GetUsbTelemetry(); } while (knownTelemetry?.Count == 0); } // Connect to the first CTM available, then try others if it fails SummitSystem tempSummit = null; try { for (int i = 0; i < theSummitManager.GetKnownTelemetry().Count; i++) { ManagerConnectStatus connectReturn; if (appModel?.CTMBeepEnables == null) { connectReturn = theSummitManager.CreateSummit(out tempSummit, theSummitManager.GetKnownTelemetry()[i], InstrumentPhysicalLayers.Any, senseModel.Mode, senseModel.Ratio, CtmBeepEnables.None); } else if (!appModel.CTMBeepEnables.DeviceDiscovered && !appModel.CTMBeepEnables.GeneralAlert && !appModel.CTMBeepEnables.NoDeviceDiscovered && !appModel.CTMBeepEnables.TelMCompleted && !appModel.CTMBeepEnables.TelMLost) { connectReturn = theSummitManager.CreateSummit(out tempSummit, theSummitManager.GetKnownTelemetry()[i], InstrumentPhysicalLayers.Any, senseModel.Mode, senseModel.Ratio, CtmBeepEnables.None); } else { connectReturn = theSummitManager.CreateSummit(out tempSummit, theSummitManager.GetKnownTelemetry()[i], InstrumentPhysicalLayers.Any, senseModel.Mode, senseModel.Ratio, ConfigConversions.BeepEnablesConvert(appModel)); } // Write out the result _log.Info("Create Summit Result: " + connectReturn.ToString()); // Break if it failed successful if (tempSummit != null && connectReturn.HasFlag(ManagerConnectStatus.Success)) { break; } } } catch (Exception e) { _log.Error(e); } // Make sure telemetry was connected to, if not fail if (tempSummit == null) { // inform user that CTM was not successfully connected to if (hasShownMessageToUser) { MainViewModel.ShowMessageBox("Could not connect to CTM. Check that CTM is placed correctly over INS on chest. If problem persists, please inform researcher of problem.", "Connection to CTM Failed", MessageBoxButton.OK, MessageBoxImage.Error); hasShownMessageToUser = false; } return(false); } else { theSummit = tempSummit; return(true); } }
/// <summary> /// Write Sense Configuration Settings /// </summary> /// <param name="localModel">The config file to use for sensing parameters</param> /// <param name="theSummit">Summit system</param> /// <param name="showErrorMessage">True if you want a popup message on errors or false if show no error popup</param> /// <returns>true if successfully configuring sensing or false if unsuccessful</returns> public bool SummitConfigureSensing(SummitSystem theSummit, SenseModel localModel, bool showErrorMessage) { APIReturnInfo bufferReturnInfo; //This checks to see if sensing is already enabled. This can happen if adaptive is already running and we don't need to configure it. //If it is, then skip setting up sensing if (CheckIsAdaptiveRunning(theSummit)) { return(true); } int counter = 5; while (counter > 0) { if (StopSensing(theSummit, showErrorMessage)) { break; } else { _log.Warn("Could not stop sensing before configure sensing. Trying again..."); counter--; Thread.Sleep(500); } } if (counter == 0) { _log.Warn("Could not stop sensing before configure sensing. Returning false"); return(false); } // Create a sensing configuration List <TimeDomainChannel> TimeDomainChannels = new List <TimeDomainChannel>(4); // Channel Specific configuration - 0 TimeDomainChannels.Add(new TimeDomainChannel( GetTDSampleRate(localModel.Sense.TimeDomains[0].IsEnabled, localModel), ConfigConversions.TdMuxInputsConvert(localModel.Sense.TimeDomains[0].Inputs[0]), ConfigConversions.TdMuxInputsConvert(localModel.Sense.TimeDomains[0].Inputs[1]), ConfigConversions.TdEvokedResponseEnableConvert(localModel.Sense.TimeDomains[0].TdEvokedResponseEnable), ConfigConversions.TdLpfStage1Convert(localModel.Sense.TimeDomains[0].Lpf1), ConfigConversions.TdLpfStage2Convert(localModel.Sense.TimeDomains[0].Lpf2), ConfigConversions.TdHpfsConvert(localModel.Sense.TimeDomains[0].Hpf))); // Channel Specific configuration - 1 TimeDomainChannels.Add(new TimeDomainChannel( GetTDSampleRate(localModel.Sense.TimeDomains[1].IsEnabled, localModel), ConfigConversions.TdMuxInputsConvert(localModel.Sense.TimeDomains[1].Inputs[0]), ConfigConversions.TdMuxInputsConvert(localModel.Sense.TimeDomains[1].Inputs[1]), ConfigConversions.TdEvokedResponseEnableConvert(localModel.Sense.TimeDomains[1].TdEvokedResponseEnable), ConfigConversions.TdLpfStage1Convert(localModel.Sense.TimeDomains[1].Lpf1), ConfigConversions.TdLpfStage2Convert(localModel.Sense.TimeDomains[1].Lpf2), ConfigConversions.TdHpfsConvert(localModel.Sense.TimeDomains[1].Hpf))); // Channel Specific configuration - 2 TimeDomainChannels.Add(new TimeDomainChannel( GetTDSampleRate(localModel.Sense.TimeDomains[2].IsEnabled, localModel), ConfigConversions.TdMuxInputsConvert(localModel.Sense.TimeDomains[2].Inputs[0]), ConfigConversions.TdMuxInputsConvert(localModel.Sense.TimeDomains[2].Inputs[1]), ConfigConversions.TdEvokedResponseEnableConvert(localModel.Sense.TimeDomains[2].TdEvokedResponseEnable), ConfigConversions.TdLpfStage1Convert(localModel.Sense.TimeDomains[2].Lpf1), ConfigConversions.TdLpfStage2Convert(localModel.Sense.TimeDomains[2].Lpf2), ConfigConversions.TdHpfsConvert(localModel.Sense.TimeDomains[2].Hpf))); // Channel Specific configuration - 3 TimeDomainChannels.Add(new TimeDomainChannel( GetTDSampleRate(localModel.Sense.TimeDomains[3].IsEnabled, localModel), ConfigConversions.TdMuxInputsConvert(localModel.Sense.TimeDomains[3].Inputs[0]), ConfigConversions.TdMuxInputsConvert(localModel.Sense.TimeDomains[3].Inputs[1]), ConfigConversions.TdEvokedResponseEnableConvert(localModel.Sense.TimeDomains[3].TdEvokedResponseEnable), ConfigConversions.TdLpfStage1Convert(localModel.Sense.TimeDomains[3].Lpf1), ConfigConversions.TdLpfStage2Convert(localModel.Sense.TimeDomains[3].Lpf2), ConfigConversions.TdHpfsConvert(localModel.Sense.TimeDomains[3].Hpf))); // Set up the FFT FftConfiguration fftChannel = new FftConfiguration( ConfigConversions.FftSizesConvert(localModel.Sense.FFT.FftSize), localModel.Sense.FFT.FftInterval, ConfigConversions.FftWindowAutoLoadsConvert(localModel.Sense.FFT.WindowLoad), localModel.Sense.FFT.WindowEnabled, ConfigConversions.FftWeightMultipliesConvert(localModel.Sense.FFT.WeightMultiplies), localModel.Sense.FFT.StreamSizeBins, localModel.Sense.FFT.StreamOffsetBins); // Set up the Power channels List <PowerChannel> powerChannels = new List <PowerChannel>(); //This goes through each power channel and gets the lower power band and upper power band. //Medtronic api uses bin index values for setting power channels instead of actual values in Hz //This calls the CalculatePowerBins class to convert to proper medtronic api values from the user config file //User config file has estimated values in Hz for each channel. //CalculatePowerBins converts them to actual power values and we are able to get the bin index value from that. CalculatePowerBins calculatePowerBins = new CalculatePowerBins(_log); List <PowerBandModel> powerBandsList = calculatePowerBins.GetPowerBands(localModel); if (powerBandsList == null || powerBandsList.Count < 3) { MessageBox.Show("Error calculating power bins. Please check that power bins are correct in the config file and try again.", "Error", MessageBoxButton.OK, MessageBoxImage.Hand); return(false); } for (int i = 0; i < 4; i++) { //Add the lower and upper power bands to the power channel powerChannels.Add(new PowerChannel(powerBandsList[i].lowerIndexBand0, powerBandsList[i].upperIndexBand0, powerBandsList[i].lowerIndexBand1, powerBandsList[i].upperIndexBand1)); } //Gets the enabled power bands from the sense config file and returns the correct api call for all enabled BandEnables theBandEnables = ConfigConversions.PowerBandEnablesConvert(localModel); // Set up the miscellaneous settings MiscellaneousSensing miscsettings = new MiscellaneousSensing(); miscsettings.StreamingRate = ConfigConversions.MiscStreamRateConvert(localModel.Sense.Misc.StreamingRate); miscsettings.LrTriggers = ConfigConversions.MiscloopRecordingTriggersConvert(localModel.Sense.Misc.LoopRecordingTriggersState, localModel.Sense.Misc.LoopRecordingTriggersIsEnabled); miscsettings.LrPostBufferTime = localModel.Sense.Misc.LoopRecordingPostBufferTime; miscsettings.Bridging = ConfigConversions.MiscBridgingConfigConvert(localModel.Sense.Misc.Bridging); //Writes all sensing information here try { bufferReturnInfo = theSummit.WriteSensingTimeDomainChannels(TimeDomainChannels); if (!CheckForReturnError(bufferReturnInfo, "Writing Sensing Time Domain", showErrorMessage)) { return(false); } bufferReturnInfo = theSummit.WriteSensingFftSettings(fftChannel); if (!CheckForReturnError(bufferReturnInfo, "Writing Sensing FFT", showErrorMessage)) { return(false); } bufferReturnInfo = theSummit.WriteSensingPowerChannels(theBandEnables, powerChannels); if (!CheckForReturnError(bufferReturnInfo, "Writing Sensing Power", showErrorMessage)) { return(false); } bufferReturnInfo = theSummit.WriteSensingMiscSettings(miscsettings); if (!CheckForReturnError(bufferReturnInfo, "Writing Sensing Misc", showErrorMessage)) { return(false); } bufferReturnInfo = theSummit.WriteSensingAccelSettings(ConfigConversions.AccelSampleRateConvert(localModel.Sense.Accelerometer.SampleRate, localModel.Sense.Accelerometer.SampleRateDisabled)); if (!CheckForReturnError(bufferReturnInfo, "Writing Sensing Accel", showErrorMessage)) { return(false); } } catch (Exception error) { _log.Error(error); return(false); } return(true); }