ExampleAwgModuleΒΆ

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
// ExampleAwgModule shows the usage of the AWG module.
// It uses the AWG sequencer to generate a wave form.
// The defined waveform is applied, measured and the
// results are written to a file.
public static void ExampleAwgModule(string dev = DEFAULT_DEVICE) // Timeout(10000)
{
  ziDotNET daq = connect(dev);
  resetDeviceToDefault(daq, dev);

  // check device type, option
  if (!isDeviceFamily(daq, dev, "UHFAWG") && !isDeviceFamily(daq, dev, "UHFQA") && !hasOption(daq, dev, "AWG"))
  {
    Skip("Test does not support this device.");
  }

  // Create instrument configuration: disable all outputs, demods and scopes.
  daq.setInt(String.Format("/{0}/demods/*/enable", dev), 0);
  daq.setInt(String.Format("/{0}/demods/*/trigger", dev), 0);
  daq.setInt(String.Format("/{0}/sigouts/*/enables/*", dev), 0);
  daq.setInt(String.Format("/{0}/scopes/*/enable", dev), 0);
  if (hasOption(daq, dev, "IA"))
  {
    daq.setInt(String.Format("/{0}/imps/*/enable", dev), 0);
  }
  daq.sync();

  // Now configure the instrument for this experiment. The following channels
  // and indices work on all device configurations. The values below may be
  // changed if the instrument has multiple input/output channels and/or either
  // the Multifrequency or Multidemodulator options installed.
  int in_channel = 0;
  double frequency = 1e6;
  double amp = 1.0;

  daq.setDouble(String.Format("/{0}/sigouts/0/amplitudes/*", dev), 0.0);
  daq.sync();

  daq.setInt(String.Format("/{0}/sigins/0/imp50", dev), 1);
  daq.setInt(String.Format("/{0}/sigins/0/ac", dev), 0);
  daq.setInt(String.Format("/{0}/sigins/0/diff", dev), 0);
  daq.setInt(String.Format("/{0}/sigins/0/range", dev), 1);
  daq.setDouble(String.Format("/{0}/oscs/0/freq", dev), frequency);
  daq.setInt(String.Format("/{0}/sigouts/0/on", dev), 1);
  daq.setInt(String.Format("/{0}/sigouts/0/range", dev), 1);
  daq.setInt(String.Format("/{0}/sigouts/0/enables/3", dev), 1);
  daq.setDouble(String.Format("/{0}/awgs/0/outputs/0/amplitude", dev), amp);
  daq.setInt(String.Format("/{0}/awgs/0/outputs/0/mode", dev), 0);
  daq.setInt(String.Format("/{0}/awgs/0/time", dev), 0);
  daq.setInt(String.Format("/{0}/awgs/0/userregs/0", dev), 0);

  daq.sync();

  // Number of points in AWG waveform
  int AWG_N = 2000;

  // Define an AWG program as a string stored in the variable awg_program, equivalent to what would
  // be entered in the Sequence Editor window in the graphical UI.
  // This example demonstrates four methods of definig waveforms via the API
  // - (wave w0) loaded directly from programmatically generated CSV file wave0.csv.
  //             Waveform shape: Blackman window with negative amplitude.
  // - (wave w1) using the waveform generation functionalities available in the AWG Sequencer language.
  //             Waveform shape: Gaussian function with positive amplitude.
  // - (wave w2) using the vect() function and programmatic string replacement.
  //             Waveform shape: Single period of a sine wave.
  string awg_program =
    "const AWG_N = _c1_;\n" +
    "wave w0 = \"wave0\";\n" +
    "wave w1 = gauss(AWG_N, AWG_N/2, AWG_N/20);\n" +
    "wave w2 = vect(_w2_);\n" +
    "wave w3 = zeros(AWG_N);\n" +
    "setTrigger(1);\n" +
    "setTrigger(0);\n" +
    "playWave(w0);\n" +
    "playWave(w1);\n" +
    "playWave(w2);\n" +
    "playWave(w3);\n";

  // Reference waves

  // Define an array of values that are used to write values for wave w0 to a CSV file in the
  // module's data directory (Blackman windows)
  var waveform_0 = Enumerable.Range(0, AWG_N).Select(
    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))));
  double width = AWG_N / 20;
  var linspace = Enumerable.Range(0, AWG_N).Select(
    v => (v * AWG_N / ((double)AWG_N - 1.0d)) - AWG_N / 2);
  var waveform_1 = linspace.Select(
    v => Math.Exp(-v * v / (2 * width * width)));
  linspace = Enumerable.Range(0, AWG_N).Select(
    v => (v * 2 * Math.PI / ((double)AWG_N - 1.0d)));
  var waveform_2 = linspace.Select(
    v => Math.Sin(v));
  linspace = Enumerable.Range(0, AWG_N).Select(
    v => (v * 12 * Math.PI / ((double)AWG_N - 1.0d)) - 6 * Math.PI);
  var waveform_3 = linspace.Select(
    v => Sinc(v));

  // concatenated reference wave
  double f_s = 1.8e9; // sampling rate of scope and AWG
  double full_scale = 0.75;
  var y_expected = waveform_0.Concat(waveform_1).Concat(waveform_2).Concat(waveform_3).Select(
    v => v * full_scale * amp).ToArray();
  var x_expected = Enumerable.Range(0, 4 * AWG_N).Select(v => v / f_s).ToArray();

  // Replace placeholders in program
  awg_program = awg_program.Replace("_w2_", string.Join(",", waveform_2));
  awg_program = awg_program.Replace("_c1_", AWG_N.ToString());

  // Create an instance of the AWG Module
  ziModule awgModule = daq.awgModule();
  awgModule.setByte("device", dev);
  awgModule.execute();


  // Get the modules data directory
  string data_dir = awgModule.getString("directory");
  // All CSV files within the waves directory are automatically recognized by the AWG module
  data_dir = data_dir + "\\awg\\waves";
  if (!Directory.Exists(data_dir))
  {
    // The data directory is created by the AWG module and should always exist. If this exception is raised,
    // something might be wrong with the file system.
    Fail($"AWG module wave directory {data_dir} does not exist or is not a directory");
  }
  // Save waveform data to CSV
  string csv_file = data_dir + "\\wave0.csv";
  // The following line always formats a double as "3.14" and not "3,14".
  var waveform_0_formatted = waveform_0.Select(v => v.ToString(CultureInfo.InvariantCulture));
  File.WriteAllText(@csv_file, string.Join(",", waveform_0_formatted));

  // Transfer the AWG sequence program. Compilation starts automatically.
  // Note: when using an AWG program from a source file (and only then), the
  //       compiler needs to be started explicitly with
  //       awgModule.set("compiler/start", 1)
  awgModule.setByte("compiler/sourcestring", awg_program);
  while (awgModule.getInt("compiler/status") == -1)
  {
    System.Threading.Thread.Sleep(100);
  }

  // check compiler result
  long status = awgModule.getInt("compiler/status");
  if (status == 1)
  {
    // compilation failed
    String message = awgModule.getString("compiler/statusstring");
    System.Diagnostics.Trace.WriteLine("AWG Program:");
    System.Diagnostics.Trace.WriteLine(awg_program);
    System.Diagnostics.Trace.WriteLine("---");
    System.Diagnostics.Trace.WriteLine(message, "Compiler message:");
    Fail("Compilation failed");
  }
  if (status == 0)
  {
    System.Diagnostics.Trace.WriteLine("Compilation successful with no warnings" +
      ", will upload the program to the instrument.");
  }
  if (status == 2)
  {
    System.Diagnostics.Trace.WriteLine("Compilation successful with warnings" +
      ", will upload the program to the instrument.");
    String message = awgModule.getString("compiler/statusstring");
    System.Diagnostics.Trace.WriteLine("Compiler warning:");
    System.Diagnostics.Trace.WriteLine(message);
  }

  // wait for waveform upload to finish
  while (awgModule.getDouble("progress") < 1.0)
  {
    System.Diagnostics.Trace.WriteLine(
      awgModule.getDouble("progress"), "Progress");
    System.Threading.Thread.Sleep(100);
  }

  // Replace w3 with waveform_3 using vector write.
  // Let N be the total number of waveforms and M>0 be the number of waveforms defined from CSV file. Then the index
  // of the waveform to be replaced is defined as following:
  // - 0,...,M-1 for all waveforms defined from CSV file alphabetically ordered by filename,
  // - M,...,N-1 in the order that the waveforms are defined in the sequencer program.
  // For the case of M=0, the index is defined as:
  // - 0,...,N-1 in the order that the waveforms are defined in the sequencer program.
  // Of course, for the trivial case of 1 waveform, use index=0 to replace it.
  // Here we replace waveform w3, the 4th waveform defined in the sequencer program. Using 0-based indexing the
  // index of the waveform we want to replace (w3, a vector of zeros) is 3:
  // Write the waveform to the memory. For the transferred array, only 16-bit unsigned integer
  // data (0...65536) is accepted.
  // For dual-channel waves, interleaving is required.

  // The following function corresponds to ziPython utility function 'convert_awg_waveform'.
  Func<double, ushort> convert_awg_waveform = v => (ushort)((32767.0) * v);
  daq.setVector(String.Format("/{0}/awgs/0/waveform/waves/3", dev), waveform_3.Select(convert_awg_waveform).ToArray());

  // Configure the Scope for measurement
  daq.setInt(
    String.Format("/{0}/scopes/0/channels/0/inputselect", dev), in_channel);
  daq.setInt(String.Format("/{0}/scopes/0/time", dev), 0);
  daq.setInt(String.Format("/{0}/scopes/0/enable", dev), 0);
  daq.setInt(String.Format("/{0}/scopes/0/length", dev), 16836);

  // Now configure the scope's trigger to get aligned data.
  daq.setInt(String.Format("/{0}/scopes/0/trigenable", dev), 1);
  // Here we trigger on UHF signal input 1. If the instrument has the DIG Option installed we could
  // trigger the scope using an AWG Trigger instead (see the `setTrigger(1);` line in `awg_program` above).
  // 0:   Signal Input 1
  // 192: AWG Trigger 1
  long trigchannel = 0;
  daq.setInt(String.Format("/{0}/scopes/0/trigchannel", dev), trigchannel);
  if (trigchannel == 0)
  {
    // Trigger on the falling edge of the negative blackman waveform `w0` from our AWG program.
    daq.setInt(String.Format("/{0}/scopes/0/trigslope", dev), 2);
    daq.setDouble(String.Format("/{0}/scopes/0/triglevel", dev), -0.600);

    // Set hysteresis triggering threshold to avoid triggering on noise
    // 'trighysteresis/mode' :
    //  0 - absolute, use an absolute value ('scopes/0/trighysteresis/absolute')
    //  1 - relative, use a relative value ('scopes/0trighysteresis/relative') of the trigchannel's input range
    //      (0.1=10%).
    daq.setDouble(String.Format("/{0}/scopes/0/trighysteresis/mode", dev), 0);
    daq.setDouble(String.Format("/{0}/scopes/0/trighysteresis/relative", dev), 0.025);

    // Set a negative trigdelay to capture the beginning of the waveform.
    daq.setDouble(String.Format("/{0}/scopes/0/trigdelay", dev), -1.0e-6);
  }
  else
  {
    // Assume we're using an AWG Trigger, then the scope configuration is simple: Trigger on rising edge.
    daq.setInt(String.Format("/{0}/scopes/0/trigslope", dev), 1);

    // Set trigdelay to 0.0: Start recording from when the trigger is activated.
    daq.setDouble(String.Format("/{0}/scopes/0/trigdelay", dev), 0.0);
  }

  // the trigger reference position relative within the wave, a value of 0.5 corresponds to the center of the wave
  daq.setDouble(String.Format("/{0}/scopes/0/trigreference", dev), 0.0);

  // Set the hold off time in-between triggers.
  daq.setDouble(String.Format("/{0}/scopes/0/trigholdoff", dev), 0.025);

  // Set up the Scope Module.
  ziModule scopeModule = daq.scopeModule();
  scopeModule.setInt("mode", 1);
  scopeModule.subscribe(String.Format("/{0}/scopes/0/wave", dev));
  daq.setInt(String.Format("/{0}/scopes/0/single", dev), 1);
  scopeModule.execute();
  daq.setInt(String.Format("/{0}/scopes/0/enable", dev), 1);
  daq.sync();
  System.Threading.Thread.Sleep(100);

  // Start the AWG in single-shot mode
  daq.setInt(String.Format("/{0}/awgs/0/single", dev), 1);
  daq.setInt(String.Format("/{0}/awgs/0/enable", dev), 1);

  // Read the scope data (manual timeout of 1 second)
  double local_timeout = 1.0;
  while (scopeModule.progress() < 1.0 && local_timeout > 0.0)
  {
    System.Diagnostics.Trace.WriteLine(
      scopeModule.progress() * 100.0, "Scope Progress");
    System.Threading.Thread.Sleep(20);
    local_timeout -= 0.02;
  }
  string path = String.Format("/{0}/scopes/0/wave", dev);
  Lookup lookup = scopeModule.read();
  ZIScopeWave[] scopeWaves1 = lookup[path][0].scopeWaves;
  float[,] y_measured_in = SimpleValue.getFloatVec2D(scopeWaves1[0].wave);
  float[] y_measured = new float[y_measured_in.Length];
  for (int i = 0; i < y_measured_in.Length; ++i)
  {
    y_measured[i] = y_measured_in[0, i];
  }

  var x_measured = Enumerable.Range(0, y_measured.Length).Select(
    v => -(long)v * scopeWaves1[0].header.dt +
      (scopeWaves1[0].header.timeStamp -
      scopeWaves1[0].header.triggerTimeStamp) / f_s
      ).ToArray();

  // write signals to files
  String fileName = Environment.CurrentDirectory + "/awg_measured.txt";
  System.IO.StreamWriter file = new System.IO.StreamWriter(fileName);
  file.WriteLine("t [ns], measured signal [V]");
  for (int i = 0; i < y_measured.Length; ++i)
  {
    file.WriteLine("{0} {1}", x_measured[i] * 1e9, y_measured[i]);
  }
  file.Close();

  fileName = Environment.CurrentDirectory + "/awg_expected.txt";
  file = new System.IO.StreamWriter(fileName);
  file.WriteLine("t [ns], expected signal [V]");
  for (int i = 0; i < y_expected.Length; ++i)
  {
    file.WriteLine("{0} {1}", x_expected[i] * 1e9, y_expected[i]);
  }
  file.Close();

  // checks
  AssertNotEqual(0, x_measured.Length);
  AssertNotEqual(0, y_measured.Length);

  // find minimal difference
  double dMinMax = 1e10;
  for (int i = 0; i < x_measured.Length - x_expected.Length; i++)
  {
    double dMax = 0;
    for (int k = 0; k < x_expected.Length; k++)
    {
      double d = Math.Abs(y_expected[k] - y_measured[k + i]);
      if (d > dMax)
      {
        dMax = d;
      }
    }

    if (dMax < dMinMax)
    {
      dMinMax = dMax;
    }
  }
  Debug.Assert(dMinMax < 0.1);

  scopeModule.clear();  // Release module resources. Especially important if modules are created
                        // inside a loop to prevent excessive resource consumption.
  awgModule.clear();
  daq.disconnect();
}