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); }
/// <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); }
/// <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); }
/// <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); }
/// <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); }
/// <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()); }
/// <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); }