/// <summary>Acquires the incoming signal, trains the DPD model, applies the memory polynomial to the reference waveform, and downloads the predistorted waveform to the generator.
        ///  If generation is in progress when this function is called, generation will resume with the predistorted waveform.</summary>
        /// <param name="specAn">Specifies the SpecAn signal to configure.</param>
        /// <param name="rfsgSession">Specifies the open RFSG session to configure.</param>
        /// <param name="referenceWaveform">Specifies the <see cref="Waveform"/> whose data defines the complex baseband equivalent of the RF signal applied at the input
        /// port of the device under test when performing the measurement. See the RFmx help for more documention of this parameter.</param>
        /// <param name="selectorString">Pass an empty string. The signal name that is passed when creating the signal configuration is used.
        /// See the RFmx help for more documention of this parameter.</param>
        /// <returns>Common results after the application of memory polynomial based DPD.</returns>
        public static MemoryPolynomialResults PerformMemoryPolynomial(RFmxSpecAnMX specAn, NIRfsg rfsgSession, MemoryPolynomialConfiguration mpConfig,
                                                                      Waveform referenceWaveform, string selectorString = "")
        {
            //Instantiate new waveform with reference waveform properties
            MemoryPolynomialResults mpResults = new MemoryPolynomialResults()
            {
                PredistortedWaveform = referenceWaveform
            };

            mpResults.PredistortedWaveform.Data = referenceWaveform.Data.Clone(); // clone waveform so RFmx can't act on reference waveform
            mpResults.PredistortedWaveform.UpdateNameAndScript(referenceWaveform.Name + "postMpDpd");

            RFmxSpecAnMXDpdApplyDpdIdleDurationPresent idlePresent = referenceWaveform.IdleDurationPresent ? RFmxSpecAnMXDpdApplyDpdIdleDurationPresent.True : RFmxSpecAnMXDpdApplyDpdIdleDurationPresent.False;

            RfsgGenerationStatus preDpdGenerationStatus = rfsgSession.CheckGenerationStatus();

            rfsgSession.Abort(); // abort so we don't mess with the loop logic

            for (int i = 0; i < mpConfig.NumberOfIterations; i++)
            {
                specAn.Dpd.Configuration.ConfigurePreviousDpdPolynomial(selectorString, mpResults.Polynomial);
                rfsgSession.Initiate();
                specAn.Initiate(selectorString, "");
                specAn.WaitForMeasurementComplete(selectorString, 10.0); // wait for polynomial coefficients to be calculated
                                                                         //waveform data and PAPR are overwritten in post DPD waveform
                specAn.Dpd.ApplyDpd.ApplyDigitalPredistortion(selectorString, referenceWaveform.Data, idlePresent, 10.0, ref mpResults.PredistortedWaveform.Data,
                                                              out mpResults.PowerResults.WaveformTruePapr_dB, out mpResults.PowerResults.WaveformPowerOffset_dB);
                //Waveform's PAPR is modified to adjust the output power of the waveform on a per waveform basis rather than changing the
                //user's configured output power on the instrument
                mpResults.PredistortedWaveform.PAPR_dB = mpResults.PowerResults.WaveformPowerOffset_dB + mpResults.PowerResults.WaveformTruePapr_dB;
                DownloadWaveform(rfsgSession, mpResults.PredistortedWaveform); // implicit abort
                ApplyWaveformAttributes(rfsgSession, mpResults.PredistortedWaveform);
                specAn.Dpd.Results.FetchDpdPolynomial(selectorString, 10.0, ref mpResults.Polynomial);
            }

            mpResults.PowerResults.TrainingPower_dBm = rfsgSession.RF.PowerLevel;
            if (preDpdGenerationStatus == RfsgGenerationStatus.InProgress)
            {
                rfsgSession.Initiate(); // restart generation if it was running on function call
            }
            return(mpResults);
        }
        /// <summary>Acquires the incoming signal, trains the DPD model, applies the lookup table to the reference waveform, and downloads the predistorted waveform to the generator.
        ///  If generation is in progress when this function is called, generation will resume with the predistorted waveform.</summary>
        /// <param name="specAn">Specifies the SpecAn signal to configure.</param>
        /// <param name="rfsgSession">Specifies the open RFSG session to configure.</param>
        /// <param name="referenceWaveform">Specifies the <see cref="Waveform"/> whose data defines the complex baseband equivalent of the RF signal applied at the input
        /// port of the device under test when performing the measurement. See the RFmx help for more documention of this parameter.</param>
        /// <param name="selectorString">Pass an empty string. The signal name that is passed when creating the signal configuration is used.
        /// See the RFmx help for more documention of this parameter.</param>
        /// <returns>Common results after the application of lookup table based DPD.</returns>
        public static LookupTableResults PerformLookupTable(RFmxSpecAnMX specAn, NIRfsg rfsgSession, Waveform referenceWaveform, string selectorString = "")
        {
            //Instantiate new waveform with reference waveform properties
            LookupTableResults lutResults = new LookupTableResults()
            {
                PredistortedWaveform = referenceWaveform,
            };

            lutResults.PredistortedWaveform.Data = referenceWaveform.Data.Clone(); // clone waveform so RFmx can't act on reference waveform
            lutResults.PredistortedWaveform.UpdateNameAndScript(referenceWaveform.Name + "postLutDpd");

            RfsgGenerationStatus preDpdGenerationStatus = rfsgSession.CheckGenerationStatus();

            if (preDpdGenerationStatus == RfsgGenerationStatus.Complete)
            {
                rfsgSession.Initiate(); // initiate if not already generating
            }
            specAn.Initiate(selectorString, "");
            RFmxSpecAnMXDpdApplyDpdIdleDurationPresent idlePresent = referenceWaveform.IdleDurationPresent ? RFmxSpecAnMXDpdApplyDpdIdleDurationPresent.True : RFmxSpecAnMXDpdApplyDpdIdleDurationPresent.False;

            specAn.WaitForMeasurementComplete(selectorString, 10.0); // wait for LUT creation to finish
                                                                     //waveform data and PAPR are overwritten in post DPD waveform
            specAn.Dpd.ApplyDpd.ApplyDigitalPredistortion(selectorString, referenceWaveform.Data, idlePresent, 10.0, ref lutResults.PredistortedWaveform.Data,
                                                          out lutResults.PowerResults.WaveformTruePapr_dB, out lutResults.PowerResults.WaveformPowerOffset_dB);

            //Waveform's PAPR is modified to adjust the output power of the waveform on a per waveform basis rather than changing the
            //user's configured output power on the instrument
            lutResults.PredistortedWaveform.PAPR_dB = lutResults.PowerResults.WaveformPowerOffset_dB + lutResults.PowerResults.WaveformTruePapr_dB;
            DownloadWaveform(rfsgSession, lutResults.PredistortedWaveform); // implicit call to rfsg abort
            ApplyWaveformAttributes(rfsgSession, lutResults.PredistortedWaveform);
            lutResults.PowerResults.TrainingPower_dBm = rfsgSession.RF.PowerLevel;
            specAn.Dpd.Results.FetchLookupTable(selectorString, 10.0, ref lutResults.InputPowers_dBm, ref lutResults.ComplexGains_dB);

            if (preDpdGenerationStatus == RfsgGenerationStatus.InProgress)
            {
                rfsgSession.Initiate(); // restart generation if it was running on function call
            }
            return(lutResults);
        }
        /// <summary>Configures common pre-DPD CFR settings and applies CFR to the reference waveform, which is returned by the function.</summary>
        /// <param name="specAn">Specifies the SpecAn signal to configure.</param>
        /// <param name="referenceWaveform">Specifies the <see cref="Waveform"/> whose data defines the complex baseband equivalent of the RF signal on which the
        /// pre-DPD signal conditioning is applied. See the RFmx help for more documention of this parameter.</param>
        /// <param name="preDpdCfrConfig">Specifies common pre-DPD CFR settings to apply.</param>
        /// <param name="selectorString">Pass an empty string. The signal name that is passed when creating the signal configuration is used.
        /// See the RFmx help for more documention of this parameter.</param>
        /// <returns>The reference waveform with CFR applied.</returns>
        public static Waveform ConfigurePreDpdCrestFactorReduction(RFmxSpecAnMX specAn, Waveform referenceWaveform, PreDpdCrestFactorReductionConfiguration preDpdCfrConfig, string selectorString = "")
        {
            Waveform preDpdCfrWaveform = referenceWaveform;

            RFmxSpecAnMXDpdApplyDpdIdleDurationPresent preDpdCfrIdlePresent = preDpdCfrWaveform.IdleDurationPresent ?
                                                                              RFmxSpecAnMXDpdApplyDpdIdleDurationPresent.True : RFmxSpecAnMXDpdApplyDpdIdleDurationPresent.False;

            specAn.Dpd.PreDpd.SetCfrEnabled(selectorString, preDpdCfrConfig.Enabled);
            specAn.Dpd.PreDpd.SetCfrMethod(selectorString, preDpdCfrConfig.Method);
            specAn.Dpd.PreDpd.SetCfrMaximumIterations(selectorString, preDpdCfrConfig.MaxIterations);
            specAn.Dpd.PreDpd.SetCfrTargetPapr(selectorString, preDpdCfrConfig.TargetPapr_dB);
            specAn.Dpd.PreDpd.SetCfrWindowType(selectorString, preDpdCfrConfig.WindowType);
            specAn.Dpd.PreDpd.SetCfrWindowLength(selectorString, preDpdCfrConfig.WindowLength);
            specAn.Dpd.PreDpd.SetCfrShapingFactor(selectorString, preDpdCfrConfig.ShapingFactor);
            specAn.Dpd.PreDpd.SetCfrShapingThreshold(selectorString, preDpdCfrConfig.ShapingThreshold_dB);
            specAn.Dpd.PreDpd.SetCfrFilterEnabled(selectorString, preDpdCfrConfig.FilterEnabled);
            specAn.Dpd.PreDpd.SetCfrNumberOfCarriers(selectorString, preDpdCfrConfig.CarrierChannels.Length);

            // Configure the carrier channels
            for (int i = 0; i < preDpdCfrConfig.CarrierChannels.Length; i++)
            {
                string carrierString = RFmxSpecAnMX.BuildCarrierString2(selectorString, i);
                specAn.Dpd.PreDpd.SetCarrierOffset(carrierString, preDpdCfrConfig.CarrierChannels[i].Offset_Hz);
                specAn.Dpd.PreDpd.SetCarrierBandwidth(carrierString, preDpdCfrConfig.CarrierChannels[i].Bandwidth_Hz);
            }

            // Only call Apply Pre-DPD CFR and modify the waveform if Pre-DPD CFR is enabled
            if (preDpdCfrConfig.Enabled == RFmxSpecAnMXDpdPreDpdCfrEnabled.True)
            {
                // Configure the new waveform
                preDpdCfrWaveform.Data = referenceWaveform.Data.Clone(); // clone waveform so RFmx can't act on reference waveform
                preDpdCfrWaveform.UpdateNameAndScript(referenceWaveform.Name + "preDPDCFR");

                // Apply CFR and return
                specAn.Dpd.PreDpd.ApplyPreDpdSignalConditioning(selectorString, referenceWaveform.Data, preDpdCfrIdlePresent, ref preDpdCfrWaveform.Data, out preDpdCfrWaveform.PAPR_dB);
            }
            return(preDpdCfrWaveform);
        }