ExampleAwgModule#

  1// ExampleAwgModule shows the usage of the AWG module.
  2// It uses the AWG sequencer to generate a wave form.
  3// The defined waveform is applied, measured and the
  4// results are written to a file.
  5public static void ExampleAwgModule(string dev = DEFAULT_DEVICE) // Timeout(10000)
  6{
  7  ziDotNET daq = connect(dev);
  8  resetDeviceToDefault(daq, dev);
  9  // check device type, option
 10  if (!isDeviceFamily(daq, dev, "UHFAWG") &&
 11      !isDeviceFamily(daq, dev, "UHFQA") &&
 12      !hasOption(daq, dev, "AWG"))
 13  {
 14    Skip("Test does not support this device.");
 15  }
 16  // Create instrument configuration: disable all outputs, demods and scopes.
 17  const ZIListNodes_enum flags = ZIListNodes_enum.ZI_LIST_NODES_LEAVESONLY;
 18  var hasDemods = daq.listNodes(String.Format("/{0}/demods/*", dev), flags).Count > 0;
 19  if (hasDemods)
 20  {
 21    daq.setInt(String.Format("/{0}/demods/*/enable", dev), 0);
 22    daq.setInt(String.Format("/{0}/demods/*/trigger", dev), 0);
 23  }
 24  
 25  daq.setInt(String.Format("/{0}/sigouts/*/enables/*", dev), 0);
 26  daq.setInt(String.Format("/{0}/scopes/*/enable", dev), 0);
 27  if (hasOption(daq, dev, "IA"))
 28  {
 29    daq.setInt(String.Format("/{0}/imps/*/enable", dev), 0);
 30  }
 31  daq.sync();
 32
 33  // Now configure the instrument for this experiment. The following channels
 34  // and indices work on all device configurations. The values below may be
 35  // changed if the instrument has multiple input/output channels and/or either
 36  // the Multifrequency or Multidemodulator options installed.
 37  int in_channel = 0;
 38  double frequency = 1e6;
 39  double amp = 1.0;
 40
 41  daq.setDouble(String.Format("/{0}/sigouts/0/amplitudes/*", dev), 0.0);
 42  daq.sync();
 43
 44  daq.setInt(String.Format("/{0}/sigins/0/imp50", dev), 1);
 45  daq.setInt(String.Format("/{0}/sigins/0/ac", dev), 0);
 46  daq.setInt(String.Format("/{0}/sigins/0/diff", dev), 0);
 47  daq.setInt(String.Format("/{0}/sigins/0/range", dev), 1);
 48  daq.setDouble(String.Format("/{0}/oscs/0/freq", dev), frequency);
 49  daq.setInt(String.Format("/{0}/sigouts/0/on", dev), 1);
 50  daq.setInt(String.Format("/{0}/sigouts/0/range", dev), 1);
 51  daq.setDouble(String.Format("/{0}/awgs/0/outputs/0/amplitude", dev), amp);
 52  daq.setInt(String.Format("/{0}/awgs/0/outputs/0/mode", dev), 0);
 53  daq.setInt(String.Format("/{0}/awgs/0/time", dev), 0);
 54  daq.setInt(String.Format("/{0}/awgs/0/userregs/0", dev), 0);
 55
 56  daq.sync();
 57
 58  // Number of points in AWG waveform
 59  int AWG_N = 2000;
 60
 61  // Define an AWG program as a string stored in the variable awg_program, equivalent to what would
 62  // be entered in the Sequence Editor window in the graphical UI.
 63  // This example demonstrates four methods of definig waveforms via the API
 64  // - (wave w0) loaded directly from programmatically generated CSV file wave0.csv.
 65  //             Waveform shape: Blackman window with negative amplitude.
 66  // - (wave w1) using the waveform generation functionalities available in the AWG Sequencer language.
 67  //             Waveform shape: Gaussian function with positive amplitude.
 68  // - (wave w2) using the vect() function and programmatic string replacement.
 69  //             Waveform shape: Single period of a sine wave.
 70  string awg_program =
 71    "const AWG_N = _c1_;\n" +
 72    "wave w0 = \"wave0\";\n" +
 73    "wave w1 = gauss(AWG_N, AWG_N/2, AWG_N/20);\n" +
 74    "wave w2 = vect(_w2_);\n" +
 75    "wave w3 = zeros(AWG_N);\n" +
 76    "setTrigger(1);\n" +
 77    "setTrigger(0);\n" +
 78    "playWave(w0);\n" +
 79    "playWave(w1);\n" +
 80    "playWave(w2);\n" +
 81    "playWave(w3);\n";
 82
 83  // Reference waves
 84
 85  // Define an array of values that are used to write values for wave w0 to a CSV file in the
 86  // module's data directory (Blackman windows)
 87  var waveform_0 = Enumerable.Range(0, AWG_N).Select(
 88    v => -1.0 * (0.42 - 0.5 * Math.Cos(2.0 * Math.PI * v / (AWG_N - 1)) + 0.08 * Math.Cos(4 * Math.PI * v / (AWG_N - 1))));
 89  double width = AWG_N / 20;
 90  var linspace = Enumerable.Range(0, AWG_N).Select(
 91    v => (v * AWG_N / ((double)AWG_N - 1.0d)) - AWG_N / 2);
 92  var waveform_1 = linspace.Select(
 93    v => Math.Exp(-v * v / (2 * width * width)));
 94  linspace = Enumerable.Range(0, AWG_N).Select(
 95    v => (v * 2 * Math.PI / ((double)AWG_N - 1.0d)));
 96  var waveform_2 = linspace.Select(
 97    v => Math.Sin(v));
 98  linspace = Enumerable.Range(0, AWG_N).Select(
 99    v => (v * 12 * Math.PI / ((double)AWG_N - 1.0d)) - 6 * Math.PI);
100  var waveform_3 = linspace.Select(
101    v => Sinc(v));
102
103  // concatenated reference wave
104  double f_s = 1.8e9; // sampling rate of scope and AWG
105  double full_scale = 0.75;
106  var y_expected = waveform_0.Concat(waveform_1).Concat(waveform_2).Concat(waveform_3).Select(
107    v => v * full_scale * amp).ToArray();
108  var x_expected = Enumerable.Range(0, 4 * AWG_N).Select(v => v / f_s).ToArray();
109
110  // Replace placeholders in program
111  awg_program = awg_program.Replace("_w2_", string.Join(",", waveform_2));
112  awg_program = awg_program.Replace("_c1_", AWG_N.ToString());
113
114  // Create an instance of the AWG Module
115  ziModule awgModule = daq.awgModule();
116  awgModule.setByte("device", dev);
117  awgModule.execute();
118
119
120  // Get the modules data directory
121  string data_dir = awgModule.getString("directory");
122  // All CSV files within the waves directory are automatically recognized by the AWG module
123  data_dir = data_dir + "\\awg\\waves";
124  if (!Directory.Exists(data_dir))
125  {
126    // The data directory is created by the AWG module and should always exist. If this exception is raised,
127    // something might be wrong with the file system.
128    Fail($"AWG module wave directory {data_dir} does not exist or is not a directory");
129  }
130  // Save waveform data to CSV
131  string csv_file = data_dir + "\\wave0.csv";
132  // The following line always formats a double as "3.14" and not "3,14".
133  var waveform_0_formatted = waveform_0.Select(v => v.ToString(CultureInfo.InvariantCulture));
134  File.WriteAllText(@csv_file, string.Join(",", waveform_0_formatted));
135
136  // Transfer the AWG sequence program. Compilation starts automatically.
137  // Note: when using an AWG program from a source file (and only then), the
138  //       compiler needs to be started explicitly with
139  //       awgModule.set("compiler/start", 1)
140  awgModule.setByte("compiler/sourcestring", awg_program);
141  while (awgModule.getInt("compiler/status") == -1)
142  {
143    System.Threading.Thread.Sleep(100);
144  }
145
146  // check compiler result
147  long status = awgModule.getInt("compiler/status");
148  if (status == 1)
149  {
150    // compilation failed
151    String message = awgModule.getString("compiler/statusstring");
152    System.Diagnostics.Trace.WriteLine("AWG Program:");
153    System.Diagnostics.Trace.WriteLine(awg_program);
154    System.Diagnostics.Trace.WriteLine("---");
155    System.Diagnostics.Trace.WriteLine(message, "Compiler message:");
156    Fail("Compilation failed");
157  }
158  if (status == 0)
159  {
160    System.Diagnostics.Trace.WriteLine("Compilation successful with no warnings" +
161      ", will upload the program to the instrument.");
162  }
163  if (status == 2)
164  {
165    System.Diagnostics.Trace.WriteLine("Compilation successful with warnings" +
166      ", will upload the program to the instrument.");
167    String message = awgModule.getString("compiler/statusstring");
168    System.Diagnostics.Trace.WriteLine("Compiler warning:");
169    System.Diagnostics.Trace.WriteLine(message);
170  }
171
172  // wait for waveform upload to finish
173  while (awgModule.getDouble("progress") < 1.0)
174  {
175    System.Diagnostics.Trace.WriteLine(
176      awgModule.getDouble("progress"), "Progress");
177    System.Threading.Thread.Sleep(100);
178  }
179
180  // Replace w3 with waveform_3 using vector write.
181  // Let N be the total number of waveforms and M>0 be the number of waveforms defined from CSV file. Then the index
182  // of the waveform to be replaced is defined as following:
183  // - 0,...,M-1 for all waveforms defined from CSV file alphabetically ordered by filename,
184  // - M,...,N-1 in the order that the waveforms are defined in the sequencer program.
185  // For the case of M=0, the index is defined as:
186  // - 0,...,N-1 in the order that the waveforms are defined in the sequencer program.
187  // Of course, for the trivial case of 1 waveform, use index=0 to replace it.
188  // Here we replace waveform w3, the 4th waveform defined in the sequencer program. Using 0-based indexing the
189  // index of the waveform we want to replace (w3, a vector of zeros) is 3:
190  // Write the waveform to the memory. For the transferred array, only 16-bit unsigned integer
191  // data (0...65536) is accepted.
192  // For dual-channel waves, interleaving is required.
193
194  // The following function corresponds to ziPython utility function 'convert_awg_waveform'.
195  Func<double, ushort> convert_awg_waveform = v => (ushort)((32767.0) * v);
196  daq.setVector(String.Format("/{0}/awgs/0/waveform/waves/3", dev), waveform_3.Select(convert_awg_waveform).ToArray());
197
198  // Configure the Scope for measurement
199  daq.setInt(
200    String.Format("/{0}/scopes/0/channels/0/inputselect", dev), in_channel);
201  daq.setInt(String.Format("/{0}/scopes/0/time", dev), 0);
202  daq.setInt(String.Format("/{0}/scopes/0/enable", dev), 0);
203  daq.setInt(String.Format("/{0}/scopes/0/length", dev), 16836);
204
205  // Now configure the scope's trigger to get aligned data.
206  daq.setInt(String.Format("/{0}/scopes/0/trigenable", dev), 1);
207  // Here we trigger on UHF signal input 1. If the instrument has the DIG Option installed we could
208  // trigger the scope using an AWG Trigger instead (see the `setTrigger(1);` line in `awg_program` above).
209  // 0:   Signal Input 1
210  // 192: AWG Trigger 1
211  long trigchannel = 0;
212  daq.setInt(String.Format("/{0}/scopes/0/trigchannel", dev), trigchannel);
213  if (trigchannel == 0)
214  {
215    // Trigger on the falling edge of the negative blackman waveform `w0` from our AWG program.
216    daq.setInt(String.Format("/{0}/scopes/0/trigslope", dev), 2);
217    daq.setDouble(String.Format("/{0}/scopes/0/triglevel", dev), -0.600);
218
219    // Set hysteresis triggering threshold to avoid triggering on noise
220    // 'trighysteresis/mode' :
221    //  0 - absolute, use an absolute value ('scopes/0/trighysteresis/absolute')
222    //  1 - relative, use a relative value ('scopes/0trighysteresis/relative') of the trigchannel's input range
223    //      (0.1=10%).
224    daq.setDouble(String.Format("/{0}/scopes/0/trighysteresis/mode", dev), 0);
225    daq.setDouble(String.Format("/{0}/scopes/0/trighysteresis/relative", dev), 0.025);
226
227    // Set a negative trigdelay to capture the beginning of the waveform.
228    daq.setDouble(String.Format("/{0}/scopes/0/trigdelay", dev), -1.0e-6);
229  }
230  else
231  {
232    // Assume we're using an AWG Trigger, then the scope configuration is simple: Trigger on rising edge.
233    daq.setInt(String.Format("/{0}/scopes/0/trigslope", dev), 1);
234
235    // Set trigdelay to 0.0: Start recording from when the trigger is activated.
236    daq.setDouble(String.Format("/{0}/scopes/0/trigdelay", dev), 0.0);
237  }
238
239  // the trigger reference position relative within the wave, a value of 0.5 corresponds to the center of the wave
240  daq.setDouble(String.Format("/{0}/scopes/0/trigreference", dev), 0.0);
241
242  // Set the hold off time in-between triggers.
243  daq.setDouble(String.Format("/{0}/scopes/0/trigholdoff", dev), 0.025);
244
245  // Set up the Scope Module.
246  ziModule scopeModule = daq.scopeModule();
247  scopeModule.setInt("mode", 1);
248  scopeModule.subscribe(String.Format("/{0}/scopes/0/wave", dev));
249  daq.setInt(String.Format("/{0}/scopes/0/single", dev), 1);
250  scopeModule.execute();
251  daq.setInt(String.Format("/{0}/scopes/0/enable", dev), 1);
252  daq.sync();
253  System.Threading.Thread.Sleep(100);
254
255  // Start the AWG in single-shot mode
256  daq.setInt(String.Format("/{0}/awgs/0/single", dev), 1);
257  daq.setInt(String.Format("/{0}/awgs/0/enable", dev), 1);
258
259  // Read the scope data (manual timeout of 1 second)
260  double local_timeout = 1.0;
261  while (scopeModule.progress() < 1.0 && local_timeout > 0.0)
262  {
263    System.Diagnostics.Trace.WriteLine(
264      scopeModule.progress() * 100.0, "Scope Progress");
265    System.Threading.Thread.Sleep(20);
266    local_timeout -= 0.02;
267  }
268  string path = String.Format("/{0}/scopes/0/wave", dev);
269  Lookup lookup = scopeModule.read();
270  ZIScopeWave[] scopeWaves1 = lookup[path][0].scopeWaves;
271  float[,] y_measured_in = SimpleValue.getFloatVec2D(scopeWaves1[0].wave);
272  float[] y_measured = new float[y_measured_in.Length];
273  for (int i = 0; i < y_measured_in.Length; ++i)
274  {
275    y_measured[i] = y_measured_in[0, i];
276  }
277
278  var x_measured = Enumerable.Range(0, y_measured.Length).Select(
279    v => -(long)v * scopeWaves1[0].header.dt +
280      (scopeWaves1[0].header.timeStamp -
281      scopeWaves1[0].header.triggerTimeStamp) / f_s
282      ).ToArray();
283
284  // write signals to files
285  String fileName = Environment.CurrentDirectory + "/awg_measured.txt";
286  System.IO.StreamWriter file = new System.IO.StreamWriter(fileName);
287  file.WriteLine("t [ns], measured signal [V]");
288  for (int i = 0; i < y_measured.Length; ++i)
289  {
290    file.WriteLine("{0} {1}", x_measured[i] * 1e9, y_measured[i]);
291  }
292  file.Close();
293
294  fileName = Environment.CurrentDirectory + "/awg_expected.txt";
295  file = new System.IO.StreamWriter(fileName);
296  file.WriteLine("t [ns], expected signal [V]");
297  for (int i = 0; i < y_expected.Length; ++i)
298  {
299    file.WriteLine("{0} {1}", x_expected[i] * 1e9, y_expected[i]);
300  }
301  file.Close();
302
303  // checks
304  AssertNotEqual(0, x_measured.Length);
305  AssertNotEqual(0, y_measured.Length);
306
307  // find minimal difference
308  double dMinMax = 1e10;
309  for (int i = 0; i < x_measured.Length - x_expected.Length; i++)
310  {
311    double dMax = 0;
312    for (int k = 0; k < x_expected.Length; k++)
313    {
314      double d = Math.Abs(y_expected[k] - y_measured[k + i]);
315      if (d > dMax)
316      {
317        dMax = d;
318      }
319    }
320
321    if (dMax < dMinMax)
322    {
323      dMinMax = dMax;
324    }
325  }
326  Debug.Assert(dMinMax < 0.1);
327
328  scopeModule.clear();  // Release module resources. Especially important if modules are created
329                        // inside a loop to prevent excessive resource consumption.
330  awgModule.clear();
331  daq.disconnect();
332}