public static void DownloadWaveform(NIRfsg rfsgHandle, Waveform waveform)
        {
            IntPtr rfsgPtr = rfsgHandle.GetInstrumentHandle().DangerousGetHandle();

            rfsgHandle.Abort();

            rfsgHandle.RF.PowerLevelType = RfsgRFPowerLevelType.PeakPower;

            try
            {
                rfsgHandle.Arb.ClearWaveform(waveform.WaveformName); //Clear existing waveform to avoid erros
            }
            catch (Ivi.Driver.OutOfRangeException)
            { //Intentionally ignore this exception; clearing the waveform failed because it doesn't exist
            }

            rfsgHandle.Arb.WriteWaveform(waveform.WaveformName, waveform.WaveformData);

            //Store loaded parameters
            NIRfsgPlayback.StoreWaveformSignalBandwidth(rfsgPtr, waveform.WaveformName, waveform.SignalBandwidth_Hz);
            NIRfsgPlayback.StoreWaveformPapr(rfsgPtr, waveform.WaveformName, waveform.PAPR_dB);
            NIRfsgPlayback.StoreWaveformBurstStartLocations(rfsgPtr, waveform.WaveformName, waveform.BurstStartLocations);
            NIRfsgPlayback.StoreWaveformBurstStopLocations(rfsgPtr, waveform.WaveformName, waveform.BurstStopLocations);
            NIRfsgPlayback.StoreWaveformSampleRate(rfsgPtr, waveform.WaveformName, waveform.SampleRate);

            //Manually configure additional settings
            NIRfsgPlayback.StoreWaveformLOOffsetMode(rfsgPtr, waveform.WaveformName, NIRfsgPlaybackLOOffsetMode.Disabled);
            NIRfsgPlayback.StoreWaveformRuntimeScaling(rfsgPtr, waveform.WaveformName, -1.5);
            NIRfsgPlayback.StoreWaveformRFBlankingEnabled(rfsgPtr, waveform.WaveformName, false);
        }
        private static void NormalizeWaveform(ref Waveform waveform)
        {
            //Normalize the waveform data

            double[]      magnitudeArray = waveform.WaveformData.GetMagnitudeDataArray(false);
            ComplexSingle waveformMax    = new ComplexSingle((float)magnitudeArray.Max(), 0);
            WritableBuffer <ComplexSingle> waveformBuffer = waveform.WaveformData.GetWritableBuffer();

            for (int i = 0; i < waveformBuffer.Count(); i++)
            {
                waveformBuffer[i] = waveformBuffer[i] / waveformMax; //Scale each point by the max value
            }

            //Calculate PAPR over only the burst length

            int offset = waveform.BurstStartLocations[0];
            int count  = waveform.BurstStopLocations[0] - offset;

            double[] waveformBurst = new double[count];
            //Retrieve the subset of the magnitude waveform covering the waveform burst
            Array.Copy(waveform.WaveformData.GetMagnitudeDataArray(false), offset, waveformBurst, 0, count);

            for (int i = 0; i < count; i++)
            {
                waveformBurst[i] = Math.Pow(waveformBurst[i], 2);
            }
            double rms = Math.Sqrt(waveformBurst.Sum() / count); //RMS = sqrt(1/n*(|x_0|^2+|x_1|^2...|x_n|^2))

            //PAPR (Peak to Average Power Ratio) = Peak Power/Avg Power
            //PAPR (dB) = 20*log(max/avg)
            //Since we already scaled the data, the max value is simply 1
            waveform.PAPR_dB = 20 * Math.Log10(1 / rms);
        }
Beispiel #3
0
 /// <summary>Calls <see cref="NIRfsgPlayback.SetScriptToGenerateSingleRfsg(IntPtr, string)"/>, which will download the script contained in <paramref name="waveform"/> and apply
 /// all associated parameters.</summary>
 /// <param name="rfsgHandle">The open RFSG session to configure.</param>
 /// <param name="waveform">Specifies the waveform and its associated script that is to be used for generation.</param>
 public static void ApplyWaveformAttributes(NIRfsg rfsgHandle, Waveform waveform)
 {
     if (string.IsNullOrEmpty(waveform.Script)) // default to continuous if no script in waveform
     {
         ConfigureContinuousGeneration(rfsgHandle, waveform);
     }
     else
     {
         IntPtr rfsgPtr = rfsgHandle.GetInstrumentHandle().DangerousGetHandle();
         NIRfsgPlayback.SetScriptToGenerateSingleRfsg(rfsgPtr, waveform.Script);
     }
 }
        public static Waveform LoadWaveformFromTDMS(string filePath, string waveformName = "", bool normalizeWaveform = true)
        {
            Waveform waveform = new Waveform();

            if (string.IsNullOrEmpty(waveformName))
            {
                waveformName = Path.GetFileNameWithoutExtension(filePath);
                Utilities.FormatWaveformName(ref waveformName);
            }
            waveform.WaveformName = waveformName;
            NIRfsgPlayback.ReadWaveformFromFileComplex(filePath, ref waveform.WaveformData);
            NIRfsgPlayback.ReadSignalBandwidthFromFile(filePath, 0, out waveform.SignalBandwidth_Hz);

            NIRfsgPlayback.ReadWaveformFileVersionFromFile(filePath, out string waveformVersion);
            if (waveformVersion == "1.0.0")
            {
                NIRfsgPlayback.ReadPeakPowerAdjustmentFromFile(filePath, 0, out waveform.PAPR_dB);
            }
            else
            {
                NIRfsgPlayback.ReadPaprFromFile(filePath, 0, out waveform.PAPR_dB);  //Version 2.0.0 and later
            }
            NIRfsgPlayback.ReadBurstStartLocationsFromFile(filePath, 0, ref waveform.BurstStartLocations);
            NIRfsgPlayback.ReadBurstStopLocationsFromFile(filePath, 0, ref waveform.BurstStopLocations);

            //Statement reads: if NOT BurstStartLocations > 0 AND expression is not null (? operand)
            //In other words, manually set BurstStartLocations when the length is 0 or less or array is null
            if (!(waveform.BurstStartLocations?.Length > 0))
            {
                //Set burst start to the first sample(0)
                waveform.BurstStartLocations = new int[1] {
                    0
                };
            }
            if (!(waveform.BurstStopLocations?.Length > 0))
            {
                //Set burst stop to the last sample (number of samples minus one)
                waveform.BurstStopLocations = new int[1] {
                    waveform.WaveformData.SampleCount - 1
                };
            }

            NIRfsgPlayback.ReadSampleRateFromFile(filePath, 0, out waveform.SampleRate);
            waveform.BurstLength_s = CalculateWaveformDuration(waveform.BurstStartLocations, waveform.BurstStopLocations, waveform.SampleRate);

            if (normalizeWaveform)
            {
                NormalizeWaveform(waveform);
            }

            return(waveform);
        }
        public static Waveform GetWaveformParametersByName(NIRfsg rfsgHandle, string waveformName)
        {
            IntPtr rfsgPtr = rfsgHandle.GetInstrumentHandle().DangerousGetHandle();

            Waveform waveform = new Waveform
            {
                WaveformName = waveformName
            };

            NIRfsgPlayback.RetrieveWaveformSignalBandwidth(rfsgPtr, waveformName, out waveform.SignalBandwidth_Hz);
            NIRfsgPlayback.RetrieveWaveformPapr(rfsgPtr, waveformName, out waveform.PAPR_dB);
            NIRfsgPlayback.RetrieveWaveformSampleRate(rfsgPtr, waveformName, out waveform.SampleRate);
            NIRfsgPlayback.RetrieveWaveformBurstStartLocations(rfsgPtr, waveformName, ref waveform.BurstStartLocations);
            NIRfsgPlayback.RetrieveWaveformBurstStopLocations(rfsgPtr, waveformName, ref waveform.BurstStopLocations);

            waveform.BurstLength_s = CalculateWaveformDuration(waveform.BurstStartLocations, waveform.BurstStopLocations, waveform.SampleRate);

            return(waveform);
        }
Beispiel #6
0
        /// <summary>Configures the generator to generate the waveform plus idle time continuously for Dynamic EVM test cases. Also configures triggering or pulsing settings to
        /// control the DUT. Use <see cref="AbortGeneration(NIRfsg, int)"/> to abort generation to always ensure that the DUT state is returned to the initial state.</summary>
        /// <param name="rfsgHandle">The open RFSG session to configure.</param>
        /// <param name="waveform">Specifies the waveform to generate; its burst length will be used in conjuction with the duty cycle to calculate the idle time.
        /// Call <see cref="DownloadWaveform(NIRfsg, Waveform)"/> prior to calling this function.</param>
        /// <param name="waveTiming">Specifies the timing parameters used to configure the bursted generation.</param>
        /// <param name="paenConfig">Specifies parameters pertaining to how the DUT is controlled during generation.</param>
        /// <param name="period">Returns the total generation period, consisting of the waveform burst length, idle time, and any additional pre/post burst time configured.</param>
        /// <param name="idleTime">Returns the computed idle time based upon the requested duty cycle.</param>
        /// <returns>The Waveform with the "Script" parameter set to the newly created generation script.</returns>
        public static Waveform ConfigureBurstedGeneration(NIRfsg rfsgHandle, Waveform waveform, WaveformTimingConfiguration waveTiming,
                                                          PAENConfiguration paenConfig, out double period, out double idleTime)
        {
            // Validate input parameters
            if (waveTiming.DutyCycle_Percent <= 0 || waveTiming.DutyCycle_Percent >= 100)
            {
                throw new ArgumentOutOfRangeException("DutyCycle_Percent", waveTiming.DutyCycle_Percent, "Duty cycle must be greater than 0% and less than 100%. " +
                                                      "For a duty cycle of 100%, use SG.ConfigureContinuous generation instead.");
            }
            if (waveTiming.PreBurstTime_s <= 0 || waveTiming.PostBurstTime_s <= 0)
            {
                throw new ArgumentOutOfRangeException("PreBurstTime | PostBurstTime", "PreBurstTime and PostBurstTime must be greater than 0 seconds");
            }

            //Download the generation script to the generator for later initiation
            waveform.Script = GenerateBurstedScript(paenConfig, waveTiming, waveform, out period, out idleTime);
            ApplyWaveformAttributes(rfsgHandle, waveform);

            //Configure the triggering for PA enable if selected
            if (paenConfig.PAEnableMode != PAENMode.Disabled)
            {
                rfsgHandle.DeviceEvents.MarkerEvents[1].ExportedOutputTerminal = RfsgMarkerEventExportedOutputTerminal.FromString(
                    paenConfig.PAEnableTriggerExportTerminal);
                rfsgHandle.DeviceEvents.MarkerEvents[1].OutputBehaviour = paenConfig.PAEnableTriggerMode;

                // Ensure that the initial state for the digital line is low when using toggle mode to ensure the DUT state is correct
                rfsgHandle.DeviceEvents.MarkerEvents[1].ToggleInitialState = RfsgMarkerEventToggleInitialState.DigitalLow;
            }
            //Configure scriptTrigger0 for software triggering. This way, when it is time to abort we can stop
            //the loop and trigger the appropriate off command if PAEN mode is Static
            rfsgHandle.Triggers.ScriptTriggers[0].ConfigureSoftwareTrigger();

            //Configure the trigger to be generated on the first sample of each waveform generation,
            //denoted in the script below as "marker0"
            rfsgHandle.DeviceEvents.MarkerEvents[0].ExportedOutputTerminal =
                RfsgMarkerEventExportedOutputTerminal.FromString(waveTiming.BurstStartTriggerExport);

            // Return updated waveform struct to caller
            return(waveform);
        }
        private static void NormalizeWaveform(Waveform waveform)
        {
            // Normalize the waveform data
            float[] magnitudeArray = ComplexSingle.GetMagnitudes(waveform.WaveformData.GetRawData());
            float   magnitudeMax   = magnitudeArray.Max();
            WritableBuffer <ComplexSingle> waveformBuffer = waveform.WaveformData.GetWritableBuffer();

            for (int i = 0; i < waveformBuffer.Count(); i++)
            {
                waveformBuffer[i] = ComplexSingle.FromPolar(waveformBuffer[i].Magnitude / magnitudeMax, waveformBuffer[i].Phase);
            }

            // Calculate PAPR over only the burst length
            double burstPowerSum    = 0;
            int    burstSampleCount = 0;

            for (int i = 0; i < waveform.BurstStartLocations.Length; i++)
            {
                int offset = waveform.BurstStartLocations[i];
                int count  = waveform.BurstStopLocations[i] - offset;
                burstSampleCount += count;
                foreach (ComplexSingle iqPoint in waveformBuffer.Skip(offset).Take(count))
                {
                    burstPowerSum += iqPoint.Real * iqPoint.Real + iqPoint.Imaginary * iqPoint.Imaginary;
                }
            }

            // RMS = sqrt(1/n*(|x_0|^2+|x_1|^2...|x_n|^2))
            // |x_n| = sqrt(i_n^2 + q_n^2) therefore |x_n|^2 = i_n^2 + q_n^2
            // RMS Power = v_rms^2 = 1/n*(|x_0|^2+|x_1|^2...|x_n|^2) hence p_rms = p_avg

            // averagePower = burstPowerSum / burstSampleCount;

            // PAPR (Peak to Average Power Ratio) = Peak Power/Avg Power
            // PAPR (dB) = 10*log(Peak Power/Avg Power)
            // Since we already scaled the data, the max value is simply 1
            // instead of doing waveform.PAPR_dB = 10 * Math.Log10(1 / averagePower) we will save a divide and invert the averagePower calculation

            waveform.PAPR_dB = 10 * Math.Log10(burstSampleCount / burstPowerSum);
        }
        public static void ConfigureContinuousGeneration(NIRfsg rfsgHandle, Waveform waveform, string waveformStartTriggerExport = "PXI_Trig0")
        {
            //Configure the trigger to be generated on the first sample of each waveform generation,
            //denoted in the script below as "marker0"
            rfsgHandle.DeviceEvents.MarkerEvents[0].ExportedOutputTerminal = RfsgMarkerEventExportedOutputTerminal.FromString(waveformStartTriggerExport);

            //A software trigger is configured that is used in the script below to control generation of
            //the script. This ensures that a complete packet is always generated before aborting, and
            //allows all generation functions to share a single abort function.
            rfsgHandle.Triggers.ScriptTriggers[0].ConfigureSoftwareTrigger();

            string script = $@"script REPEAT{waveform.WaveformName}
                                    repeat until ScriptTrigger0
                                        generate {waveform.WaveformName} marker0(0)
                                    end repeat
                                end script";

            //Get the instrument handle to utilize with the RFSGPlayback library
            IntPtr rfsgPtr = rfsgHandle.GetInstrumentHandle().DangerousGetHandle();

            //Download the newly created script for generation when "Initiate" is called
            NIRfsgPlayback.SetScriptToGenerateSingleRfsg(rfsgPtr, script);
        }
Beispiel #9
0
        /// <summary>Downloads a previously loaded waveform to the instrument and sets associated properties.</summary>
        /// <param name="rfsgHandle">The open RFSG session to configure.</param>
        /// <param name="waveform">The waveform data and associated properties to download to the instrument. Use <see cref="LoadWaveformFromTDMS(string, string)"/> to load a waveform from a TDMS file.</param>
        public static void DownloadWaveform(NIRfsg rfsgHandle, Waveform waveform)
        {
            IntPtr rfsgPtr = rfsgHandle.GetInstrumentHandle().DangerousGetHandle();

            rfsgHandle.Abort();

            try
            {
                rfsgHandle.Arb.ClearWaveform(waveform.Name); //Clear existing waveform to avoid erros
            }
            catch (Exception ex)
            {
                if (ex is Ivi.Driver.OutOfRangeException || ex is Ivi.Driver.IviCDriverException)
                {
                }                                                                                     //Intentionally ignore this exception; clearing the waveform failed because it doesn't exist
                else
                {
                    throw;
                }
            }

            rfsgHandle.RF.PowerLevelType = RfsgRFPowerLevelType.PeakPower; // set power level to peak before writing so RFSG doesn't scale waveform
            rfsgHandle.Arb.WriteWaveform(waveform.Name, waveform.Data);

            //Store loaded parameters
            NIRfsgPlayback.StoreWaveformSignalBandwidth(rfsgPtr, waveform.Name, waveform.SignalBandwidth_Hz);
            NIRfsgPlayback.StoreWaveformPapr(rfsgPtr, waveform.Name, waveform.PAPR_dB);
            NIRfsgPlayback.StoreWaveformBurstStartLocations(rfsgPtr, waveform.Name, waveform.BurstStartLocations);
            NIRfsgPlayback.StoreWaveformBurstStopLocations(rfsgPtr, waveform.Name, waveform.BurstStopLocations);
            NIRfsgPlayback.StoreWaveformSampleRate(rfsgPtr, waveform.Name, waveform.SampleRate);
            NIRfsgPlayback.StoreWaveformRuntimeScaling(rfsgPtr, waveform.Name, waveform.RuntimeScaling);
            NIRfsgPlayback.StoreWaveformSize(rfsgPtr, waveform.Name, waveform.Data.SampleCount);

            //Manually configure additional settings
            NIRfsgPlayback.StoreWaveformLOOffsetMode(rfsgPtr, waveform.Name, NIRfsgPlaybackLOOffsetMode.Auto);
            NIRfsgPlayback.StoreWaveformRFBlankingEnabled(rfsgPtr, waveform.Name, false);
        }
Beispiel #10
0
        /// <summary>Configures the generator to generate the waveform continuously with a trigger on the first sample of the waveform. Use <see cref="AbortGeneration(NIRfsg, int)"/>
        /// to abort generation to always ensure complete packet generation.</summary>
        /// <param name="rfsgHandle">The open RFSG session to configure.</param>
        /// <param name="waveform">Specifies the waveform to generate continuously. Call <see cref="DownloadWaveform(NIRfsg, Waveform)"/> prior to calling this function.</param>
        /// <param name="markerEventExportTerminal">(Optional) Specifies the terminal name where the trigger at the start of the waveform should be exported to.
        /// The default value is to generate a trigger on the PXI_Trig0 line.</param>
        /// <returns>The Waveform with the "Script" parameter set to the newly created generation script.</returns>
        public static Waveform ConfigureContinuousGeneration(NIRfsg rfsgHandle, Waveform waveform, string markerEventExportTerminal = "PXI_Trig0")
        {
            //Configure the trigger to be generated on the first sample of each waveform generation,
            //denoted in the script below as "marker0"
            rfsgHandle.DeviceEvents.MarkerEvents[0].ExportedOutputTerminal = RfsgMarkerEventExportedOutputTerminal.FromString(markerEventExportTerminal);

            //A software trigger is configured that is used in the script below to control generation of
            //the script. This ensures that a complete packet is always generated before aborting, and
            //allows all generation functions to share a single abort function.
            rfsgHandle.Triggers.ScriptTriggers[0].ConfigureSoftwareTrigger();

            // Create continuous generation script that is unique to the waveform
            waveform.Script = $@"script REPEAT{waveform.Name}
                                    repeat until ScriptTrigger0
                                        generate {waveform.Name} marker0(0)
                                    end repeat
                                end script";

            // Configure the instrument to generate this waveform
            ApplyWaveformAttributes(rfsgHandle, waveform);

            // Return updated waveform struct to caller
            return(waveform);
        }
Beispiel #11
0
        /// <summary>Creates the generation script used for bursted generation.</summary>
        /// <param name="waveTiming">Specifies the timing parameters used to configure the bursted generation.</param>
        /// <param name="paenConfig">Specifies parameters pertaining to how the DUT is controlled during generation.</param>
        /// <param name="waveform">Specifies the waveform to generate; its burst length will be used in conjuction with the duty cycle to calculate the idle time.</param>
        /// <param name="generationPeriod_s">Returns the total generation period, consisting of the waveform burst length, idle time, and any additional pre/post burst time configured.</param>
        /// <param name="idleTime_s">Returns the computed idle time based upon the requested duty cycle.</param>
        /// <returns>The script to implement the requested bursted generation timing.</returns>
        private static string GenerateBurstedScript(PAENConfiguration paenConfig, WaveformTimingConfiguration waveTiming, Waveform waveform,
                                                    out double generationPeriod_s, out double idleTime_s)
        {
            string scriptName = string.Format("{0}{1}", waveform.Name, waveTiming.DutyCycle_Percent);

            //Calculate various timining information
            double dutyCycle      = waveTiming.DutyCycle_Percent / 100;
            double totalBurstTime = waveTiming.PreBurstTime_s + waveform.BurstLength_s + waveTiming.PostBurstTime_s;

            idleTime_s         = (totalBurstTime / dutyCycle) - totalBurstTime;
            generationPeriod_s = totalBurstTime + idleTime_s;

            //Convert all time based values to sample based values
            long preBurstSamp, postBurstSamp, idleSamp, enableSamples, disableSamples;

            preBurstSamp   = TimeToSamples(waveTiming.PreBurstTime_s, waveform.SampleRate);
            postBurstSamp  = TimeToSamples(waveTiming.PostBurstTime_s, waveform.SampleRate);
            idleSamp       = TimeToSamples(idleTime_s, waveform.SampleRate);
            enableSamples  = TimeToSamples(paenConfig.CommandEnableTime_s, waveform.SampleRate);
            disableSamples = TimeToSamples(paenConfig.CommandDisableTime_s, waveform.SampleRate);

            //RFSG enforces a minimum wait time of 8 samples, so ensure that the minimum pre/post burst time
            // and idle time are at least 8 samples long
            if (preBurstSamp < 8)
            {
                preBurstSamp = 8;
            }
            if (postBurstSamp < 8)
            {
                postBurstSamp = 8;
            }
            if (idleSamp < 8)
            {
                idleSamp = 8;
            }

            //Initialize the script StringBuilder with the first line of the script (name)
            StringBuilder sb = new StringBuilder($"script {scriptName}");

            sb.AppendLine();

            #region Script Building
            //If we have a static PA Enable mode, ensure that we trigger at the beginning of the script prior to looping.
            if (paenConfig.PAEnableMode == PAENMode.Static)
            {
                sb.AppendLine("wait 8 marker1(7)");
            }

            //Configure for endless repeating
            sb.AppendLine("Repeat until scriptTrigger0");

            //Configure the idle time prior to each packet generation
            sb.Append($"wait {idleSamp}");

            //If PAEN Mode is dynamic we need to trigger the PA to enable
            if (paenConfig.PAEnableMode == PAENMode.Dynamic)
            {
                //PA Enable is triggered at or before the last sample of the wait period
                long PAEnableTriggerLoc = idleSamp - enableSamples - 1;
                sb.Append($" marker1({PAEnableTriggerLoc})");
            }

            sb.AppendLine();

            //Configure waiting for the pre-burst time
            sb.AppendLine($"wait {preBurstSamp}");

            //Configure generation of the selected waveform but only for the burst length; send a trigger at the beginning of each burst
            sb.Append($"generate {waveform.Name} subset({waveform.BurstStartLocations[0]},{waveform.BurstStopLocations[0]}) marker0(0)");

            //Check to see if the command time is longer than the post-burst time, which determines when the PA disable command needs sent
            bool LongCommand = waveTiming.PostBurstTime_s <= paenConfig.CommandDisableTime_s;

            if (paenConfig.PAEnableMode == PAENMode.Dynamic && LongCommand)
            {
                //Trigger is placed a number of samples from the end of the burst corresponding with
                //how much longer than the post burst time it is
                long PADisableTriggerLoc = waveform.BurstStopLocations[0] - (disableSamples - postBurstSamp) - 1;
                sb.Append($" marker1({PADisableTriggerLoc})");
            }
            sb.AppendLine();

            //Configure waiting for the post-burst time
            sb.Append($"wait {postBurstSamp}");

            //If the ommand time is shorter than the post-burst time, the disable trigger must be sent
            //during the post-burst time
            if (paenConfig.PAEnableMode == PAENMode.Dynamic && !LongCommand)
            {
                long PADisableTriggerLoc = postBurstSamp - disableSamples - 1;
                sb.Append($" marker1({PADisableTriggerLoc})");
            }
            sb.AppendLine();
            //Close out the script
            sb.AppendLine("end repeat");

            //If we have a static PA Enable mode, ensure that we trigger at the end of the script prior to concluding.
            if (paenConfig.PAEnableMode == PAENMode.Static)
            {
                sb.AppendLine("wait 10 marker1(0)");
            }

            sb.AppendLine("end script");
            #endregion

            return(sb.ToString());
        }
Beispiel #12
0
        /// <summary>Loads a waveform and relevant properties from a TDMS file.</summary>
        /// <param name="filePath">Specifies the absolute path to the .TDMS waveform file on disk.</param>
        /// <param name="waveformName">(Optional) Specifies the name to use to represent the waveform. The file name will be used by default.</param>
        /// <returns>The waveform data and associated properties represented in the Waveform type.</returns>
        public static Waveform LoadWaveformFromTDMS(string filePath, string waveformName = "")
        {
            Waveform waveform = new Waveform();

            if (string.IsNullOrEmpty(waveformName))
            {
                waveformName = Path.GetFileNameWithoutExtension(filePath);
                waveformName = FormatWaveformName(waveformName);
            }

            waveform.Name = waveformName;
            NIRfsgPlayback.ReadWaveformFromFileComplex(filePath, ref waveform.Data);
            NIRfsgPlayback.ReadWaveformFileVersionFromFile(filePath, out string waveformVersion);

            if (waveformVersion == "1.0.0")
            {
                // 1.0.0 waveforms use peak power adjustment = papr + runtime scaling
                // we will scale the waveform and calculate papr and runtime scaling manually
                float peak = ComplexSingle.GetMagnitudes(waveform.Data.GetRawData()).Max();
                waveform.RuntimeScaling = 20.0 * Math.Log10(peak);
                NIRfsgPlayback.ReadPeakPowerAdjustmentFromFile(filePath, 0, out double peakPowerAdjustment);
                waveform.PAPR_dB = peakPowerAdjustment + waveform.RuntimeScaling;

                // scale the waveform to full scale
                WritableBuffer <ComplexSingle> waveformBuffer = waveform.Data.GetWritableBuffer();
                ComplexSingle scale = ComplexSingle.FromPolar(1.0f / peak, 0.0f);
                for (int i = 0; i < waveform.Data.SampleCount; i++)
                {
                    waveformBuffer[i] = waveformBuffer[i] * scale; // multiplication is faster than division
                }
            }
            else
            {
                NIRfsgPlayback.ReadPaprFromFile(filePath, 0, out waveform.PAPR_dB); //Version 2.0.0 and later
                NIRfsgPlayback.ReadRuntimeScalingFromFile(filePath, 0, out waveform.RuntimeScaling);
            }

            NIRfsgPlayback.ReadSampleRateFromFile(filePath, 0, out waveform.SampleRate);
            NIRfsgPlayback.ReadSignalBandwidthFromFile(filePath, 0, out waveform.SignalBandwidth_Hz);
            if (waveform.SignalBandwidth_Hz == 0.0)
            {
                waveform.SignalBandwidth_Hz = 0.8 * waveform.SampleRate;
            }

            NIRfsgPlayback.ReadBurstStartLocationsFromFile(filePath, 0, ref waveform.BurstStartLocations);
            NIRfsgPlayback.ReadBurstStopLocationsFromFile(filePath, 0, ref waveform.BurstStopLocations);
            //Statement reads: if NOT BurstStartLocations > 0 AND expression is not null (? operand)
            //In other words, manually set BurstStartLocations when the length is 0 or less or array is null
            if (!(waveform.BurstStartLocations?.Length > 0))
            {
                waveform.BurstStartLocations = new int[1] {
                    0
                }
            }
            ;                                                    //Set burst start to the first sample(0)
            if (!(waveform.BurstStopLocations?.Length > 0))
            {
                waveform.BurstStopLocations = new int[1] {
                    waveform.Data.SampleCount - 1
                }
            }
            ;                                                                               //Set burst stop to the last sample (number of samples minus one)

            // calculating IdleDurationPresent like this also accounts for tools like wlan sfp that put in burst start and stop locations even if there is no idle time in the waveform
            waveform.IdleDurationPresent = waveform.BurstStopLocations.First() - waveform.BurstStartLocations.First() < waveform.Data.SampleCount - 1;

            waveform.BurstLength_s = CalculateWaveformDuration(waveform.BurstStartLocations, waveform.BurstStopLocations, waveform.SampleRate);

            return(waveform);
        }
        public static void ConfigureBurstedGeneration(NIRfsg rfsgHandle, Waveform waveform, WaveformTimingConfiguration waveTiming,
                                                      PAENConfiguration paenConfig, out double period, out double idleTime)
        {
            IntPtr rfsgPtr = rfsgHandle.GetInstrumentHandle().DangerousGetHandle();

            rfsgHandle.Arb.GenerationMode = RfsgWaveformGenerationMode.Script;

            string scriptName = String.Format("{0}{1}", waveform.WaveformName, waveTiming.DutyCycle_Percent);

            if (waveTiming.DutyCycle_Percent <= 0)
            {
                throw new System.ArgumentOutOfRangeException("DutyCycle_Percent", waveTiming.DutyCycle_Percent, "Duty cycle must be greater than 0 %");
            }

            //Calculate various timining information
            double dutyCycle      = waveTiming.DutyCycle_Percent / 100;
            double totalBurstTime = waveTiming.PreBurstTime_s + waveform.BurstLength_s + waveTiming.PostBurstTime_s;

            idleTime = (totalBurstTime / dutyCycle) - totalBurstTime;
            period   = totalBurstTime + idleTime;

            //Convert all time based values to sample based values
            long preBurstSamp, postBurstSamp, idleSamp, enableSamples, disableSamples;

            preBurstSamp   = TimeToSamples(waveTiming.PreBurstTime_s, waveform.SampleRate);
            postBurstSamp  = TimeToSamples(waveTiming.PostBurstTime_s, waveform.SampleRate);
            idleSamp       = TimeToSamples(idleTime, waveform.SampleRate);
            enableSamples  = TimeToSamples(paenConfig.CommandEnableTime_s, waveform.SampleRate);
            disableSamples = TimeToSamples(paenConfig.CommandDisableTime_s, waveform.SampleRate);

            //RFSG enforces a minimum wait time of 8 samples, so ensure that the minimum pre/post burst time
            // and idle time are at least 8 samples long
            if (preBurstSamp < 8)
            {
                preBurstSamp = 8;
            }
            if (postBurstSamp < 8)
            {
                postBurstSamp = 8;
            }
            if (idleSamp < 8)
            {
                idleSamp = 8;
            }

            //Initialize the script StringBuilder with the first line of the script (name)
            StringBuilder sb = new StringBuilder($"script {scriptName}");

            sb.AppendLine();

            #region Script Building
            //If we have a static PA Enable mode, ensure that we trigger at the beginning of the script prior to looping.
            if (paenConfig.PAEnableMode == PAENMode.Static)
            {
                sb.AppendLine("wait 8 marker1(7)");
            }

            //Configure for endless repeating
            sb.AppendLine("Repeat until scriptTrigger0");

            //Configure the idle time prior to each packet generation
            sb.Append($"wait {idleSamp}");

            //If PAEN Mode is dynamic we need to trigger the PA to enable
            if (paenConfig.PAEnableMode == PAENMode.Dynamic)
            {
                //PA Enable is triggered at or before the last sample of the wait period
                long PAEnableTriggerLoc = idleSamp - enableSamples - 1;
                sb.Append($" marker1({PAEnableTriggerLoc})");
            }

            sb.AppendLine();

            //Configure waiting for the pre-burst time
            sb.AppendLine($"wait {preBurstSamp}");

            //Configure generation of the selected waveform but only for the burst length; send a trigger at the beginning of each burst
            sb.Append($"generate {waveform.WaveformName} subset({waveform.BurstStartLocations[0]},{waveform.BurstStopLocations[0]}) marker0(0)");

            //Check to see if the command time is longer than the post-burst time, which determines when the PA disable command needs sent
            bool LongCommand = waveTiming.PostBurstTime_s <= paenConfig.CommandDisableTime_s;

            if (paenConfig.PAEnableMode == PAENMode.Dynamic && LongCommand)
            {
                //Trigger is placed a number of samples from the end of the burst corresponding with
                //how much longer than the post burst time it is
                long PADisableTriggerLoc = waveform.BurstStopLocations[0] - (disableSamples - postBurstSamp) - 1;
                sb.Append($" marker1({PADisableTriggerLoc})");
            }
            sb.AppendLine();

            //Configure waiting for the post-burst time
            sb.Append($"wait {postBurstSamp}");

            //If the ommand time is shorter than the post-burst time, the disable trigger must be sent
            //during the post-burst time
            if (paenConfig.PAEnableMode == PAENMode.Dynamic && !LongCommand)
            {
                long PADisableTriggerLoc = postBurstSamp - disableSamples - 1;
                sb.Append($" marker1({PADisableTriggerLoc})");
            }
            sb.AppendLine();
            //Close out the script
            sb.AppendLine("end repeat");

            //If we have a static PA Enable mode, ensure that we trigger at the end of the script prior to concluding.
            if (paenConfig.PAEnableMode == PAENMode.Static)
            {
                sb.AppendLine("wait 10 marker1(0)");
            }

            sb.AppendLine("end script");
            #endregion

            //Download the generation script to the generator for later initiation
            NIRfsgPlayback.SetScriptToGenerateSingleRfsg(rfsgPtr, sb.ToString());

            //Configure the triggering for PA enable if selected
            if (paenConfig.PAEnableMode != PAENMode.Disabled)
            {
                rfsgHandle.DeviceEvents.MarkerEvents[1].ExportedOutputTerminal = RfsgMarkerEventExportedOutputTerminal.FromString(
                    paenConfig.PAEnableTriggerExportTerminal);
                rfsgHandle.DeviceEvents.MarkerEvents[1].OutputBehaviour = paenConfig.PAEnableTriggerMode;

                //Configure scriptTrigger0 for software triggering. This way, when it is time to abort we can stop
                //the loop and trigger the appropriate off command if PAEN mode is Static
                rfsgHandle.Triggers.ScriptTriggers[0].ConfigureSoftwareTrigger();
            }

            //Configure the trigger to be generated on the first sample of each waveform generation,
            //denoted in the script below as "marker0"
            rfsgHandle.DeviceEvents.MarkerEvents[0].ExportedOutputTerminal =
                RfsgMarkerEventExportedOutputTerminal.FromString(waveTiming.BurstStartTriggerExport);
        }
        public static Waveform LoadWaveformFromTDMS(ref NIRfsg rfsgHandle, string filePath, string waveformName = "", bool normalizeWaveform = true)
        {
            Waveform waveform = new Waveform
            {
                WaveformData = new ComplexWaveform <ComplexSingle>(0)
            };

            if (string.IsNullOrEmpty(waveformName))
            {
                waveformName = Path.GetFileNameWithoutExtension(filePath);
                Utilities.FormatWaveformName(ref waveformName);
            }
            waveform.WaveformName = waveformName;
            NIRfsgPlayback.ReadWaveformFromFileComplex(filePath, ref waveform.WaveformData);
            NIRfsgPlayback.ReadSignalBandwidthFromFile(filePath, 0, out waveform.SignalBandwidth_Hz);

            NIRfsgPlayback.ReadWaveformFileVersionFromFile(filePath, out string waveformVersion);
            if (waveformVersion == "1.0.0")
            {
                NIRfsgPlayback.ReadPeakPowerAdjustmentFromFile(filePath, 0, out waveform.PAPR_dB);
            }
            else
            {
                NIRfsgPlayback.ReadPaprFromFile(filePath, 0, out waveform.PAPR_dB);  //Version 2.0.0 and later
            }
            NIRfsgPlayback.ReadBurstStartLocationsFromFile(filePath, 0, ref waveform.BurstStartLocations);
            NIRfsgPlayback.ReadBurstStopLocationsFromFile(filePath, 0, ref waveform.BurstStopLocations);

            // If the waveform does not have burst start or stop locations stored, then we will set the burst start to
            // the first sample (0) and the stop to the last sample (number of samples minus one
            if (waveform.BurstStartLocations == null)
            {
                waveform.BurstStartLocations = new int[1] {
                    0
                }
            }
            ;
            //Separate checks because null array throws exception when checking length
            else if (waveform.BurstStopLocations.Length <= 0)
            {
                waveform.BurstStartLocations = new int[1] {
                    0
                }
            }
            ;

            if (waveform.BurstStopLocations == null)
            {
                waveform.BurstStopLocations = new int[1] {
                    waveform.WaveformData.SampleCount - 1
                }
            }
            ;
            //Separate checks because null array throws exception when checking length
            else if (waveform.BurstStopLocations.Length <= 0)
            {
                waveform.BurstStopLocations = new int[1] {
                    waveform.WaveformData.SampleCount - 1
                }
            }
            ;

            waveform.SampleRate    = 1 / waveform.WaveformData.PrecisionTiming.SampleInterval.FractionalSeconds;               //Seconds per sample
            waveform.BurstLength_s = (waveform.BurstStopLocations[0] - waveform.BurstStartLocations[0]) / waveform.SampleRate; //  no. samples / (samples/s) = len_s

            if (normalizeWaveform)
            {
                NormalizeWaveform(ref waveform);
            }

            return(waveform);
        }