diff --git a/Examples/LTE_NanosemiDPD_EVM_ACP_UL/App.config b/Examples/LTE_NanosemiDPD_EVM_ACP_UL/App.config new file mode 100644 index 0000000..8e15646 --- /dev/null +++ b/Examples/LTE_NanosemiDPD_EVM_ACP_UL/App.config @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/Examples/LTE_NanosemiDPD_EVM_ACP_UL/LTE_NanosemiDPD_EVM_ACP_UL.cs b/Examples/LTE_NanosemiDPD_EVM_ACP_UL/LTE_NanosemiDPD_EVM_ACP_UL.cs new file mode 100644 index 0000000..dcc0c8e --- /dev/null +++ b/Examples/LTE_NanosemiDPD_EVM_ACP_UL/LTE_NanosemiDPD_EVM_ACP_UL.cs @@ -0,0 +1,198 @@ +using NationalInstruments.ModularInstruments.NIRfsg; +using NationalInstruments.RFmx.InstrMX; +using NationalInstruments.RFmx.LteMX; +using NationalInstruments.RFmx.SpecAnMX; +using System; +using static NationalInstruments.ReferenceDesignLibraries.SA.RFmxLTE; +using static NationalInstruments.ReferenceDesignLibraries.SG; + +namespace NationalInstruments.ReferenceDesignLibraries.Examples +{ + public class LTE_NanosemiDPD_EVM_ACP_UL + { + #region Declaring Parameters + //shared Parameters + public string resourceName; + public double centerFrequency; + public string signalStringSpecan, signalStringLte; + public string resultStringSpecan, resultStringLte; + + //Generator Configuration + public SG.InstrumentConfiguration SgInstrConfig; + + public string filePath; + + //Analyzer Configuration + public SA.RFmxInstr.InstrumentConfiguration saInstrConfig; + public SA.CommonConfiguration saCommonConfig; + public SA.AutoLevelConfiguration saAutolevelConfig; + + public SA.RFmxLTE.StandardConfiguration StandardConfigLte = new SA.RFmxLTE.StandardConfiguration(); + + + //Measurements Configuration + public SA.RFmxLTE.AcpConfiguration AcpConfigLte; + public SA.RFmxLTE.AcpResults AcpResultsLte = new SA.RFmxLTE.AcpResults(); + + public SA.RFmxLTE.ModAccConfiguration ModaccConfigLte; + public SA.RFmxLTE.ModAccResults ModaccResultsLte = new SA.RFmxLTE.ModAccResults(); + + //Methods Configuration + public Methods.NanosemiDPD.NanosemiDPDConfiguration nsDPDConfig; + public Methods.NanosemiDPD.PreDpdCrestFactorReductionConfiguration preDpdCrestFactorReductionConfig; + public bool EnableDpd; + #endregion + + public LTE_NanosemiDPD_EVM_ACP_UL() + { + InitializeParameters(); + } + + public void InitializeParameters() + { + //shared Parameters + centerFrequency = 1.95e9; //Hz + resourceName = "5840"; + filePath = @"C:\Users\Public\Documents\National Instruments\RFIC Test Software\Waveforms\LTE_FDD_UL_1x20MHz_256QAM_OS4.tdms"; + signalStringSpecan = "specanSig0"; + signalStringLte = "lteSig0"; + resultStringSpecan = "specanResult0"; + resultStringLte = "lteResult0"; + + //Generator Configuiration + SgInstrConfig = SG.InstrumentConfiguration.GetDefault(); + SgInstrConfig.CarrierFrequency_Hz = centerFrequency; + SgInstrConfig.DutAverageInputPower_dBm = -10.0; + SgInstrConfig.ExternalAttenuation_dB = 0; + + //Analyzer Configuration + saInstrConfig = SA.RFmxInstr.InstrumentConfiguration.GetDefault(); + saCommonConfig = SA.CommonConfiguration.GetDefault(); + saCommonConfig.ExternalAttenuation_dB = 0; + saCommonConfig.CenterFrequency_Hz = centerFrequency; + saCommonConfig.ReferenceLevel_dBm = 0; + + saAutolevelConfig = SA.AutoLevelConfiguration.GetDefault(); + saAutolevelConfig.Enabled = true; + + + StandardConfigLte = SA.RFmxLTE.StandardConfiguration.GetDefault(); + StandardConfigLte.ComponentCarrierConfigurations[0].Bandwidth_Hz = 20.0e6; + StandardConfigLte.ComponentCarrierConfigurations[0].PuschModulationType = RFmxLteMXPuschModulationType.ModulationType256Qam; + + AcpConfigLte = SA.RFmxLTE.AcpConfiguration.GetDefault(); + + ModaccConfigLte = SA.RFmxLTE.ModAccConfiguration.GetDefault(); + + //Methods Configuration + nsDPDConfig = Methods.NanosemiDPD.NanosemiDPDConfiguration.GetDefault(); + nsDPDConfig.DpdConfiguration = NanoSemiLinearizer.Interop.nstdpd.DpdGetDefaultConfig(); + nsDPDConfig.NumberOfIterations = 3; + nsDPDConfig.DpdConfiguration.lvl = NanoSemiLinearizer.Interop.nstdpd.DpdLevel.NST_DPD_LEVEL0; + nsDPDConfig.DpdConfiguration.rho = 0.1f; + nsDPDConfig.DpdConfiguration.training_samples = 25000; + EnableDpd = true; + + } + + public void Run() + { + #region Create Sessions + NIRfsg nIRfsg = new NIRfsg(resourceName, false, false); + RFmxInstrMX instr = new RFmxInstrMX(resourceName, ""); + RFmxSpecAnMX specAn = instr.GetSpecAnSignalConfiguration(signalStringSpecan); + RFmxLteMX lte = instr.GetLteSignalConfiguration(signalStringLte); + #endregion + + #region Configure Generation + ConfigureInstrument(nIRfsg, SgInstrConfig); + Waveform waveform = LoadWaveformFromTDMS(filePath); + + DownloadWaveform(nIRfsg, waveform); + ConfigureContinuousGeneration(nIRfsg, waveform); + nIRfsg.Initiate(); + #endregion + + #region Configure Analyzer + saAutolevelConfig.MeasurementInterval_s = waveform.BurstLength_s; + SA.RFmxInstr.ConfigureInstrument(instr, saInstrConfig); + + SA.RFmxSpecAn.ConfigureCommon(specAn, saCommonConfig); + + + SA.RFmxLTE.ConfigureCommon(lte, saCommonConfig); + SA.RFmxLTE.ConfigureStandard(lte, StandardConfigLte); + #endregion + + #region Configure and Measure DPD + if (EnableDpd) + { + Methods.NanosemiDPD.ConfigureNanosemi(specAn, nsDPDConfig, waveform, ""); + Console.WriteLine("\n--------------- Measurement Results with DPD --------------\n"); + Methods.NanosemiDPD.PerformNanosemiDPD(specAn, nIRfsg, waveform, nsDPDConfig, ""); + } + else + { + Console.WriteLine("\n------------- Measurement Results without DPD -------------\n"); + } + #endregion + + #region Measure + + ConfigureAcp(lte, AcpConfigLte); + RFmxLteMXMeasurementTypes[] lteMeasurements = new RFmxLteMXMeasurementTypes[1] { RFmxLteMXMeasurementTypes.Acp }; + SA.RFmxLTE.SelectAndInitiateMeasurements(lte, lteMeasurements, saAutolevelConfig, false, "", resultStringLte); + AcpResultsLte = FetchAcp(lte, RFmxLteMX.BuildResultString(resultStringLte)); + PrintACPResults(); + + ConfigureModAcc(lte, ModaccConfigLte); + lteMeasurements[0] = RFmxLteMXMeasurementTypes.ModAcc; + SA.RFmxLTE.SelectAndInitiateMeasurements(lte, lteMeasurements, saAutolevelConfig, false, "", resultStringLte); + ModaccResultsLte = FetchModAcc(lte, RFmxLteMX.BuildResultString(resultStringLte)); + PrintModAccResults(); + #endregion + + specAn.Dispose(); + specAn = null; + lte.Dispose(); + instr.Close(); + + AbortGeneration(nIRfsg); + CloseInstrument(nIRfsg); + } + + #region Utilities + private void PrintACPResults() + { + Console.WriteLine("\n----------------------- ACP Results -----------------------\n"); + Console.WriteLine("Carrier Absolute Power (dBm): {0:0.00}\n", AcpResultsLte.ComponentCarrierResults[0].AbsolutePower_dBm); + Console.WriteLine("\n-----------Offset Channel Measurements----------- \n"); + + for (int i = 0; i < AcpResultsLte.OffsetResults.Length; i++) + { + Console.WriteLine("Offset {0}", i); + Console.WriteLine("Lower Absolute Power (dBm) : {0:0.000}", AcpResultsLte.OffsetResults[i].LowerAbsolutePower_dBm); + Console.WriteLine("Upper Absolute Power (dBm) : {0:0.000}", AcpResultsLte.OffsetResults[i].UpperAbsolutePower_dBm); + Console.WriteLine("Lower Relative Power (dB) : {0:0.000}", AcpResultsLte.OffsetResults[i].LowerRelativePower_dB); + Console.WriteLine("Upper Relative Power (dB) : {0:0.000}", AcpResultsLte.OffsetResults[i].UpperRelativePower_dB); + Console.WriteLine("Offset Frequency (Hz) : {0:0.000}", AcpResultsLte.OffsetResults[i].Frequency_Hz); + Console.WriteLine("Offset Integration Bandwidth (Hz) : {0:0.000}", AcpResultsLte.OffsetResults[i].IntegrationBandwidth_Hz); + Console.WriteLine("-------------------------------------------------\n"); + } + } + private void PrintModAccResults() + { + for (int i = 0; i < ModaccResultsLte.ComponentCarrierResults.Length; i++) + { + Console.WriteLine("\n----------------------- EVM Results CC {0} -----------------------\n", i); + Console.WriteLine("Composite RMS EVM Mean (% or dB) : {0:0.000}", ModaccResultsLte.ComponentCarrierResults[i].MeanRmsCompositeEvm); + Console.WriteLine("Composite Peak EVM Maximum (% or dB) : {0:0.000}", ModaccResultsLte.ComponentCarrierResults[i].MaxPeakCompositeEvm); + Console.WriteLine("Composite Peak EVM Slot Index : {0}", ModaccResultsLte.ComponentCarrierResults[i].PeakCompositeEvmSlotIndex); + Console.WriteLine("Composite Peak EVM Symbol Index : {0}", ModaccResultsLte.ComponentCarrierResults[i].PeakCompositeEvmSymbolIndex); + Console.WriteLine("Composite Peak EVM Subcarrier Index : {0}", ModaccResultsLte.ComponentCarrierResults[i].PeakCompositeEvmSubcarrierIndex); + Console.WriteLine("Component Carrier Frequency Error Mean (Hz) : {0:0.000}", ModaccResultsLte.ComponentCarrierResults[i].MeanFrequencyError_Hz); + } + } + #endregion + } +} diff --git a/Examples/LTE_NanosemiDPD_EVM_ACP_UL/LTE_NanosemiDPD_EVM_ACP_UL.csproj b/Examples/LTE_NanosemiDPD_EVM_ACP_UL/LTE_NanosemiDPD_EVM_ACP_UL.csproj new file mode 100644 index 0000000..01b9d01 --- /dev/null +++ b/Examples/LTE_NanosemiDPD_EVM_ACP_UL/LTE_NanosemiDPD_EVM_ACP_UL.csproj @@ -0,0 +1,87 @@ + + + + + Debug + AnyCPU + {C95B388B-A9DD-4019-B4A1-64BC0188083D} + Exe + LTE_NanosemiDPD_EVM_ACP_UL + LTE_NanosemiDPD_EVM_ACP_UL + v4.5 + 512 + true + + + x64 + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + + + AnyCPU + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + + + true + + + + + + + + + + + + + + + + + + + + + + + + + + + {8e7d5092-b685-4ad3-9da3-7c53e56f0f32} + Common + + + {7fdb7cdb-4281-4b0d-a407-a6d4c63fcd36} + NanosemiDPD + + + {ab49d5a9-9585-445a-ae8a-e4dd2b8956aa} + RFmxInstr + + + {1eb6f1ae-2eea-4dc8-8cdb-09b5fadf2236} + RFmxLTE + + + {3d1bc0f6-273c-4a44-82f6-3bb933862ffb} + RFmxSpecAn + + + {89abcac5-cedb-4535-bd58-e205a97834d9} + SG + + + + \ No newline at end of file diff --git a/Examples/LTE_NanosemiDPD_EVM_ACP_UL/LTE_NanosemiDPD_EVM_ACP_UL.sln b/Examples/LTE_NanosemiDPD_EVM_ACP_UL/LTE_NanosemiDPD_EVM_ACP_UL.sln new file mode 100644 index 0000000..31d8efb --- /dev/null +++ b/Examples/LTE_NanosemiDPD_EVM_ACP_UL/LTE_NanosemiDPD_EVM_ACP_UL.sln @@ -0,0 +1,101 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 16 +VisualStudioVersion = 16.0.29324.140 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "LTE_NanosemiDPD_EVM_ACP_UL", "LTE_NanosemiDPD_EVM_ACP_UL.csproj", "{C95B388B-A9DD-4019-B4A1-64BC0188083D}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Dependencies", "Dependencies", "{09F0830E-B2C7-45C5-BDDF-2E355662B9EA}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "RFmxInstr", "..\..\Source\SA\RFmx Instr\RFmxInstr.csproj", "{AB49D5A9-9585-445A-AE8A-E4DD2B8956AA}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "RFmxLTE", "..\..\Source\SA\RFmx LTE\RFmxLTE.csproj", "{1EB6F1AE-2EEA-4DC8-8CDB-09B5FADF2236}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "RFmxSpecAn", "..\..\Source\SA\RFmx SpecAn\RFmxSpecAn.csproj", "{3D1BC0F6-273C-4A44-82F6-3BB933862FFB}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "NanosemiDPD", "..\..\Source\Methods\NanosemiDPD\NanosemiDPD.csproj", "{7FDB7CDB-4281-4B0D-A407-A6D4C63FCD36}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SG", "..\..\Source\SG\SG.csproj", "{89ABCAC5-CEDB-4535-BD58-E205A97834D9}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Common", "..\..\Source\Common\Common.csproj", "{8E7D5092-B685-4AD3-9DA3-7C53E56F0F32}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Debug|x64 = Debug|x64 + Release|Any CPU = Release|Any CPU + Release|x64 = Release|x64 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {C95B388B-A9DD-4019-B4A1-64BC0188083D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {C95B388B-A9DD-4019-B4A1-64BC0188083D}.Debug|Any CPU.Build.0 = Debug|Any CPU + {C95B388B-A9DD-4019-B4A1-64BC0188083D}.Debug|x64.ActiveCfg = Debug|Any CPU + {C95B388B-A9DD-4019-B4A1-64BC0188083D}.Debug|x64.Build.0 = Debug|Any CPU + {C95B388B-A9DD-4019-B4A1-64BC0188083D}.Release|Any CPU.ActiveCfg = Release|Any CPU + {C95B388B-A9DD-4019-B4A1-64BC0188083D}.Release|Any CPU.Build.0 = Release|Any CPU + {C95B388B-A9DD-4019-B4A1-64BC0188083D}.Release|x64.ActiveCfg = Release|Any CPU + {C95B388B-A9DD-4019-B4A1-64BC0188083D}.Release|x64.Build.0 = Release|Any CPU + {AB49D5A9-9585-445A-AE8A-E4DD2B8956AA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {AB49D5A9-9585-445A-AE8A-E4DD2B8956AA}.Debug|Any CPU.Build.0 = Debug|Any CPU + {AB49D5A9-9585-445A-AE8A-E4DD2B8956AA}.Debug|x64.ActiveCfg = Debug|Any CPU + {AB49D5A9-9585-445A-AE8A-E4DD2B8956AA}.Debug|x64.Build.0 = Debug|Any CPU + {AB49D5A9-9585-445A-AE8A-E4DD2B8956AA}.Release|Any CPU.ActiveCfg = Release|Any CPU + {AB49D5A9-9585-445A-AE8A-E4DD2B8956AA}.Release|Any CPU.Build.0 = Release|Any CPU + {AB49D5A9-9585-445A-AE8A-E4DD2B8956AA}.Release|x64.ActiveCfg = Release|Any CPU + {AB49D5A9-9585-445A-AE8A-E4DD2B8956AA}.Release|x64.Build.0 = Release|Any CPU + {1EB6F1AE-2EEA-4DC8-8CDB-09B5FADF2236}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {1EB6F1AE-2EEA-4DC8-8CDB-09B5FADF2236}.Debug|Any CPU.Build.0 = Debug|Any CPU + {1EB6F1AE-2EEA-4DC8-8CDB-09B5FADF2236}.Debug|x64.ActiveCfg = Debug|Any CPU + {1EB6F1AE-2EEA-4DC8-8CDB-09B5FADF2236}.Debug|x64.Build.0 = Debug|Any CPU + {1EB6F1AE-2EEA-4DC8-8CDB-09B5FADF2236}.Release|Any CPU.ActiveCfg = Release|Any CPU + {1EB6F1AE-2EEA-4DC8-8CDB-09B5FADF2236}.Release|Any CPU.Build.0 = Release|Any CPU + {1EB6F1AE-2EEA-4DC8-8CDB-09B5FADF2236}.Release|x64.ActiveCfg = Release|Any CPU + {1EB6F1AE-2EEA-4DC8-8CDB-09B5FADF2236}.Release|x64.Build.0 = Release|Any CPU + {3D1BC0F6-273C-4A44-82F6-3BB933862FFB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {3D1BC0F6-273C-4A44-82F6-3BB933862FFB}.Debug|Any CPU.Build.0 = Debug|Any CPU + {3D1BC0F6-273C-4A44-82F6-3BB933862FFB}.Debug|x64.ActiveCfg = Debug|Any CPU + {3D1BC0F6-273C-4A44-82F6-3BB933862FFB}.Debug|x64.Build.0 = Debug|Any CPU + {3D1BC0F6-273C-4A44-82F6-3BB933862FFB}.Release|Any CPU.ActiveCfg = Release|Any CPU + {3D1BC0F6-273C-4A44-82F6-3BB933862FFB}.Release|Any CPU.Build.0 = Release|Any CPU + {3D1BC0F6-273C-4A44-82F6-3BB933862FFB}.Release|x64.ActiveCfg = Release|Any CPU + {3D1BC0F6-273C-4A44-82F6-3BB933862FFB}.Release|x64.Build.0 = Release|Any CPU + {7FDB7CDB-4281-4B0D-A407-A6D4C63FCD36}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {7FDB7CDB-4281-4B0D-A407-A6D4C63FCD36}.Debug|Any CPU.Build.0 = Debug|Any CPU + {7FDB7CDB-4281-4B0D-A407-A6D4C63FCD36}.Debug|x64.ActiveCfg = Debug|x64 + {7FDB7CDB-4281-4B0D-A407-A6D4C63FCD36}.Debug|x64.Build.0 = Debug|x64 + {7FDB7CDB-4281-4B0D-A407-A6D4C63FCD36}.Release|Any CPU.ActiveCfg = Release|Any CPU + {7FDB7CDB-4281-4B0D-A407-A6D4C63FCD36}.Release|Any CPU.Build.0 = Release|Any CPU + {7FDB7CDB-4281-4B0D-A407-A6D4C63FCD36}.Release|x64.ActiveCfg = Release|x64 + {7FDB7CDB-4281-4B0D-A407-A6D4C63FCD36}.Release|x64.Build.0 = Release|x64 + {89ABCAC5-CEDB-4535-BD58-E205A97834D9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {89ABCAC5-CEDB-4535-BD58-E205A97834D9}.Debug|Any CPU.Build.0 = Debug|Any CPU + {89ABCAC5-CEDB-4535-BD58-E205A97834D9}.Debug|x64.ActiveCfg = Debug|Any CPU + {89ABCAC5-CEDB-4535-BD58-E205A97834D9}.Debug|x64.Build.0 = Debug|Any CPU + {89ABCAC5-CEDB-4535-BD58-E205A97834D9}.Release|Any CPU.ActiveCfg = Release|Any CPU + {89ABCAC5-CEDB-4535-BD58-E205A97834D9}.Release|Any CPU.Build.0 = Release|Any CPU + {89ABCAC5-CEDB-4535-BD58-E205A97834D9}.Release|x64.ActiveCfg = Release|Any CPU + {89ABCAC5-CEDB-4535-BD58-E205A97834D9}.Release|x64.Build.0 = Release|Any CPU + {8E7D5092-B685-4AD3-9DA3-7C53E56F0F32}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {8E7D5092-B685-4AD3-9DA3-7C53E56F0F32}.Debug|Any CPU.Build.0 = Debug|Any CPU + {8E7D5092-B685-4AD3-9DA3-7C53E56F0F32}.Debug|x64.ActiveCfg = Debug|Any CPU + {8E7D5092-B685-4AD3-9DA3-7C53E56F0F32}.Debug|x64.Build.0 = Debug|Any CPU + {8E7D5092-B685-4AD3-9DA3-7C53E56F0F32}.Release|Any CPU.ActiveCfg = Release|Any CPU + {8E7D5092-B685-4AD3-9DA3-7C53E56F0F32}.Release|Any CPU.Build.0 = Release|Any CPU + {8E7D5092-B685-4AD3-9DA3-7C53E56F0F32}.Release|x64.ActiveCfg = Release|Any CPU + {8E7D5092-B685-4AD3-9DA3-7C53E56F0F32}.Release|x64.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(NestedProjects) = preSolution + {AB49D5A9-9585-445A-AE8A-E4DD2B8956AA} = {09F0830E-B2C7-45C5-BDDF-2E355662B9EA} + {1EB6F1AE-2EEA-4DC8-8CDB-09B5FADF2236} = {09F0830E-B2C7-45C5-BDDF-2E355662B9EA} + {3D1BC0F6-273C-4A44-82F6-3BB933862FFB} = {09F0830E-B2C7-45C5-BDDF-2E355662B9EA} + {7FDB7CDB-4281-4B0D-A407-A6D4C63FCD36} = {09F0830E-B2C7-45C5-BDDF-2E355662B9EA} + {89ABCAC5-CEDB-4535-BD58-E205A97834D9} = {09F0830E-B2C7-45C5-BDDF-2E355662B9EA} + {8E7D5092-B685-4AD3-9DA3-7C53E56F0F32} = {09F0830E-B2C7-45C5-BDDF-2E355662B9EA} + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {2A8146D3-319B-49A6-AA8C-8D0A662DF6F3} + EndGlobalSection +EndGlobal diff --git a/Examples/LTE_NanosemiDPD_EVM_ACP_UL/Program.cs b/Examples/LTE_NanosemiDPD_EVM_ACP_UL/Program.cs new file mode 100644 index 0000000..a0d79cc --- /dev/null +++ b/Examples/LTE_NanosemiDPD_EVM_ACP_UL/Program.cs @@ -0,0 +1,29 @@ +using System; + +namespace NationalInstruments.ReferenceDesignLibraries.Examples +{ + class Program + { + static void Main(string[] args) + { + Console.WriteLine("Example Application LTE_NanosemiDPD_EVM_ACP_UL\n"); + LTE_NanosemiDPD_EVM_ACP_UL lteNanosemiDpdAmPmEvmAcpUl = new LTE_NanosemiDPD_EVM_ACP_UL(); + try + { + lteNanosemiDpdAmPmEvmAcpUl.Run(); + + } + catch (Exception e) + { + + DisplayError(e); + } + Console.WriteLine("Press any key to finish."); + Console.ReadKey(); + } + static void DisplayError(Exception e) + { + Console.WriteLine("ERROR:\n" + e.GetType() + ": " + e.Message); + } + } +} \ No newline at end of file diff --git a/Examples/LTE_NanosemiDPD_EVM_ACP_UL/Properties/AssemblyInfo.cs b/Examples/LTE_NanosemiDPD_EVM_ACP_UL/Properties/AssemblyInfo.cs new file mode 100644 index 0000000..b37b7d8 --- /dev/null +++ b/Examples/LTE_NanosemiDPD_EVM_ACP_UL/Properties/AssemblyInfo.cs @@ -0,0 +1,36 @@ +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("LTE_NanosemiDPD_EVM_ACP_UL")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("National Instruments")] +[assembly: AssemblyProduct("LTE_NanosemiDPD_EVM_ACP_UL")] +[assembly: AssemblyCopyright("Copyright © National Instruments 2020")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible(false)] + +// The following GUID is for the ID of the typelib if this project is exposed to COM +[assembly: Guid("c95b388b-a9dd-4019-b4a1-64bc0188083d")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Build and Revision Numbers +// by using the '*' as shown below: +// [assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/Examples/LTE_NanosemiDPD_EVM_ACP_UL/RFmxInstr.cs b/Examples/LTE_NanosemiDPD_EVM_ACP_UL/RFmxInstr.cs new file mode 100644 index 0000000..03b3850 --- /dev/null +++ b/Examples/LTE_NanosemiDPD_EVM_ACP_UL/RFmxInstr.cs @@ -0,0 +1,55 @@ +using NationalInstruments.RFmx.InstrMX; +using System.Text.RegularExpressions; + +namespace NationalInstruments.ReferenceDesignLibraries.SA +{ + /// Defines common types and methods for configuring instruments with NI-RFmx. + public static class RFmxInstr + { + #region Type Definitions + /// Defines common instrument configurations to apply to the analyzer. + public struct InstrumentConfiguration + { + /// Defines the local oscillator sharing behavior for VST devices. + public LocalOscillatorSharingMode LOSharingMode; + /// Specifies the frequency reference source. + public string FrequencyReferenceSource; + + /// Returns the struct with default values set. + /// The struct with default values set. + public static InstrumentConfiguration GetDefault() + { + return new InstrumentConfiguration + { + LOSharingMode = LocalOscillatorSharingMode.Automatic, + FrequencyReferenceSource = RFmxInstrMXConstants.PxiClock + }; + } + } + #endregion + + #region Instrument Configurations + /// Applies common instrument settings to the analyzer. + /// The open RFmx Instr session to configure. + /// The instrument configuration properties to apply. + public static void ConfigureInstrument(RFmxInstrMX instrHandle, InstrumentConfiguration instrConfig) + { + instrHandle.SetFrequencyReferenceSource("", instrConfig.FrequencyReferenceSource); + instrHandle.GetInstrumentModel("", out string model); + // Only configure LO settings on supported VSTs + if (Regex.IsMatch(model, "NI PXIe-58[34].")) // Matches 583x and 584x VST families + { + if (instrConfig.LOSharingMode == LocalOscillatorSharingMode.None) + instrHandle.ConfigureAutomaticSGSASharedLO("", RFmxInstrMXAutomaticSGSASharedLO.Disabled); + else + { + instrHandle.ConfigureAutomaticSGSASharedLO("", RFmxInstrMXAutomaticSGSASharedLO.Enabled); + // Configure automatic LO offsetting + instrHandle.SetLOLeakageAvoidanceEnabled("", RFmxInstrMXLOLeakageAvoidanceEnabled.True); + instrHandle.ResetAttribute("", RFmxInstrMXPropertyId.DownconverterFrequencyOffset); + } + } + } + #endregion + } +} diff --git a/Source/Methods/NanosemiDPD/AssemblyInfo.cs b/Source/Methods/NanosemiDPD/AssemblyInfo.cs new file mode 100644 index 0000000..002de7a --- /dev/null +++ b/Source/Methods/NanosemiDPD/AssemblyInfo.cs @@ -0,0 +1,36 @@ +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("NanosemiDPD")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("National Instruments")] +[assembly: AssemblyProduct("NanosemiDPD")] +[assembly: AssemblyCopyright("Copyright © National Instruments 2020")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible(false)] + +// The following GUID is for the ID of the typelib if this project is exposed to COM +[assembly: Guid("7fdb7cdb-4281-4b0d-a407-a6d4c63fcd36")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Build and Revision Numbers +// by using the '*' as shown below: +// [assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/Source/Methods/NanosemiDPD/NanosemiDPD.cs b/Source/Methods/NanosemiDPD/NanosemiDPD.cs new file mode 100644 index 0000000..ccf0cb7 --- /dev/null +++ b/Source/Methods/NanosemiDPD/NanosemiDPD.cs @@ -0,0 +1,127 @@ +using System; +using NationalInstruments.RFmx.SpecAnMX; +using NationalInstruments.ModularInstruments.NIRfsg; +using NationalInstruments.ModularInstruments.NIRfsgPlayback; +using static NationalInstruments.ReferenceDesignLibraries.SG; +using NanoSemiLinearizer.Interop; + +namespace NationalInstruments.ReferenceDesignLibraries.Methods +{ + public static class NanosemiDPD + { + #region Type Definitions + + /// Defines commmon settings for the application of crest factor reduction on the reference waveform prior to applying DPD for a single carrier channel. + public struct PreDpdCrestFactorReductionConfiguration + { + + public nstdpd.CfrConfig CfrConfiguration; + + /// Returns the struct with default values set. + /// The struct with default values set. + public static PreDpdCrestFactorReductionConfiguration GetDefault() + { + return new PreDpdCrestFactorReductionConfiguration + { + CfrConfiguration = nstdpd.CfrGetDefaultConfig() + }; + + } + } + + public struct NanosemiDPDConfiguration + { + public nstdpd.DpdConfig DpdConfiguration; + /// Specifies the number of iterations over which Nanosemi DPD is computed using indirect-learning architecture. + public int NumberOfIterations; + /// Specifies the duration of the reference waveform considered for the DPD measurement. + public double IQAcquisitionLength_s; + + public static NanosemiDPDConfiguration GetDefault() + { + return new NanosemiDPDConfiguration + { + + DpdConfiguration = nstdpd.DpdGetDefaultConfig(), + NumberOfIterations = 3, + IQAcquisitionLength_s = 1e-3 + + }; + } + } + + + public struct NanosemiDPDResults + { + /// Returns the whose data represents the complex baseband equivalent of the RF signal + /// after applying digital pre-distortion. + public Waveform PredistortedWaveform; + } + #endregion + + #region ConfigureNanosemiDPD + + /// Specifies the SpecAn signal to configure. + /// Specifies the whose data defines the complex baseband equivalent of the RF signal on which the + /// pre-DPD signal conditioning is applied. + /// Pass an empty string. The signal name that is passed when creating the signal configuration is used. + public static void ConfigureNanosemi(RFmxSpecAnMX specAn, NanosemiDPDConfiguration nsDPDConfig, Waveform referenceWaveform, string selectorString = "") + { + specAn.SelectMeasurements("", RFmxSpecAnMXMeasurementTypes.IQ, true); + specAn.IQ.Configuration.ConfigureAcquisition(selectorString, referenceWaveform.SampleRate, 1, nsDPDConfig.IQAcquisitionLength_s, 0); + nsDPDConfig.DpdConfiguration.f_sample = (float)(referenceWaveform.SampleRate * 1e-6); + nstdpd.DpdResetTraining(nsDPDConfig.DpdConfiguration); + + } + #endregion + + #region PerformDPD + + /// Acquires the incoming signal, trains the DPD model, applies the DPD model 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. + /// Specifies the SpecAn signal to configure. + /// Specifies the open RFSG session to configure. + /// Specifies the 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. + /// Pass an empty string. The signal name that is passed when creating the signal configuration is used. + /// Common results after the application of lookup table based DPD. + public static NanosemiDPDResults PerformNanosemiDPD(RFmxSpecAnMX specAn, NIRfsg rfsgSession, Waveform referenceWaveform, NanosemiDPDConfiguration nsDPDConfig, string selectorString = "") + { + IntPtr rfGeneratorHandle; + rfGeneratorHandle = rfsgSession.GetInstrumentHandle().DangerousGetHandle(); + NanosemiDPDResults nanosemiDPDResults = new NanosemiDPDResults() + { + PredistortedWaveform = referenceWaveform + }; + nanosemiDPDResults.PredistortedWaveform.Data = referenceWaveform.Data.Clone(); // clone waveform so RFmx can't act on reference waveform + nanosemiDPDResults.PredistortedWaveform.UpdateNameAndScript(referenceWaveform.Name + "postNsDpd"); + ComplexWaveform acquiredIQData = new ComplexWaveform(0); + + + for (int dpdIter = 0; dpdIter < nsDPDConfig.NumberOfIterations; dpdIter++) + { + specAn.Initiate(selectorString, ""); + specAn.IQ.Results.FetchData(selectorString, 3, 0, -1, ref acquiredIQData); + + nstdpd.DpdTrain(referenceWaveform.Data, acquiredIQData, nsDPDConfig.DpdConfiguration); + nanosemiDPDResults.PredistortedWaveform.Data = nstdpd.DpdApply(referenceWaveform.Data, nsDPDConfig.DpdConfiguration); + + //Modify RFSG waveform to DPD waveform and Initiate generation again. + RfsgGenerationStatus preDpdGenerationStatus = rfsgSession.CheckGenerationStatus(); + rfsgSession.Abort(); + NIRfsgPlayback.ClearAllWaveforms(rfGeneratorHandle); + + //Download DPD waveform to the RF Generator and update scripts + NIRfsgPlayback.DownloadUserWaveform(rfGeneratorHandle, nanosemiDPDResults.PredistortedWaveform.Name, nanosemiDPDResults.PredistortedWaveform.Data, true); + NIRfsgPlayback.RetrieveWaveformPapr(rfGeneratorHandle, nanosemiDPDResults.PredistortedWaveform.Name, out nanosemiDPDResults.PredistortedWaveform.PAPR_dB); + NIRfsgPlayback.RetrieveWaveformSampleRate(rfGeneratorHandle, nanosemiDPDResults.PredistortedWaveform.Name, out nanosemiDPDResults.PredistortedWaveform.SampleRate); + ApplyWaveformAttributes(rfsgSession, nanosemiDPDResults.PredistortedWaveform); + + //Initiate generation of post DPD waveform + rfsgSession.Initiate(); + } + return nanosemiDPDResults; + } + #endregion + } +} diff --git a/Source/Methods/NanosemiDPD/NanosemiDPD.csproj b/Source/Methods/NanosemiDPD/NanosemiDPD.csproj new file mode 100644 index 0000000..d2d7beb --- /dev/null +++ b/Source/Methods/NanosemiDPD/NanosemiDPD.csproj @@ -0,0 +1,89 @@ + + + + + Debug + AnyCPU + {7FDB7CDB-4281-4B0D-A407-A6D4C63FCD36} + Library + Properties + NanosemiDPD + NanosemiDPD + v4.5 + 512 + true + + + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + + + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + + + true + bin\x64\Debug\ + DEBUG;TRACE + full + x64 + 7.3 + prompt + MinimumRecommendedRules.ruleset + + + bin\x64\Release\ + TRACE + true + pdbonly + x64 + 7.3 + prompt + MinimumRecommendedRules.ruleset + + + + False + + + False + + + False + + + False + + + + + + Properties\SolutionInfo.cs + + + + + + + + {8e7d5092-b685-4ad3-9da3-7c53e56f0f32} + Common + False + + + {89abcac5-cedb-4535-bd58-e205a97834d9} + SG + False + + + + \ No newline at end of file diff --git a/Source/Methods/NanosemiDPD/Properties/AssemblyInfo.cs b/Source/Methods/NanosemiDPD/Properties/AssemblyInfo.cs new file mode 100644 index 0000000..8c1cc96 --- /dev/null +++ b/Source/Methods/NanosemiDPD/Properties/AssemblyInfo.cs @@ -0,0 +1,32 @@ +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("NanosemiDPD")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyProduct("NanosemiDPD")] +[assembly: AssemblyCulture("")] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible(false)] + +// The following GUID is for the ID of the typelib if this project is exposed to COM +[assembly: Guid("7fdb7cdb-4281-4b0d-a407-a6d4c63fcd36")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Build and Revision Numbers +// by using the '*' as shown below: +// [assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/Source/Methods/NanosemiDPD/nstdpd.cs b/Source/Methods/NanosemiDPD/nstdpd.cs new file mode 100644 index 0000000..fc347e7 --- /dev/null +++ b/Source/Methods/NanosemiDPD/nstdpd.cs @@ -0,0 +1,398 @@ +using System; +using System.Runtime.InteropServices; +using NationalInstruments; + +namespace NanoSemiLinearizer.Interop +{ + public static class nstdpd + { + #region Public Structs and Enums + /// + ///CFR configuration structure. + ///Dictates the behavior the NSTDPD CFR algorithm. + ///All fields must have non-Infinity, non-NaN values. + /// + ///The num_bands, fc_list, and bw_list fields describe the spectral mask of the input waveform as a list of carrier bands with a certain bandwidths and center frequencies. + ///If the offset field is nonzero, the mask described by num_bands/fc_list/bw_list will be frequency-shifted by that value. + ///The overal spectrum mask described by the first four fields of this struct must be fully contained within the range[-f_sample / 2, f_sample / 2]. + /// + ///struct NST_CFR_CONFIG_STRUCT + /// + [StructLayout(LayoutKind.Sequential)] + public struct CfrConfig + { + /// + /// The number of frequency bands to apply CFR to. Must be a non-negative value. + /// + public int num_bands; + /// + /// The center frequency (MHz) of each band. + /// + public float[] fc_list; + /// + /// The width of each frequency band (MHz). + /// + public float[] bw_list; + /// + /// The frequency-domain baseband offset of the input waveform (MHz). + /// + public float offset; + /// + /// The sampling rate (MHz) of the input waveform. Must be a positive value. + /// + public float f_sample; + /// + /// The desired PAPR (dB) of the CFR output. Must be a non-negative value. + /// + public float targetPAPR; + /// + /// For FDD waveforms set this to 1.0f. For a TDD DL waveform, set this to (number of downlink subframes/total subframes). For a TDD UL waveform, set this to (number of uplink subframes/total subframes). Valid values are [1,0). + /// + public float TDD_duty_cycle; + } + + /// + /// DPD Performance Levels. + /// Adaptive DPD offers four tiers of DPD performance, which are enumerated here. + /// Linearizer performance & execution time increase for higher performance levels. + /// + public enum DpdLevel + { + /// NST_DPD_LEVEL3 -> 3 + NST_DPD_LEVEL3 = 3, + /// NST_DPD_LEVEL2 -> 2 + NST_DPD_LEVEL2 = 2, + /// NST_DPD_LEVEL1 -> 1 + NST_DPD_LEVEL1 = 1, + /// NST_DPD_LEVEL0 -> 0 + NST_DPD_LEVEL0 = 0, + } + + /// + /// DPD configuration Structure. + /// Dictates the behavior of the DPD Training algorithms. + /// struct NST_DPD_CONFIG_STRUCT + /// + [StructLayout(LayoutKind.Sequential)] + public struct DpdConfig + { + /// + /// Configures the performance level of the DPD algorithm + /// + public DpdLevel lvl; + /// + /// The robustness coefficient for DPD training. Must be a positive value. + /// + public float rho; + /// + /// The absolute value of the endpoints of the VSG full-scale range, [-abs_vsg_max,abs_vsg_max]. Must be a positive value. + /// + public float abs_vsg_max; + /// + /// The number of samples to use when training the DPD. Must be a positive value less than the number of samples in the waveform. + /// + public int training_samples; + /// + /// The sampling rate (MHz) of the input waveform. Must be a positive value. + /// + public float f_sample; + /// + /// The DPD training algorithm will operate on input samples in the range [TDD_training_start_point , TDD_training_start_point + training_samples). For FDD, this can safely be set to 0. For TDD DL, choose this value so that the training algorithm is operating on a DL subframe. For TDD UL, choose this value so that the training algorithm is operating on a UL subframe. It is recommended that the subframe immediately after a UL-to-DL or DL-to-UL transition not be used for training. + /// + public int TDD_training_start_point; + } + + #endregion + + #region Private Data Structures + /// + /// Separate version of CFR Configuration Struct to allow for easier DLL calling (where Fc and BW arrays are separate inputs to the CFR methods + /// + [StructLayout(LayoutKind.Sequential)] + private struct CfrConfigLV + { + public int num_bands; + public float offset; + public float f_sample; + public float targetPAPR; + public float TDD_duty_cycle; + } + + /// + /// DPD trained or untrained. + /// + private enum dpdTrainedStatus + { + /// The DPD state has been modified by the training algorithm. + NST_DPD_TRAINED = 1, + /// The DPD state has been reset to its default value. In this state the DPD output matches its input. + NST_DPD_UNTRAINED = 0, + } + #endregion + + #region NanoSemi CFR + /// + /// Populates the CFR configuration structure with default settings. + /// + /// Default settings structure + public static CfrConfig CfrGetDefaultConfig() + { + PInvoke.nst_dpd_cfr_get_default_config(out CfrConfig config); + config.num_bands = 1; + config.bw_list = new float[1] { 0.0f }; + config.fc_list = new float[1] { 0.0f }; + return config; + } + + /// + /// The CFR algorithm clip-and-filters the input waveform to satisfy config.targetPAPR. + /// The input waveform must not contain Infinity or NaN samples. + /// All fields of the CFR configuration struct must have valid settings. + /// + /// The I channel of the input waveform to apply CFR to. + /// The Q channel of the input waveform to apply CFR to. + /// Returns the I channel of the waveform with CFR applied, with the same number of samples as wfm_in. + /// Returns the Q channel of the waveform with CFR applied, with the same number of samples as wfm_in. + /// Configures the behavior of the CFR algorithm. + public static void CfrApply(float[] wfm_in_i, float[] wfm_in_q, out float[] wfm_out_i, out float[] wfm_out_q, CfrConfig CfrConfiguration) + { + CfrConfigLV lvconf = new CfrConfigLV() + { + num_bands = CfrConfiguration.num_bands, + offset = CfrConfiguration.offset, + f_sample = CfrConfiguration.f_sample, + targetPAPR = CfrConfiguration.targetPAPR, + TDD_duty_cycle = CfrConfiguration.TDD_duty_cycle + }; + + int waveformLength = wfm_in_i.Length; + float[] wfm_out_i_pinv = new float[waveformLength]; + float[] wfm_out_q_pinv = new float[waveformLength]; + + int pInvokeResult = PInvoke.nst_dpd_cfr_apply_labview(wfm_in_i, wfm_in_q, waveformLength, wfm_out_i_pinv, wfm_out_q_pinv, lvconf, CfrConfiguration.fc_list, CfrConfiguration.bw_list); + TestForError(pInvokeResult); + + wfm_out_i = wfm_out_i_pinv; + wfm_out_q = wfm_out_q_pinv; + } + + /// + /// The CFR algorithm clip-and-filters the input waveform to satisfy config.targetPAPR. + /// The input waveform must not contain Infinity or NaN samples. + /// All fields of the CFR configuration struct must have valid settings. + /// + /// The Input waveform to apply CFR to. + /// Configures the behavior of the CFR algorithm. + /// Waveform with CFR applied + public static ComplexWaveform CfrApply(ComplexWaveform WaveformIn, CfrConfig CfrConfiguration) + { + ComplexSingle.DecomposeArray(WaveformIn.GetRawData(), out float[] IArray, out float[] QArray); + CfrApply(IArray, QArray, out float[] WfmOutI, out float[] WfmOutQ, CfrConfiguration); + + //Handle the necessary Conversion from I/Q arrays to NI ComplexWaveform Datatype + ComplexSingle[] PostCFRWaveformData = ComplexSingle.ComposeArray(WfmOutI, WfmOutQ); + ComplexWaveform CfrWaveformOut = new ComplexWaveform(0); + CfrWaveformOut.Append(PostCFRWaveformData); + CfrWaveformOut.PrecisionTiming = WaveformIn.PrecisionTiming; + + return CfrWaveformOut; + } + #endregion + + #region NanoSemi DPD + /// + /// Populates the DPD configuration structure with default settings. + /// + /// Return the default settings structure. + public static DpdConfig DpdGetDefaultConfig() + { + PInvoke.nst_dpd_get_default_config(out DpdConfig config); + config.abs_vsg_max = 1.01f; + return config; + } + + /// + /// Reset DPD to untrained state. + /// This call(1) resets the DPD state to its default (untrained) value, and (2) sets the DPD performance level to the value of config.lvl. + /// The DPD performance level must be valid. + /// + /// + public static void DpdResetTraining(DpdConfig DpdConfiguration) + { + int pInvokeResult = PInvoke.nst_dpd_reset_training(DpdConfiguration); + TestForError(pInvokeResult); + } + + /// + /// Query DPD training state. + /// Indicates whether the DPD algorithm has been trained, or is in an untrained state. + /// + /// Returns the training state of the DPD algorithm (True = Trained) + public static bool DpdQueryTrained() + { + int pInvokeResult = PInvoke.nst_dpd_query_trained(out dpdTrainedStatus trainedSt); + TestForError(pInvokeResult); + return (trainedSt == dpdTrainedStatus.NST_DPD_TRAINED); + } + /// + /// Train the Adaptive DPD. + /// Improve DPD linearization by comparing a predistorted waveform to the baseband DUT output waveform that results from applying that predistorted waveform to the DUT. + /// + /// This function updates the DPD state in memory; it has no return values. + /// The input waveforms must not contain Infinity or NaN samples. + /// For the wfm_dpd argument, the magnitudes of the complex waveform samples must not exceed config.abs_vsg_max. + /// For the wfm_Rx argument, the magnitudes of the complex waveform samples must not exceed config.abs_vsa_max. + /// All fields of the DPD configuration struct must have valid settings. + /// config.training_samples must be at least 5000. + /// + /// + /// The I channel of the predistorted waveform. + /// The Q channel of the predistorted waveform. + /// The I channel of the baseband DUT output waveform, with the same number of samples as wfm_in. + /// The Q channel of the baseband DUT output waveform, with the same number of samples as wfm_in. + /// Configures the behavior of the training algorithm. + public static void DpdTrain(float[] wfm_dpd_i, float[] wfm_dpd_q, float[] wfm_Rx_i, float[] wfm_Rx_q, DpdConfig DpdConfiguration) + { + int waveformLength = wfm_Rx_i.Length; + + int pInvokeResult = PInvoke.nst_dpd_train(wfm_dpd_i, wfm_dpd_q, waveformLength, wfm_Rx_i, wfm_Rx_q, DpdConfiguration); + TestForError(pInvokeResult); + } + + /// + /// Train the Adaptive DPD. + /// Improve DPD linearization by comparing a predistorted waveform to the baseband DUT output waveform that results from applying that predistorted waveform to the DUT. + /// + /// This function updates the DPD state in memory; it has no return values. + /// The input waveforms must not contain Infinity or NaN samples. + /// For the wfm_dpd argument, the magnitudes of the complex waveform samples must not exceed config.abs_vsg_max. + /// All fields of the DPD configuration struct must have valid settings. + /// config.training_samples must be at least 5000. + /// + /// + /// Waveform being generated to the DUT input + /// Acquired waveform from the DUT output + /// Configures the behavior of the training algorithm. + public static void DpdTrain(ComplexWaveform GeneratedWaveform, ComplexWaveform AcquiredWaveform, DpdConfig DpdConfiguration) + { + ComplexSingle.DecomposeArray(GeneratedWaveform.GetRawData(), out float[] wfm_dpd_i, out float[] wfm_dpd_q); + ComplexSingle.DecomposeArray(AcquiredWaveform.GetRawData(), out float[] wfm_Rx_i, out float[] wfm_Rx_q); + DpdTrain(wfm_dpd_i, wfm_dpd_q, wfm_Rx_i, wfm_Rx_q, DpdConfiguration); + } + + /// + /// Predistort a waveform. + /// + /// The input waveform must not contain Infinity or NaN samples. + /// The magnitudes of the complex waveform samples must not exceed config.abs_vsg_max. + /// All fields of the DPD configuration struct must have valid settings. + /// + /// + /// The I channel of the input waveform to apply DPD to. + /// The Q channel of the input waveform to apply DPD to. + /// Returns the I channel of the waveform with DPD applied, with the same number of samples as wfm_in. + /// Returns the Q channel of the waveform with DPD applied, with the same number of samples as wfm_in. + /// Configures the behavior of the training algorithm. + public static void DpdApply(float[] wfm_in_i, float[] wfm_in_q, out float[] wfm_out_i, out float[] wfm_out_q, DpdConfig DpdConfiguration) + { + int waveformLength = wfm_in_i.Length; + float[] wfm_out_i_pinv = new float[waveformLength]; + float[] wfm_out_q_pinv = new float[waveformLength]; + + int pInvokeResult = PInvoke.nst_dpd_apply(wfm_in_i, wfm_in_q, waveformLength, wfm_out_i_pinv, wfm_out_q_pinv, DpdConfiguration); + TestForError(pInvokeResult); + + wfm_out_i = wfm_out_i_pinv; + wfm_out_q = wfm_out_q_pinv; + } + + /// + /// Predistort a waveform. + /// + /// The input waveform must not contain Infinity or NaN samples. + /// The magnitudes of the complex waveform samples must not exceed config.abs_vsg_max. + /// All fields of the DPD configuration struct must have valid settings. + /// The Input waveform to apply DPD to. + /// Configures the behavior of the training algorithm. + /// Waveform with DPD applied + public static ComplexWaveform DpdApply(ComplexWaveform WaveformIn, DpdConfig DpdConfiguration) + { + ComplexSingle.DecomposeArray(WaveformIn.GetRawData(), out float[] IArray, out float[] QArray); + DpdApply(IArray, QArray, out float[] WfmOutI, out float[] WfmOutQ, DpdConfiguration); + + //Handle the necessary Conversion from I/Q arrays to NI ComplexWaveform Datatype + ComplexSingle[] PostDPDWaveformData = ComplexSingle.ComposeArray(WfmOutI, WfmOutQ); + ComplexWaveform DpdWaveformOut = new ComplexWaveform(0); + DpdWaveformOut.Append(PostDPDWaveformData); + DpdWaveformOut.PrecisionTiming = WaveformIn.PrecisionTiming; + + return DpdWaveformOut; + } + #endregion + + #region Utilities + /// + /// NSTDPD software module version. Get the version number of this release of NSTDPD. + /// + /// NSTDPD Version Number + public static float GetVersion() + { + int pInvokeResult = PInvoke.nst_dpd_version(out float version); + TestForError(pInvokeResult); + return version; + } + #endregion + + #region PInvoke + private class PInvoke + { + private const string nstdpddll = @"C:\NanoSemi\bin\nstdpd.dll"; + + [DllImport(nstdpddll, EntryPoint = "nst_dpd_error_description", CallingConvention = CallingConvention.StdCall)] + public static extern int nst_dpd_error_description(int code, out IntPtr description); + + [DllImport(nstdpddll, EntryPoint = "nst_dpd_version", CallingConvention = CallingConvention.StdCall)] + public static extern int nst_dpd_version(out float version); + + [DllImport(nstdpddll, EntryPoint = "nst_dpd_cfr_get_default_config", CallingConvention = CallingConvention.StdCall)] + public static extern int nst_dpd_cfr_get_default_config(out CfrConfig config); + + [DllImport(nstdpddll, EntryPoint = "nst_dpd_cfr_apply_labview", CallingConvention = CallingConvention.StdCall)] + public static extern int nst_dpd_cfr_apply_labview([In] float[] wfm_in_i, [In] float[] wfm_in_q, int count, [In, Out] float[] wfm_out_i, [In, Out] float[] wfm_out_q, CfrConfigLV config, [In] float[] fc_list, [In] float[] bw_list); + + [DllImport(nstdpddll, EntryPoint = "nst_dpd_reset_training", CallingConvention = CallingConvention.StdCall)] + public static extern int nst_dpd_reset_training(DpdConfig DpdConfiguration); + + [DllImport(nstdpddll, EntryPoint = "nst_dpd_train", CallingConvention = CallingConvention.StdCall)] + public static extern int nst_dpd_train(float[] wfm_dpd_i, float[] wfm_dpd_q, int count, float[] wfm_Rx_i, float[] wfm_Rx_q, DpdConfig DpdConfiguration); + + [DllImport(nstdpddll, EntryPoint = "nst_dpd_train_without_align", CallingConvention = CallingConvention.Cdecl)] + public static extern int nst_dpd_train_without_align(float[] wfm_dpd_i, float[] wfm_dpd_q, int count, float[] wfm_Rx_i, float[] wfm_Rx_q, DpdConfig DpdConfiguration); + + [DllImport(nstdpddll, EntryPoint = "nst_dpd_apply", CallingConvention = CallingConvention.StdCall)] + public static extern int nst_dpd_apply([In] float[] wfm_in_i, [In] float[] wfm_in_q, int count, [In, Out] float[] wfm_out_i, [In, Out] float[] wfm_out_q, DpdConfig DpdConfiguration); + + [DllImport(nstdpddll, EntryPoint = "nst_dpd_query_trained", CallingConvention = CallingConvention.StdCall)] + public static extern int nst_dpd_query_trained(out dpdTrainedStatus status); + + [DllImport(nstdpddll, EntryPoint = "nst_dpd_get_default_config", CallingConvention = CallingConvention.StdCall)] + public static extern int nst_dpd_get_default_config(out DpdConfig config); + } + private static int TestForError(int status) + { + if (status < 0) + { + string errorString = GetError(status); + throw new ExternalException(errorString, status); + } + return status; + } + private static string GetError(int code) + { + PInvoke.nst_dpd_error_description(code, out IntPtr errorPtr); + string errorString = Marshal.PtrToStringAnsi(errorPtr); + return errorString; + } + #endregion + } +} diff --git a/Source/Reference Design Libraries.sln b/Source/Reference Design Libraries.sln index 2e2e57a..64b0837 100644 --- a/Source/Reference Design Libraries.sln +++ b/Source/Reference Design Libraries.sln @@ -38,60 +38,120 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "RFmxInstr", "SA\RFmx Instr\ EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Envelope Tracking", "Methods\Envelope Tracking\Envelope Tracking.csproj", "{724A8D0C-EE49-405C-AA33-884FDA656088}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "NanosemiDPD", "Methods\NanosemiDPD\NanosemiDPD.csproj", "{7FDB7CDB-4281-4B0D-A407-A6D4C63FCD36}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU + Debug|x64 = Debug|x64 Release|Any CPU = Release|Any CPU + Release|x64 = Release|x64 EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution {9E8F4D28-AC1D-4A06-8C62-FC96097890B9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {9E8F4D28-AC1D-4A06-8C62-FC96097890B9}.Debug|Any CPU.Build.0 = Debug|Any CPU + {9E8F4D28-AC1D-4A06-8C62-FC96097890B9}.Debug|x64.ActiveCfg = Debug|Any CPU + {9E8F4D28-AC1D-4A06-8C62-FC96097890B9}.Debug|x64.Build.0 = Debug|Any CPU {9E8F4D28-AC1D-4A06-8C62-FC96097890B9}.Release|Any CPU.ActiveCfg = Release|Any CPU {9E8F4D28-AC1D-4A06-8C62-FC96097890B9}.Release|Any CPU.Build.0 = Release|Any CPU + {9E8F4D28-AC1D-4A06-8C62-FC96097890B9}.Release|x64.ActiveCfg = Release|Any CPU + {9E8F4D28-AC1D-4A06-8C62-FC96097890B9}.Release|x64.Build.0 = Release|Any CPU {89ABCAC5-CEDB-4535-BD58-E205A97834D9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {89ABCAC5-CEDB-4535-BD58-E205A97834D9}.Debug|Any CPU.Build.0 = Debug|Any CPU + {89ABCAC5-CEDB-4535-BD58-E205A97834D9}.Debug|x64.ActiveCfg = Debug|Any CPU + {89ABCAC5-CEDB-4535-BD58-E205A97834D9}.Debug|x64.Build.0 = Debug|Any CPU {89ABCAC5-CEDB-4535-BD58-E205A97834D9}.Release|Any CPU.ActiveCfg = Release|Any CPU {89ABCAC5-CEDB-4535-BD58-E205A97834D9}.Release|Any CPU.Build.0 = Release|Any CPU + {89ABCAC5-CEDB-4535-BD58-E205A97834D9}.Release|x64.ActiveCfg = Release|Any CPU + {89ABCAC5-CEDB-4535-BD58-E205A97834D9}.Release|x64.Build.0 = Release|Any CPU {6E38C48B-DD17-4438-9366-E336528E62AE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {6E38C48B-DD17-4438-9366-E336528E62AE}.Debug|Any CPU.Build.0 = Debug|Any CPU + {6E38C48B-DD17-4438-9366-E336528E62AE}.Debug|x64.ActiveCfg = Debug|Any CPU + {6E38C48B-DD17-4438-9366-E336528E62AE}.Debug|x64.Build.0 = Debug|Any CPU {6E38C48B-DD17-4438-9366-E336528E62AE}.Release|Any CPU.ActiveCfg = Release|Any CPU {6E38C48B-DD17-4438-9366-E336528E62AE}.Release|Any CPU.Build.0 = Release|Any CPU + {6E38C48B-DD17-4438-9366-E336528E62AE}.Release|x64.ActiveCfg = Release|Any CPU + {6E38C48B-DD17-4438-9366-E336528E62AE}.Release|x64.Build.0 = Release|Any CPU {3D1BC0F6-273C-4A44-82F6-3BB933862FFB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {3D1BC0F6-273C-4A44-82F6-3BB933862FFB}.Debug|Any CPU.Build.0 = Debug|Any CPU + {3D1BC0F6-273C-4A44-82F6-3BB933862FFB}.Debug|x64.ActiveCfg = Debug|Any CPU + {3D1BC0F6-273C-4A44-82F6-3BB933862FFB}.Debug|x64.Build.0 = Debug|Any CPU {3D1BC0F6-273C-4A44-82F6-3BB933862FFB}.Release|Any CPU.ActiveCfg = Release|Any CPU {3D1BC0F6-273C-4A44-82F6-3BB933862FFB}.Release|Any CPU.Build.0 = Release|Any CPU + {3D1BC0F6-273C-4A44-82F6-3BB933862FFB}.Release|x64.ActiveCfg = Release|Any CPU + {3D1BC0F6-273C-4A44-82F6-3BB933862FFB}.Release|x64.Build.0 = Release|Any CPU {7659CA3A-45D7-4A1C-8253-98615F119CF3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {7659CA3A-45D7-4A1C-8253-98615F119CF3}.Debug|Any CPU.Build.0 = Debug|Any CPU + {7659CA3A-45D7-4A1C-8253-98615F119CF3}.Debug|x64.ActiveCfg = Debug|Any CPU + {7659CA3A-45D7-4A1C-8253-98615F119CF3}.Debug|x64.Build.0 = Debug|Any CPU {7659CA3A-45D7-4A1C-8253-98615F119CF3}.Release|Any CPU.ActiveCfg = Release|Any CPU {7659CA3A-45D7-4A1C-8253-98615F119CF3}.Release|Any CPU.Build.0 = Release|Any CPU + {7659CA3A-45D7-4A1C-8253-98615F119CF3}.Release|x64.ActiveCfg = Release|Any CPU + {7659CA3A-45D7-4A1C-8253-98615F119CF3}.Release|x64.Build.0 = Release|Any CPU {A12F87E8-772D-4DD9-9AE1-E6A72E83F4FE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {A12F87E8-772D-4DD9-9AE1-E6A72E83F4FE}.Debug|Any CPU.Build.0 = Debug|Any CPU + {A12F87E8-772D-4DD9-9AE1-E6A72E83F4FE}.Debug|x64.ActiveCfg = Debug|Any CPU + {A12F87E8-772D-4DD9-9AE1-E6A72E83F4FE}.Debug|x64.Build.0 = Debug|Any CPU {A12F87E8-772D-4DD9-9AE1-E6A72E83F4FE}.Release|Any CPU.ActiveCfg = Release|Any CPU {A12F87E8-772D-4DD9-9AE1-E6A72E83F4FE}.Release|Any CPU.Build.0 = Release|Any CPU + {A12F87E8-772D-4DD9-9AE1-E6A72E83F4FE}.Release|x64.ActiveCfg = Release|Any CPU + {A12F87E8-772D-4DD9-9AE1-E6A72E83F4FE}.Release|x64.Build.0 = Release|Any CPU {B12280C1-69D7-4E52-8A41-95B1D568DD6B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {B12280C1-69D7-4E52-8A41-95B1D568DD6B}.Debug|Any CPU.Build.0 = Debug|Any CPU + {B12280C1-69D7-4E52-8A41-95B1D568DD6B}.Debug|x64.ActiveCfg = Debug|Any CPU + {B12280C1-69D7-4E52-8A41-95B1D568DD6B}.Debug|x64.Build.0 = Debug|Any CPU {B12280C1-69D7-4E52-8A41-95B1D568DD6B}.Release|Any CPU.ActiveCfg = Release|Any CPU {B12280C1-69D7-4E52-8A41-95B1D568DD6B}.Release|Any CPU.Build.0 = Release|Any CPU + {B12280C1-69D7-4E52-8A41-95B1D568DD6B}.Release|x64.ActiveCfg = Release|Any CPU + {B12280C1-69D7-4E52-8A41-95B1D568DD6B}.Release|x64.Build.0 = Release|Any CPU {1EB6F1AE-2EEA-4DC8-8CDB-09B5FADF2236}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {1EB6F1AE-2EEA-4DC8-8CDB-09B5FADF2236}.Debug|Any CPU.Build.0 = Debug|Any CPU + {1EB6F1AE-2EEA-4DC8-8CDB-09B5FADF2236}.Debug|x64.ActiveCfg = Debug|Any CPU + {1EB6F1AE-2EEA-4DC8-8CDB-09B5FADF2236}.Debug|x64.Build.0 = Debug|Any CPU {1EB6F1AE-2EEA-4DC8-8CDB-09B5FADF2236}.Release|Any CPU.ActiveCfg = Release|Any CPU {1EB6F1AE-2EEA-4DC8-8CDB-09B5FADF2236}.Release|Any CPU.Build.0 = Release|Any CPU + {1EB6F1AE-2EEA-4DC8-8CDB-09B5FADF2236}.Release|x64.ActiveCfg = Release|Any CPU + {1EB6F1AE-2EEA-4DC8-8CDB-09B5FADF2236}.Release|x64.Build.0 = Release|Any CPU {C725B272-463E-421D-8648-4E3F87868711}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {C725B272-463E-421D-8648-4E3F87868711}.Debug|Any CPU.Build.0 = Debug|Any CPU + {C725B272-463E-421D-8648-4E3F87868711}.Debug|x64.ActiveCfg = Debug|Any CPU + {C725B272-463E-421D-8648-4E3F87868711}.Debug|x64.Build.0 = Debug|Any CPU {C725B272-463E-421D-8648-4E3F87868711}.Release|Any CPU.ActiveCfg = Release|Any CPU {C725B272-463E-421D-8648-4E3F87868711}.Release|Any CPU.Build.0 = Release|Any CPU + {C725B272-463E-421D-8648-4E3F87868711}.Release|x64.ActiveCfg = Release|Any CPU + {C725B272-463E-421D-8648-4E3F87868711}.Release|x64.Build.0 = Release|Any CPU {8E7D5092-B685-4AD3-9DA3-7C53E56F0F32}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {8E7D5092-B685-4AD3-9DA3-7C53E56F0F32}.Debug|Any CPU.Build.0 = Debug|Any CPU + {8E7D5092-B685-4AD3-9DA3-7C53E56F0F32}.Debug|x64.ActiveCfg = Debug|Any CPU + {8E7D5092-B685-4AD3-9DA3-7C53E56F0F32}.Debug|x64.Build.0 = Debug|Any CPU {8E7D5092-B685-4AD3-9DA3-7C53E56F0F32}.Release|Any CPU.ActiveCfg = Release|Any CPU {8E7D5092-B685-4AD3-9DA3-7C53E56F0F32}.Release|Any CPU.Build.0 = Release|Any CPU + {8E7D5092-B685-4AD3-9DA3-7C53E56F0F32}.Release|x64.ActiveCfg = Release|Any CPU + {8E7D5092-B685-4AD3-9DA3-7C53E56F0F32}.Release|x64.Build.0 = Release|Any CPU {AB49D5A9-9585-445A-AE8A-E4DD2B8956AA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {AB49D5A9-9585-445A-AE8A-E4DD2B8956AA}.Debug|Any CPU.Build.0 = Debug|Any CPU + {AB49D5A9-9585-445A-AE8A-E4DD2B8956AA}.Debug|x64.ActiveCfg = Debug|Any CPU + {AB49D5A9-9585-445A-AE8A-E4DD2B8956AA}.Debug|x64.Build.0 = Debug|Any CPU {AB49D5A9-9585-445A-AE8A-E4DD2B8956AA}.Release|Any CPU.ActiveCfg = Release|Any CPU {AB49D5A9-9585-445A-AE8A-E4DD2B8956AA}.Release|Any CPU.Build.0 = Release|Any CPU + {AB49D5A9-9585-445A-AE8A-E4DD2B8956AA}.Release|x64.ActiveCfg = Release|Any CPU + {AB49D5A9-9585-445A-AE8A-E4DD2B8956AA}.Release|x64.Build.0 = Release|Any CPU {724A8D0C-EE49-405C-AA33-884FDA656088}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {724A8D0C-EE49-405C-AA33-884FDA656088}.Debug|Any CPU.Build.0 = Debug|Any CPU + {724A8D0C-EE49-405C-AA33-884FDA656088}.Debug|x64.ActiveCfg = Debug|Any CPU + {724A8D0C-EE49-405C-AA33-884FDA656088}.Debug|x64.Build.0 = Debug|Any CPU {724A8D0C-EE49-405C-AA33-884FDA656088}.Release|Any CPU.ActiveCfg = Release|Any CPU {724A8D0C-EE49-405C-AA33-884FDA656088}.Release|Any CPU.Build.0 = Release|Any CPU + {724A8D0C-EE49-405C-AA33-884FDA656088}.Release|x64.ActiveCfg = Release|Any CPU + {724A8D0C-EE49-405C-AA33-884FDA656088}.Release|x64.Build.0 = Release|Any CPU + {7FDB7CDB-4281-4B0D-A407-A6D4C63FCD36}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {7FDB7CDB-4281-4B0D-A407-A6D4C63FCD36}.Debug|Any CPU.Build.0 = Debug|Any CPU + {7FDB7CDB-4281-4B0D-A407-A6D4C63FCD36}.Debug|x64.ActiveCfg = Debug|x64 + {7FDB7CDB-4281-4B0D-A407-A6D4C63FCD36}.Debug|x64.Build.0 = Debug|x64 + {7FDB7CDB-4281-4B0D-A407-A6D4C63FCD36}.Release|Any CPU.ActiveCfg = Release|Any CPU + {7FDB7CDB-4281-4B0D-A407-A6D4C63FCD36}.Release|Any CPU.Build.0 = Release|Any CPU + {7FDB7CDB-4281-4B0D-A407-A6D4C63FCD36}.Release|x64.ActiveCfg = Release|x64 + {7FDB7CDB-4281-4B0D-A407-A6D4C63FCD36}.Release|x64.Build.0 = Release|x64 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -111,6 +171,7 @@ Global {8E7D5092-B685-4AD3-9DA3-7C53E56F0F32} = {9DF8AA31-0EBB-4E3D-ACAE-96F2350DA9F8} {AB49D5A9-9585-445A-AE8A-E4DD2B8956AA} = {2B079B61-B71B-440E-BFF7-0DF80927B5C5} {724A8D0C-EE49-405C-AA33-884FDA656088} = {3779EC48-73B9-4620-8E17-57340A70611A} + {7FDB7CDB-4281-4B0D-A407-A6D4C63FCD36} = {3779EC48-73B9-4620-8E17-57340A70611A} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {D020C05D-4EF8-45D5-8E12-D76DE5E66039}