Examples#
The following Examples are available in the Examples.cs file:
- ExampleAutorangingImpedance
- ExampleAwgModule
- ExampleDataAcquisition
- ExampleDeviceSettings
- ExampleGetDemodSample
- ExampleImpedanceCompensation
- ExampleImpedanceSweeper
- ExamplePidAdvisor
- ExamplePollDemodSample
- ExamplePollDoubleData
- ExamplePollImpedanceSample
- ExamplePollPwaData
- ExamplePollScopeData
- ExamplePollVectorData
- ExampleScopeModule
- ExampleSpectrum
- ExampleSweeper
Examples.cs#
1using System;
2using System.Collections.Generic;
3using System.Diagnostics;
4using System.Globalization;
5using System.IO;
6using System.Linq;
7using zhinst;
8
9namespace ziDotNetExamples
10{
11 /// <summary>
12 /// This exception is used to notify that the example could not be executed.
13 ///
14 /// <param name="msg">The reason why the example was ot executed</param>
15 /// </summary>
16 public class SkipException : Exception
17 {
18 public SkipException(string msg) : base(msg) { }
19 }
20
21 public class Examples
22 {
23 const string DEFAULT_DEVICE = "dev8047";
24
25 // The resetDeviceToDefault will reset the device settings
26 // to factory default. The call is quite expensive
27 // in runtime. Never use it inside loops!
28 private static void resetDeviceToDefault(ziDotNET daq, string dev)
29 {
30 if (isDeviceFamily(daq, dev, "HDAWG"))
31 {
32 // The HDAWG device does currently not support presets
33 return;
34 }
35 if (isDeviceFamily(daq, dev, "HF2"))
36 {
37 // The HF2 devices do not support the preset functionality.
38 daq.setDouble(String.Format("/{0}/demods/*/rate", dev), 250);
39 return;
40 }
41
42 daq.setInt(String.Format("/{0}/system/preset/index", dev), 0);
43 daq.setInt(String.Format("/{0}/system/preset/load", dev), 1);
44 while (daq.getInt(String.Format("/{0}/system/preset/busy", dev)) != 0)
45 {
46 System.Threading.Thread.Sleep(100);
47 }
48 System.Threading.Thread.Sleep(1000);
49 }
50
51 // The isDeviceFamily checks for a specific device family.
52 // Currently available families: "HF2", "UHF", "MF"
53 private static bool isDeviceFamily(ziDotNET daq, string dev, String family)
54 {
55 String path = String.Format("/{0}/features/devtype", dev);
56 String devType = daq.getByte(path);
57 return devType.StartsWith(family);
58 }
59
60 // The hasOption function checks if the device
61 // does support a specific functionality, thus
62 // has installed the option.
63 private static bool hasOption(ziDotNET daq, string dev, String option)
64 {
65 String path = String.Format("/{0}/features/options", dev);
66 String options = daq.getByte(path);
67 return options.Contains(option);
68 }
69
70 public static void SkipRequiresOption(ziDotNET daq, string dev, string option)
71 {
72 if (hasOption(daq, dev, option))
73 {
74 return;
75 }
76 daq.disconnect();
77 Skip($"Required a device with option {option}.");
78 }
79
80 public static void SkipForDeviceFamily(ziDotNET daq, string dev, string family)
81 {
82 if (isDeviceFamily(daq, dev, family))
83 {
84 Skip($"This example may not be run on a device of familiy {family}.");
85 daq.disconnect();
86 }
87 }
88
89 public static void SkipForDeviceFamilyAndOption(ziDotNET daq, string dev, string family, string option)
90 {
91 if (isDeviceFamily(daq, dev, family))
92 {
93 SkipRequiresOption(daq, dev, option);
94 }
95 }
96
97 // Please handle version mismatches depending on your
98 // application requirements. Version mismatches often relate
99 // to functionality changes of some nodes. The API interface is still
100 // identical. We strongly recommend to keep the version of the
101 // API and data server identical. Following approaches are possible:
102 // - Convert version mismatch to a warning for the user to upgrade / downgrade
103 // - Convert version mismatch to an error to enforce full matching
104 // - Do an automatic upgrade / downgrade
105 private static void apiServerVersionCheck(ziDotNET daq)
106 {
107 String serverVersion = daq.getByte("/zi/about/version");
108 String apiVersion = daq.version();
109
110 AssertEqual(serverVersion, apiVersion,
111 "Version mismatch between LabOne API and Data Server.");
112 }
113
114 // Connect initializes a session on the server.
115 private static ziDotNET connect(string dev)
116 {
117 ziDotNET daq = new ziDotNET();
118 String id = daq.discoveryFind(dev);
119 String iface = daq.discoveryGetValueS(dev, "connected");
120 if (string.IsNullOrWhiteSpace(iface))
121 {
122 // Device is not connected to the server
123 String ifacesList = daq.discoveryGetValueS(dev, "interfaces");
124 // Select the first available interface and use it to connect
125 string[] ifaces = ifacesList.Split('\n');
126 if (ifaces.Length > 0)
127 {
128 iface = ifaces[0];
129 }
130 }
131 String host = daq.discoveryGetValueS(dev, "serveraddress");
132 long port = daq.discoveryGetValueI(dev, "serverport");
133 long api = daq.discoveryGetValueI(dev, "apilevel");
134 System.Diagnostics.Trace.WriteLine(
135 String.Format("Connecting to server {0}:{1} wich API level {2}",
136 host, port, api));
137 daq.init(host, Convert.ToUInt16(port), (ZIAPIVersion_enum)api);
138 // Ensure that LabOne API and LabOne Data Server are from
139 // the same release version.
140 apiServerVersionCheck(daq);
141 // If device is not yet connected a reconnect
142 // will not harm.
143 System.Diagnostics.Trace.WriteLine(
144 String.Format("Connecting to {0} on inteface {1}", dev, iface));
145 daq.connectDevice(dev, iface, "");
146
147 return daq;
148 }
149
150 private static void Skip(string msg)
151 {
152 throw new SkipException($"SKIP: {msg}");
153 }
154
155 private static void Fail(string msg = null)
156 {
157 if (msg == null)
158 {
159 throw new Exception("FAILED!");
160 }
161 throw new SkipException($"FAILED: {msg}!");
162 }
163
164 private static void AssertNotEqual<T>(T expected, T actual, string msg = null) where T : IComparable<T>
165 {
166 if (msg != null)
167 {
168 Debug.Assert(!expected.Equals(actual));
169 return;
170 }
171 Debug.Assert(!expected.Equals(actual));
172 }
173
174 private static void AssertEqual<T>(T expected, T actual, string msg = null) where T : IComparable<T>
175 {
176 if (msg != null)
177 {
178 Debug.Assert(expected.Equals(actual), msg);
179 return;
180 }
181 Debug.Assert(expected.Equals(actual));
182 }
183
184 // ExamplePollDemodSample connects to the device,
185 // subscribes to a demodulator, polls the data for 0.1 s
186 // and returns the data.
187 public static void ExamplePollDemodSample(string dev = DEFAULT_DEVICE)
188 {
189 ziDotNET daq = connect(dev);
190 SkipForDeviceFamily(daq, dev, "HDAWG");
191
192 resetDeviceToDefault(daq, dev);
193 String path = String.Format("/{0}/demods/0/sample", dev);
194 daq.subscribe(path);
195 // After the subscribe, poll() can be executed continuously within a loop.
196 // Arguments to poll() are:
197 // - 'duration': poll duration in seconds
198 // - 'timeOutMilliseconds': timeout to wait for any packet from data server
199 // - 'flags': combination of poll flags that determine the
200 // behavior upon sample loss (see e.g., Python API for more information)
201 // - 'bufferSize': should be provided as 1 and will be removed in a
202 // future version
203 Lookup lookup = daq.poll(0.1, 100, 0, 1);
204 Dictionary<String, Chunk[]> nodes = lookup.nodes; // Iterable nodes
205 Chunk[] chunks = lookup[path]; // Iterable chunks
206 Chunk chunk = lookup[path][0]; // Single chunk
207 // Vector of samples
208 ZIDemodSample[] demodSamples = lookup[path][0].demodSamples;
209 // Single sample
210 ZIDemodSample demodSample0 = lookup[path][0].demodSamples[0];
211 daq.disconnect();
212
213 Debug.Assert(0 != demodSample0.timeStamp);
214 }
215
216 // ExamplePollImpedanceSample connects to the device,
217 // subscribes to a impedance stream, polls the data for 0.1 s
218 // and returns the data.
219 public static void ExamplePollImpedanceSample(string dev = DEFAULT_DEVICE)
220 {
221 ziDotNET daq = connect(dev);
222 // This example only works for devices with installed
223 // Impedance Analyzer (IA) option.
224 if (!hasOption(daq, dev, "IA"))
225 {
226 daq.disconnect();
227 Skip("Not supported by device.");
228 }
229 resetDeviceToDefault(daq, dev);
230 // Enable impedance control
231 daq.setInt(String.Format("/{0}/imps/0/enable", dev), 1);
232 // Return R and Cp
233 daq.setInt(String.Format("/{0}/imps/0/model", dev), 0);
234 // Enable user compensation
235 daq.setInt(String.Format("/{0}/imps/0/calib/user/enable", dev), 1);
236 // Wait until auto ranging has settled
237 System.Threading.Thread.Sleep(4000);
238 // Subscribe to the impedance data stream
239 String path = String.Format("/{0}/imps/0/sample", dev);
240 daq.subscribe(path);
241 // After the subscribe, poll() can be executed continuously within a loop.
242 // Arguments to poll() are:
243 // - 'duration': poll duration in seconds
244 // - 'timeOutMilliseconds': timeout to wait for any packet from data server
245 // - 'flags': combination of poll flags that determine the
246 // behavior upon sample loss (see e.g., Python API for more information)
247 // - 'bufferSize': should be provided as 1 and will be removed in a
248 // future version
249 Lookup lookup = daq.poll(0.1, 100, 0, 1);
250 Dictionary<String, Chunk[]> nodes = lookup.nodes; // Iterable nodes
251 Chunk[] chunks = lookup[path]; // Iterable chunks
252 Chunk chunk = lookup[path][0]; // Single chunk
253 // Vector of samples
254 ZIImpedanceSample[] impedanceSamples = lookup[path][0].impedanceSamples;
255 // Single sample
256 ZIImpedanceSample impedanceSample0 = lookup[path][0].impedanceSamples[0];
257 // Extract the R||C representation values
258 System.Diagnostics.Trace.WriteLine(
259 String.Format("Impedance Resistor value: {0} Ohm.", impedanceSample0.param0));
260 System.Diagnostics.Trace.WriteLine(
261 String.Format("Impedance Capacitor value: {0} F.", impedanceSample0.param1));
262 daq.disconnect();
263
264 AssertNotEqual(0ul, impedanceSample0.timeStamp);
265 }
266
267 // ExamplePollDoubleData is similar to ExamplePollDemodSample,
268 // but it subscribes and polls floating point data.
269 public static void ExamplePollDoubleData(string dev = DEFAULT_DEVICE)
270 {
271 ziDotNET daq = connect(dev);
272 String path = String.Format("/{0}/oscs/0/freq", dev);
273 daq.getAsEvent(path);
274 daq.subscribe(path);
275 Lookup lookup = daq.poll(1, 100, 0, 1);
276 Dictionary<String, Chunk[]> nodes = lookup.nodes; // Iterable nodes
277 Chunk[] chunks = lookup[path]; // Iterable chunks
278 Chunk chunk = lookup[path][0]; // Single chunk
279 ZIDoubleData[] doubleData = lookup[path][0].doubleData; // Vector of samples
280 ZIDoubleData doubleData0 = lookup[path][0].doubleData[0]; // Single sample
281 daq.disconnect();
282
283 AssertNotEqual(0ul, doubleData0.timeStamp);
284 }
285
286 // ExamplePollPwaData is similar to ExamplePollDemodSample,
287 // but it subscribes and polls periodic waveform analyzer
288 // data from a device with the Boxcar option.
289 public static void ExamplePollPwaData(string dev = DEFAULT_DEVICE) // Timeout(10000)
290 {
291 ziDotNET daq = connect(dev);
292 // The PWA example only works for devices with installed Boxcar (BOX) option
293 if (hasOption(daq, dev, "BOX"))
294 {
295 String enablePath = String.Format("/{0}/inputpwas/0/enable", dev);
296 daq.setInt(enablePath, 1);
297 String path = String.Format("/{0}/inputpwas/0/wave", dev);
298 daq.subscribe(path);
299 Lookup lookup = daq.poll(1, 100, 0, 1);
300 UInt64 timeStamp = lookup[path][0].pwaWaves[0].timeStamp;
301 UInt64 sampleCount = lookup[path][0].pwaWaves[0].sampleCount;
302 UInt32 inputSelect = lookup[path][0].pwaWaves[0].inputSelect;
303 UInt32 oscSelect = lookup[path][0].pwaWaves[0].oscSelect;
304 UInt32 harmonic = lookup[path][0].pwaWaves[0].harmonic;
305 Double frequency = lookup[path][0].pwaWaves[0].frequency;
306 Byte type = lookup[path][0].pwaWaves[0].type;
307 Byte mode = lookup[path][0].pwaWaves[0].mode;
308 Byte overflow = lookup[path][0].pwaWaves[0].overflow;
309 Byte commensurable = lookup[path][0].pwaWaves[0].commensurable;
310 double[] grid = lookup[path][0].pwaWaves[0].binPhase;
311 double[] x = lookup[path][0].pwaWaves[0].x;
312 double[] y = lookup[path][0].pwaWaves[0].y;
313 String fileName = Environment.CurrentDirectory + "/pwa.txt";
314 System.IO.StreamWriter file = new System.IO.StreamWriter(fileName);
315 file.WriteLine("TimeStamp: {0}", timeStamp);
316 file.WriteLine("Sample Count: {0}", sampleCount);
317 file.WriteLine("Input Select: {0}", inputSelect);
318 file.WriteLine("Osc Select: {0}", oscSelect);
319 file.WriteLine("Frequency: {0}", frequency);
320 for (int i = 0; i < grid.Length; ++i)
321 {
322 file.WriteLine("{0} {1} {2}", grid[i], x[i], y[i]);
323 }
324 file.Close();
325
326 AssertNotEqual(0ul, timeStamp);
327 AssertNotEqual(0ul, sampleCount);
328 AssertNotEqual(0, grid.Length);
329 }
330 daq.disconnect();
331 }
332
333 // ExamplePollScopeData is similar to ExamplePollDemodSample,
334 // but it subscribes and polls scope data.
335 public static void ExamplePollScopeData(string dev = DEFAULT_DEVICE)
336 {
337 ziDotNET daq = connect(dev);
338 SkipForDeviceFamily(daq, dev, "HDAWG");
339
340 resetDeviceToDefault(daq, dev);
341
342 String enablePath = String.Format("/{0}/scopes/0/enable", dev);
343 daq.setInt(enablePath, 1);
344 String path = String.Format("/{0}/scopes/0/wave", dev);
345 daq.subscribe(path);
346 Lookup lookup = daq.poll(1, 100, 0, 1);
347 UInt64 timeStamp = lookup[path][0].scopeWaves[0].header.timeStamp;
348 UInt64 sampleCount = lookup[path][0].scopeWaves[0].header.totalSamples;
349 daq.disconnect();
350
351 AssertNotEqual(0ul, timeStamp);
352 AssertNotEqual(0ul, sampleCount);
353 }
354
355 // ExamplePollVectorData connects to the device, requests data from
356 // vector nodes, and polls until data is received.
357 public static void ExamplePollVectorData(string dev = DEFAULT_DEVICE)
358 {
359 ziDotNET daq = connect(dev);
360
361 // This example only works for devices with the AWG option
362 if (hasOption(daq, dev, "AWG") || isDeviceFamily(daq, dev, "UHFQA") || isDeviceFamily(daq, dev, "UHFAWG") || isDeviceFamily(daq, dev, "HDAWG"))
363 {
364 resetDeviceToDefault(daq, dev);
365
366 // Request vector node from device
367 String path = String.Format("/{0}/awgs/0/waveform/waves/0", dev);
368 daq.getAsEvent(path);
369
370 // Poll until the node path is found in the result data
371 double timeout = 20;
372 double poll_time = 0.1;
373 Lookup lookup = null;
374 for (double time = 0; ; time += poll_time)
375 {
376 lookup = daq.poll(poll_time, 100, 0, 1);
377 if (lookup.nodes.ContainsKey(path))
378 break;
379 if (time > timeout)
380 Fail("Vector node data not received within timeout");
381 }
382
383 Chunk[] chunks = lookup[path]; // Iterable chunks
384 Chunk chunk = chunks[0]; // Single chunk
385 ZIVectorData vectorData = chunk.vectorData[0];
386
387 // The vector attribute of a ZIVectorData object holds a ZIVector object,
388 // which can contain a String or arrays of the following types:
389 // byte, UInt16, Uint32, Uint64, float, double
390
391 // Waveform vector data is stored as 32-bit unsigned integer
392 if (vectorData.vector != null) // Check for empty container
393 {
394 UInt32[] vector = vectorData.vector.data as UInt32[];
395 }
396
397 AssertNotEqual(0ul, vectorData.timeStamp);
398 }
399 daq.disconnect();
400 }
401
402 // ExampleGetDemodSample reads the demodulator sample value of the specified node.
403 public static void ExampleGetDemodSample(string dev = DEFAULT_DEVICE)
404 {
405 ziDotNET daq = connect(dev);
406 SkipForDeviceFamily(daq, dev, "HDAWG");
407
408 resetDeviceToDefault(daq, dev);
409 String path = String.Format("/{0}/demods/0/sample", dev);
410 ZIDemodSample sample = daq.getDemodSample(path);
411 System.Diagnostics.Trace.WriteLine(sample.frequency, "Sample frequency");
412 daq.disconnect();
413
414 AssertNotEqual(0ul, sample.timeStamp);
415 }
416
417 // ExampleSweeper instantiates a sweeper module and executes a sweep
418 // over 100 data points from 1kHz to 100kHz and writes the result into a file.
419 public static void ExampleSweeper(string dev = DEFAULT_DEVICE) // Timeout(40000)
420 {
421 ziDotNET daq = connect(dev);
422 SkipForDeviceFamily(daq, dev, "HDAWG");
423
424 resetDeviceToDefault(daq, dev);
425 ziModule sweep = daq.sweeper();
426 sweep.setByte("device", dev);
427 sweep.setDouble("start", 1e3);
428 sweep.setDouble("stop", 1e5);
429 sweep.setDouble("samplecount", 100);
430 String path = String.Format("/{0}/demods/0/sample", dev);
431 sweep.subscribe(path);
432 sweep.execute();
433 while (!sweep.finished())
434 {
435 System.Threading.Thread.Sleep(100);
436 double progress = sweep.progress() * 100;
437 System.Diagnostics.Trace.WriteLine(progress, "Progress");
438 }
439 Lookup lookup = sweep.read();
440 double[] grid = lookup[path][0].sweeperDemodWaves[0].grid;
441 double[] x = lookup[path][0].sweeperDemodWaves[0].x;
442 double[] y = lookup[path][0].sweeperDemodWaves[0].y;
443 String fileName = Environment.CurrentDirectory + "/sweep.txt";
444 System.IO.StreamWriter file = new System.IO.StreamWriter(fileName);
445 ZIChunkHeader header = lookup[path][0].header;
446 // Raw system time is the number of microseconds since linux epoch
447 file.WriteLine("Raw System Time: {0}", header.systemTime);
448 // Use the utility function ziSystemTimeToDateTime to convert to DateTime of .NET
449 file.WriteLine("Converted System Time: {0}", ziUtility.ziSystemTimeToDateTime(lookup[path][0].header.systemTime));
450 file.WriteLine("Created Timestamp: {0}", header.createdTimeStamp);
451 file.WriteLine("Changed Timestamp: {0}", header.changedTimeStamp);
452 for (int i = 0; i < grid.Length; ++i)
453 {
454 file.WriteLine("{0} {1} {2}", grid[i], x[i], y[i]);
455 }
456 file.Close();
457
458 AssertEqual(1.0, sweep.progress());
459 AssertNotEqual(0, grid.Length);
460
461 sweep.clear(); // Release module resources. Especially important if modules are created
462 // inside a loop to prevent excessive resource consumption.
463 daq.disconnect();
464 }
465
466 // ExampleImpedanceSweeper instantiates a sweeper module and prepares
467 // all settings for an impedance sweep over 30 data points.
468 // The results are written to a file.
469 public static void ExampleImpedanceSweeper(string dev = DEFAULT_DEVICE) // Timeout(40000)
470 {
471 ziDotNET daq = connect(dev);
472 // This example only works for devices with installed
473 // Impedance Analyzer (IA) option.
474 if (!hasOption(daq, dev, "IA"))
475 {
476 daq.disconnect();
477 Skip("Not supported by device.");
478 }
479
480 resetDeviceToDefault(daq, dev);
481 // Enable impedance control
482 daq.setInt(String.Format("/{0}/imps/0/enable", dev), 1);
483 // Return D and Cs
484 daq.setInt(String.Format("/{0}/imps/0/model", dev), 4);
485 // Enable user compensation
486 daq.setInt(String.Format("/{0}/imps/0/calib/user/enable", dev), 1);
487
488 // ensure correct settings of order and oscselect
489 daq.setInt(String.Format("/{0}/imps/0/demod/order", dev), 8);
490 daq.setInt(String.Format("/{0}/imps/0/demod/oscselect", dev), 0);
491 daq.sync();
492
493 ziModule sweep = daq.sweeper();
494 // Sweeper settings
495 sweep.setByte("device", dev);
496 sweep.setDouble("start", 1e3);
497 sweep.setDouble("stop", 5e6);
498 sweep.setDouble("samplecount", 30);
499 sweep.setDouble("order", 8);
500 sweep.setDouble("settling/inaccuracy", 0.0100000);
501 sweep.setDouble("bandwidthcontrol", 2);
502 sweep.setDouble("maxbandwidth", 10.0);
503 sweep.setDouble("bandwidthoverlap", 1);
504 sweep.setDouble("xmapping", 1);
505 sweep.setDouble("omegasuppression", 100.0);
506 sweep.setDouble("averaging/sample", 200);
507 sweep.setDouble("averaging/time", 0.100);
508 sweep.setDouble("averaging/tc", 20.0);
509 String path = String.Format("/{0}/imps/0/sample", dev);
510 sweep.subscribe(path);
511 sweep.execute();
512 while (!sweep.finished())
513 {
514 System.Threading.Thread.Sleep(100);
515 double progress = sweep.progress() * 100;
516 System.Diagnostics.Trace.WriteLine(progress, "Progress");
517 }
518 Lookup lookup = sweep.read();
519 double[] grid = lookup[path][0].sweeperImpedanceWaves[0].grid;
520 double[] x = lookup[path][0].sweeperImpedanceWaves[0].realz;
521 double[] y = lookup[path][0].sweeperImpedanceWaves[0].imagz;
522 double[] param0 = lookup[path][0].sweeperImpedanceWaves[0].param0;
523 double[] param1 = lookup[path][0].sweeperImpedanceWaves[0].param1;
524 UInt64[] flags = lookup[path][0].sweeperImpedanceWaves[0].flags;
525 // Save measurement data to file
526 String fileName = Environment.CurrentDirectory + "/impedance.txt";
527 System.IO.StreamWriter file = new System.IO.StreamWriter(fileName);
528 ZIChunkHeader header = lookup[path][0].header;
529 // Raw system time is the number of microseconds since linux epoch
530 file.WriteLine("Raw System Time: {0}", header.systemTime);
531 // Use the utility function ziSystemTimeToDateTime to convert to DateTime of .NET
532 file.WriteLine("Converted System Time: {0}", ziUtility.ziSystemTimeToDateTime(lookup[path][0].header.systemTime));
533 file.WriteLine("Created Timestamp: {0}", header.createdTimeStamp);
534 file.WriteLine("Changed Timestamp: {0}", header.changedTimeStamp);
535 for (int i = 0; i < grid.Length; ++i)
536 {
537 file.WriteLine("{0} {1} {2} {3} {4} {5}",
538 grid[i],
539 x[i],
540 y[i],
541 param0[i],
542 param1[i],
543 flags[i]);
544 }
545 file.Close();
546
547 AssertEqual(1.0, sweep.progress());
548 AssertNotEqual(0, grid.Length);
549
550 sweep.clear(); // Release module resources. Especially important if modules are created
551 // inside a loop to prevent excessive resource consumption.
552 daq.disconnect();
553 }
554
555 // ExampleImpedanceCompensation does a user compensation
556 // of the impedance analyser.
557 public static void ExampleImpedanceCompensation(string dev = DEFAULT_DEVICE) // Timeout(30000)
558 {
559 ziDotNET daq = connect(dev);
560 // This example only works for devices with installed
561 // Impedance Analyzer (IA) option.
562 if (!hasOption(daq, dev, "IA"))
563 {
564 daq.disconnect();
565 Skip("Not supported by device.");
566 }
567
568 resetDeviceToDefault(daq, dev);
569
570 // Enable impedance control
571 daq.setInt(String.Format("/{0}/imps/0/enable", dev), 1);
572 ziModule calib = daq.impedanceModule();
573 calib.execute();
574 calib.setByte("device", dev);
575 System.Threading.Thread.Sleep(200);
576 calib.setInt("mode", 4);
577 calib.setDouble("loads/2/r", 1000.0);
578 calib.setDouble("loads/2/c", 0.0);
579 calib.setDouble("freq/start", 100.0);
580 calib.setDouble("freq/stop", 500e3);
581 calib.setDouble("freq/samplecount", 21);
582
583 daq.setInt(String.Format("/{0}/imps/0/demod/order", dev), 8);
584 daq.setInt(String.Format("/{0}/imps/0/demod/oscselect", dev), 0);
585 daq.sync();
586
587
588 calib.setInt("step", 2);
589 calib.setInt("calibrate", 1);
590 while (true)
591 {
592 System.Threading.Thread.Sleep(100);
593 double progress = calib.progress() * 100;
594 System.Diagnostics.Trace.WriteLine(progress, "Progress");
595 Int64 calibrate = calib.getInt("calibrate");
596 if (calibrate == 0)
597 {
598 break;
599 }
600 }
601 String message = calib.getString("message");
602 System.Diagnostics.Trace.WriteLine(message, "Message");
603 AssertNotEqual(0, calib.progress());
604
605 calib.clear(); // Release module resources. Especially important if modules are created
606 // inside a loop to prevent excessive resource consumption.
607 daq.disconnect();
608 }
609
610 // ExampleSpectrum instantiates the spectrum module,
611 // reads the data and writes the result in to a file.
612 public static void ExampleSpectrum(string dev = DEFAULT_DEVICE) // Timeout(20000)
613 {
614 ziDotNET daq = connect(dev);
615 SkipForDeviceFamily(daq, dev, "HDAWG");
616 resetDeviceToDefault(daq, dev);
617 ziModule spectrum = daq.spectrum();
618 spectrum.setByte("device", dev);
619 spectrum.setInt("bit", 10);
620 String path = String.Format("/{0}/demods/0/sample", dev);
621 spectrum.subscribe(path);
622 spectrum.execute();
623 while (!spectrum.finished())
624 {
625 System.Threading.Thread.Sleep(100);
626 double progress = spectrum.progress() * 100;
627 System.Diagnostics.Trace.WriteLine(progress, "Progress");
628 }
629 Lookup lookup = spectrum.read();
630 double[] grid = lookup[path][0].spectrumWaves[0].grid;
631 double[] x = lookup[path][0].spectrumWaves[0].x;
632 double[] y = lookup[path][0].spectrumWaves[0].y;
633 String fileName = Environment.CurrentDirectory + "/spectrum.txt";
634 System.IO.StreamWriter file = new System.IO.StreamWriter(fileName);
635 for (int i = 0; i < grid.Length; ++i)
636 {
637 file.WriteLine("{0} {1} {2}", grid[i], x[i], y[i]);
638 }
639 file.Close();
640
641 AssertEqual(1.0, spectrum.progress());
642 AssertNotEqual(0, grid.Length);
643
644 spectrum.clear(); // Release module resources. Especially important if modules are created
645 // inside a loop to prevent excessive resource consumption.
646 daq.disconnect();
647 }
648
649 // ExampleScopeModule instantiates a scope module.
650 public static void ExampleScopeModule(string dev = DEFAULT_DEVICE) // Timeout(20000)
651 {
652 ziDotNET daq = connect(dev);
653 if (isDeviceFamily(daq, dev, "HDAWG"))
654 {
655 daq.disconnect();
656 Skip("Not supported by device.");
657 }
658 resetDeviceToDefault(daq, dev);
659 ziModule scopeModule = daq.scopeModule();
660 String path = String.Format("/{0}/scopes/0/wave", dev);
661 scopeModule.subscribe(path);
662 scopeModule.execute();
663 // The HF2 devices do not have a single event functionality.
664 if (!isDeviceFamily(daq, dev, "HF2"))
665 {
666 daq.setInt(String.Format("/{0}/scopes/0/single", dev), 1);
667 daq.setInt(String.Format("/{0}/scopes/0/trigenable", dev), 0);
668 }
669 daq.setInt(String.Format("/{0}/scopes/0/enable", dev), 1);
670
671 Lookup lookup;
672 bool allSegments = false;
673 do
674 {
675 System.Threading.Thread.Sleep(100);
676 double progress = scopeModule.progress() * 100;
677 System.Diagnostics.Trace.WriteLine(progress, "Progress");
678 lookup = scopeModule.read();
679 if (lookup.nodes.ContainsKey(path))
680 {
681 ZIScopeWave[] scopeWaves = lookup[path][0].scopeWaves;
682 UInt64 totalSegments = scopeWaves[0].header.totalSegments;
683 UInt64 segmentNumber = scopeWaves[0].header.segmentNumber;
684 allSegments = (totalSegments == 0) ||
685 (segmentNumber >= totalSegments - 1);
686 }
687 } while (!allSegments);
688 ZIScopeWave[] scopeWaves1 = lookup[path][0].scopeWaves;
689 float[,] wave = SimpleValue.getFloatVec2D(scopeWaves1[0].wave);
690 // ...
691 System.Diagnostics.Trace.WriteLine(wave.Length, "Wave Size");
692 AssertNotEqual(0, wave.Length);
693
694 scopeModule.clear(); // Release module resources. Especially important if modules are created
695 // inside a loop to prevent excessive resource consumption.
696 daq.disconnect();
697 }
698
699 // ExampleDeviceSettings instantiates a deviceSettings module and performs a save
700 // and load of device settings. The LabOne UI uses this module to save and
701 // load the device settings.
702 public static void ExampleDeviceSettings(string dev = DEFAULT_DEVICE) // Timeout(15000)
703 {
704 ziDotNET daq = connect(dev);
705 resetDeviceToDefault(daq, dev);
706 ziModule settings = daq.deviceSettings();
707 // First save the current device settings
708 settings.setString("device", dev);
709 settings.setString("command", "save");
710 settings.setString("filename", "test_settings");
711 settings.setString("path", Environment.CurrentDirectory);
712 settings.execute();
713 while (!settings.finished())
714 {
715 System.Threading.Thread.Sleep(100);
716 }
717 // Remember the current device parameter for later comparison
718 String path = String.Format("/{0}/oscs/0/freq", dev);
719 Double originalValue = daq.getDouble(path);
720 // Change the parameter
721 daq.setDouble(path, 2 * originalValue);
722 // Load device settings from file
723 settings.setString("device", dev);
724 settings.setString("command", "load");
725 settings.setString("filename", "test_settings");
726 settings.setString("path", Environment.CurrentDirectory);
727 settings.execute();
728 while (!settings.finished())
729 {
730 System.Threading.Thread.Sleep(100);
731 }
732 // Check the restored parameter
733 Double newValue = daq.getDouble(path);
734
735 AssertEqual(originalValue, newValue);
736
737 settings.clear(); // Release module resources. Especially important if modules are created
738 // inside a loop to prevent excessive resource consumption.
739 daq.disconnect();
740 }
741
742 // ExamplePidAdvisor shows the usage of the PID advisor
743 public static void ExamplePidAdvisor(string dev = DEFAULT_DEVICE) // Timeout(40000)
744 {
745 ziDotNET daq = connect(dev);
746 if (!hasOption(daq, dev, "PID"))
747 {
748 daq.disconnect();
749 Skip("Not supported by device.");
750 }
751
752 resetDeviceToDefault(daq, dev);
753
754 daq.setInt(String.Format("/{0}/demods/*/rate", dev), 0);
755 daq.setInt(String.Format("/{0}/demods/*/trigger", dev), 0);
756 daq.setInt(String.Format("/{0}/sigouts/*/enables/*", dev), 0);
757 daq.setInt(String.Format("/{0}/demods/*/enable", dev), 0);
758 daq.setInt(String.Format("/{0}/scopes/*/enable", dev), 0);
759
760 // now the settings relevant to this experiment
761 // PID configuration.
762 double target_bw = 10e3; // Target bandwidth (Hz).
763 int pid_input = 3; // PID input (3 = Demod phase).
764 int pid_input_channel = 0; // Demodulator number.
765 double setpoint = 0.0; // Phase setpoint.
766 int phase_unwrap = 1; //
767 int pid_output = 2; // PID output (2 = oscillator frequency).
768 int pid_output_channel = 0; // The index of the oscillator controlled by PID.
769 double pid_center_frequency = 500e3; // (Hz).
770 double pid_limits = 10e3; // (Hz).
771
772
773 if (!isDeviceFamily(daq, dev, "HF2"))
774 {
775 daq.setInt(String.Format("/{0}/pids/0/input", dev), pid_input);
776 daq.setInt(String.Format("/{0}/pids/0/inputchannel", dev), pid_input_channel);
777 daq.setDouble(String.Format("/{0}/pids/0/setpoint", dev), setpoint);
778 daq.setInt(String.Format("/{0}/pids/0/output", dev), pid_output);
779 daq.setInt(String.Format("/{0}/pids/0/outputchannel", dev), pid_output_channel);
780 daq.setDouble(String.Format("/{0}/pids/0/center", dev), pid_center_frequency);
781 daq.setInt(String.Format("/{0}/pids/0/enable", dev), 0);
782 daq.setInt(String.Format("/{0}/pids/0/phaseunwrap", dev), phase_unwrap);
783 daq.setDouble(String.Format("/{0}/pids/0/limitlower", dev), -pid_limits);
784 daq.setDouble(String.Format("/{0}/pids/0/limitupper", dev), pid_limits);
785 }
786 // Perform a global synchronisation between the device and the data server:
787 // Ensure that the settings have taken effect on the device before starting
788 // the pidAdvisor.
789 daq.sync();
790
791 // set up PID Advisor
792 ziModule pidAdvisor = daq.pidAdvisor();
793
794 // Turn off auto-calc on param change. Enabled
795 // auto calculation can be used to automatically
796 // update response data based on user input.
797 pidAdvisor.setInt("auto", 0);
798 pidAdvisor.setByte("device", dev);
799 pidAdvisor.setDouble("pid/targetbw", target_bw);
800
801 // PID advising mode (bit coded)
802 // bit 0: optimize/tune P
803 // bit 1: optimize/tune I
804 // bit 2: optimize/tune D
805 // Example: mode = 7: Optimize/tune PID
806 pidAdvisor.setInt("pid/mode", 7);
807
808 // PID index to use (first PID of device: 0)
809 pidAdvisor.setInt("index", 0);
810
811 // DUT model
812 // source = 1: Lowpass first order
813 // source = 2: Lowpass second order
814 // source = 3: Resonator frequency
815 // source = 4: Internal PLL
816 // source = 5: VCO
817 // source = 6: Resonator amplitude
818 pidAdvisor.setInt("dut/source", 4);
819
820 if (isDeviceFamily(daq, dev, "HF2"))
821 {
822 // Since the PLL and PID are 2 separate hardware units on the
823 // device, we need to additionally specify that the PID
824 // Advisor should model the HF2's PLL.
825 pidAdvisor.setByte("pid/type", "pll");
826 }
827
828 // IO Delay of the feedback system describing the earliest response
829 // for a step change. This parameter does not affect the shape of
830 // the DUT transfer function
831 pidAdvisor.setDouble("dut/delay", 0.0);
832
833 // Other DUT parameters (not required for the internal PLL model)
834 // pidAdvisor.setDouble('dut/gain', 1.0)
835 // pidAdvisor.setDouble('dut/bw', 1000)
836 // pidAdvisor.setDouble('dut/fcenter', 15e6)
837 // pidAdvisor.setDouble('dut/damping', 0.1)
838 // pidAdvisor.setDouble('dut/q', 10e3)
839
840 // Start values for the PID optimization. Zero
841 // values will imitate a guess. Other values can be
842 // used as hints for the optimization process.
843 pidAdvisor.setDouble("pid/p", 0);
844 pidAdvisor.setDouble("pid/i", 0);
845 pidAdvisor.setDouble("pid/d", 0);
846 pidAdvisor.setInt("calculate", 0);
847
848 // Start the module thread
849 pidAdvisor.execute();
850 System.Threading.Thread.Sleep(1000);
851
852 // Advise
853 pidAdvisor.setInt("calculate", 1);
854 System.Diagnostics.Trace.WriteLine(
855 "Starting advising. Optimization process may run up to a minute...");
856
857 var watch = System.Diagnostics.Stopwatch.StartNew();
858 while (true)
859 {
860 double progress = pidAdvisor.progress() * 100;
861 System.Diagnostics.Trace.WriteLine(progress, "Progress");
862 System.Threading.Thread.Sleep(1000);
863 Int64 calc = pidAdvisor.getInt("calculate");
864 if (calc == 0)
865 {
866 break;
867 }
868 }
869
870 watch.Stop();
871 var elapsedMs = watch.ElapsedMilliseconds;
872
873 System.Diagnostics.Trace.WriteLine(
874 String.Format("Advice took {0} s.", watch.ElapsedMilliseconds / 1000.0));
875
876 // Get the advised values
877 double p_adv = pidAdvisor.getDouble("pid/p");
878 double i_adv = pidAdvisor.getDouble("pid/i");
879 double d_adv = pidAdvisor.getDouble("pid/d");
880 double dlimittimeconstant_adv =
881 pidAdvisor.getDouble("pid/dlimittimeconstant");
882 double rate_adv = pidAdvisor.getDouble("pid/rate");
883 double bw_adv = pidAdvisor.getDouble("bw");
884
885 System.Diagnostics.Trace.WriteLine(p_adv, "P");
886 System.Diagnostics.Trace.WriteLine(i_adv, "I");
887 System.Diagnostics.Trace.WriteLine(d_adv, "D");
888 System.Diagnostics.Trace.WriteLine(dlimittimeconstant_adv, "D_tc");
889 System.Diagnostics.Trace.WriteLine(rate_adv, "rate");
890 System.Diagnostics.Trace.WriteLine(bw_adv, "bw");
891
892 // copy the values from the Advisor to the device
893 pidAdvisor.setInt("todevice", 1);
894
895 // Get all calculated parameters.
896 Lookup result = pidAdvisor.get("*");
897
898 // extract bode plot and step response
899 double[] grid = result["/bode"][0].advisorWaves[0].grid;
900 double[] x = result["/bode"][0].advisorWaves[0].x;
901 double[] y = result["/bode"][0].advisorWaves[0].y;
902 String fileName = Environment.CurrentDirectory + "/pidAdvisor.txt";
903 System.IO.StreamWriter file = new System.IO.StreamWriter(fileName);
904 for (int i = 0; i < grid.Length; ++i)
905 {
906 file.WriteLine("{0} {1} {2}", grid[i], x[i], y[i]);
907 }
908 file.Close();
909
910 AssertEqual(1.0, pidAdvisor.progress());
911 AssertNotEqual(0, grid.Length);
912
913 pidAdvisor.clear(); // Release module resources. Especially important if modules are created
914 // inside a loop to prevent excessive resource consumption.
915 daq.disconnect();
916 }
917
918 static double Sinc(double x)
919 {
920 return x != 0.0 ? Math.Sin(Math.PI * x) / (Math.PI * x) : 1.0;
921 }
922
923 // ExampleAwgModule shows the usage of the AWG module.
924 // It uses the AWG sequencer to generate a wave form.
925 // The defined waveform is applied, measured and the
926 // results are written to a file.
927 public static void ExampleAwgModule(string dev = DEFAULT_DEVICE) // Timeout(10000)
928 {
929 ziDotNET daq = connect(dev);
930 resetDeviceToDefault(daq, dev);
931 // check device type, option
932 if (!isDeviceFamily(daq, dev, "UHFAWG") &&
933 !isDeviceFamily(daq, dev, "UHFQA") &&
934 !hasOption(daq, dev, "AWG"))
935 {
936 Skip("Test does not support this device.");
937 }
938 // Create instrument configuration: disable all outputs, demods and scopes.
939 const ZIListNodes_enum flags = ZIListNodes_enum.ZI_LIST_NODES_LEAVESONLY;
940 var hasDemods = daq.listNodes(String.Format("/{0}/demods/*", dev), flags).Count > 0;
941 if (hasDemods)
942 {
943 daq.setInt(String.Format("/{0}/demods/*/enable", dev), 0);
944 daq.setInt(String.Format("/{0}/demods/*/trigger", dev), 0);
945 }
946
947 daq.setInt(String.Format("/{0}/sigouts/*/enables/*", dev), 0);
948 daq.setInt(String.Format("/{0}/scopes/*/enable", dev), 0);
949 if (hasOption(daq, dev, "IA"))
950 {
951 daq.setInt(String.Format("/{0}/imps/*/enable", dev), 0);
952 }
953 daq.sync();
954
955 // Now configure the instrument for this experiment. The following channels
956 // and indices work on all device configurations. The values below may be
957 // changed if the instrument has multiple input/output channels and/or either
958 // the Multifrequency or Multidemodulator options installed.
959 int in_channel = 0;
960 double frequency = 1e6;
961 double amp = 1.0;
962
963 daq.setDouble(String.Format("/{0}/sigouts/0/amplitudes/*", dev), 0.0);
964 daq.sync();
965
966 daq.setInt(String.Format("/{0}/sigins/0/imp50", dev), 1);
967 daq.setInt(String.Format("/{0}/sigins/0/ac", dev), 0);
968 daq.setInt(String.Format("/{0}/sigins/0/diff", dev), 0);
969 daq.setInt(String.Format("/{0}/sigins/0/range", dev), 1);
970 daq.setDouble(String.Format("/{0}/oscs/0/freq", dev), frequency);
971 daq.setInt(String.Format("/{0}/sigouts/0/on", dev), 1);
972 daq.setInt(String.Format("/{0}/sigouts/0/range", dev), 1);
973 daq.setDouble(String.Format("/{0}/awgs/0/outputs/0/amplitude", dev), amp);
974 daq.setInt(String.Format("/{0}/awgs/0/outputs/0/mode", dev), 0);
975 daq.setInt(String.Format("/{0}/awgs/0/time", dev), 0);
976 daq.setInt(String.Format("/{0}/awgs/0/userregs/0", dev), 0);
977
978 daq.sync();
979
980 // Number of points in AWG waveform
981 int AWG_N = 2000;
982
983 // Define an AWG program as a string stored in the variable awg_program, equivalent to what would
984 // be entered in the Sequence Editor window in the graphical UI.
985 // This example demonstrates four methods of definig waveforms via the API
986 // - (wave w0) loaded directly from programmatically generated CSV file wave0.csv.
987 // Waveform shape: Blackman window with negative amplitude.
988 // - (wave w1) using the waveform generation functionalities available in the AWG Sequencer language.
989 // Waveform shape: Gaussian function with positive amplitude.
990 // - (wave w2) using the vect() function and programmatic string replacement.
991 // Waveform shape: Single period of a sine wave.
992 string awg_program =
993 "const AWG_N = _c1_;\n" +
994 "wave w0 = \"wave0\";\n" +
995 "wave w1 = gauss(AWG_N, AWG_N/2, AWG_N/20);\n" +
996 "wave w2 = vect(_w2_);\n" +
997 "wave w3 = zeros(AWG_N);\n" +
998 "setTrigger(1);\n" +
999 "setTrigger(0);\n" +
1000 "playWave(w0);\n" +
1001 "playWave(w1);\n" +
1002 "playWave(w2);\n" +
1003 "playWave(w3);\n";
1004
1005 // Reference waves
1006
1007 // Define an array of values that are used to write values for wave w0 to a CSV file in the
1008 // module's data directory (Blackman windows)
1009 var waveform_0 = Enumerable.Range(0, AWG_N).Select(
1010 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))));
1011 double width = AWG_N / 20;
1012 var linspace = Enumerable.Range(0, AWG_N).Select(
1013 v => (v * AWG_N / ((double)AWG_N - 1.0d)) - AWG_N / 2);
1014 var waveform_1 = linspace.Select(
1015 v => Math.Exp(-v * v / (2 * width * width)));
1016 linspace = Enumerable.Range(0, AWG_N).Select(
1017 v => (v * 2 * Math.PI / ((double)AWG_N - 1.0d)));
1018 var waveform_2 = linspace.Select(
1019 v => Math.Sin(v));
1020 linspace = Enumerable.Range(0, AWG_N).Select(
1021 v => (v * 12 * Math.PI / ((double)AWG_N - 1.0d)) - 6 * Math.PI);
1022 var waveform_3 = linspace.Select(
1023 v => Sinc(v));
1024
1025 // concatenated reference wave
1026 double f_s = 1.8e9; // sampling rate of scope and AWG
1027 double full_scale = 0.75;
1028 var y_expected = waveform_0.Concat(waveform_1).Concat(waveform_2).Concat(waveform_3).Select(
1029 v => v * full_scale * amp).ToArray();
1030 var x_expected = Enumerable.Range(0, 4 * AWG_N).Select(v => v / f_s).ToArray();
1031
1032 // Replace placeholders in program
1033 awg_program = awg_program.Replace("_w2_", string.Join(",", waveform_2));
1034 awg_program = awg_program.Replace("_c1_", AWG_N.ToString());
1035
1036 // Create an instance of the AWG Module
1037 ziModule awgModule = daq.awgModule();
1038 awgModule.setByte("device", dev);
1039 awgModule.execute();
1040
1041
1042 // Get the modules data directory
1043 string data_dir = awgModule.getString("directory");
1044 // All CSV files within the waves directory are automatically recognized by the AWG module
1045 data_dir = data_dir + "\\awg\\waves";
1046 if (!Directory.Exists(data_dir))
1047 {
1048 // The data directory is created by the AWG module and should always exist. If this exception is raised,
1049 // something might be wrong with the file system.
1050 Fail($"AWG module wave directory {data_dir} does not exist or is not a directory");
1051 }
1052 // Save waveform data to CSV
1053 string csv_file = data_dir + "\\wave0.csv";
1054 // The following line always formats a double as "3.14" and not "3,14".
1055 var waveform_0_formatted = waveform_0.Select(v => v.ToString(CultureInfo.InvariantCulture));
1056 File.WriteAllText(@csv_file, string.Join(",", waveform_0_formatted));
1057
1058 // Transfer the AWG sequence program. Compilation starts automatically.
1059 // Note: when using an AWG program from a source file (and only then), the
1060 // compiler needs to be started explicitly with
1061 // awgModule.set("compiler/start", 1)
1062 awgModule.setByte("compiler/sourcestring", awg_program);
1063 while (awgModule.getInt("compiler/status") == -1)
1064 {
1065 System.Threading.Thread.Sleep(100);
1066 }
1067
1068 // check compiler result
1069 long status = awgModule.getInt("compiler/status");
1070 if (status == 1)
1071 {
1072 // compilation failed
1073 String message = awgModule.getString("compiler/statusstring");
1074 System.Diagnostics.Trace.WriteLine("AWG Program:");
1075 System.Diagnostics.Trace.WriteLine(awg_program);
1076 System.Diagnostics.Trace.WriteLine("---");
1077 System.Diagnostics.Trace.WriteLine(message, "Compiler message:");
1078 Fail("Compilation failed");
1079 }
1080 if (status == 0)
1081 {
1082 System.Diagnostics.Trace.WriteLine("Compilation successful with no warnings" +
1083 ", will upload the program to the instrument.");
1084 }
1085 if (status == 2)
1086 {
1087 System.Diagnostics.Trace.WriteLine("Compilation successful with warnings" +
1088 ", will upload the program to the instrument.");
1089 String message = awgModule.getString("compiler/statusstring");
1090 System.Diagnostics.Trace.WriteLine("Compiler warning:");
1091 System.Diagnostics.Trace.WriteLine(message);
1092 }
1093
1094 // wait for waveform upload to finish
1095 while (awgModule.getDouble("progress") < 1.0)
1096 {
1097 System.Diagnostics.Trace.WriteLine(
1098 awgModule.getDouble("progress"), "Progress");
1099 System.Threading.Thread.Sleep(100);
1100 }
1101
1102 // Replace w3 with waveform_3 using vector write.
1103 // Let N be the total number of waveforms and M>0 be the number of waveforms defined from CSV file. Then the index
1104 // of the waveform to be replaced is defined as following:
1105 // - 0,...,M-1 for all waveforms defined from CSV file alphabetically ordered by filename,
1106 // - M,...,N-1 in the order that the waveforms are defined in the sequencer program.
1107 // For the case of M=0, the index is defined as:
1108 // - 0,...,N-1 in the order that the waveforms are defined in the sequencer program.
1109 // Of course, for the trivial case of 1 waveform, use index=0 to replace it.
1110 // Here we replace waveform w3, the 4th waveform defined in the sequencer program. Using 0-based indexing the
1111 // index of the waveform we want to replace (w3, a vector of zeros) is 3:
1112 // Write the waveform to the memory. For the transferred array, only 16-bit unsigned integer
1113 // data (0...65536) is accepted.
1114 // For dual-channel waves, interleaving is required.
1115
1116 // The following function corresponds to ziPython utility function 'convert_awg_waveform'.
1117 Func<double, ushort> convert_awg_waveform = v => (ushort)((32767.0) * v);
1118 daq.setVector(String.Format("/{0}/awgs/0/waveform/waves/3", dev), waveform_3.Select(convert_awg_waveform).ToArray());
1119
1120 // Configure the Scope for measurement
1121 daq.setInt(
1122 String.Format("/{0}/scopes/0/channels/0/inputselect", dev), in_channel);
1123 daq.setInt(String.Format("/{0}/scopes/0/time", dev), 0);
1124 daq.setInt(String.Format("/{0}/scopes/0/enable", dev), 0);
1125 daq.setInt(String.Format("/{0}/scopes/0/length", dev), 16836);
1126
1127 // Now configure the scope's trigger to get aligned data.
1128 daq.setInt(String.Format("/{0}/scopes/0/trigenable", dev), 1);
1129 // Here we trigger on UHF signal input 1. If the instrument has the DIG Option installed we could
1130 // trigger the scope using an AWG Trigger instead (see the `setTrigger(1);` line in `awg_program` above).
1131 // 0: Signal Input 1
1132 // 192: AWG Trigger 1
1133 long trigchannel = 0;
1134 daq.setInt(String.Format("/{0}/scopes/0/trigchannel", dev), trigchannel);
1135 if (trigchannel == 0)
1136 {
1137 // Trigger on the falling edge of the negative blackman waveform `w0` from our AWG program.
1138 daq.setInt(String.Format("/{0}/scopes/0/trigslope", dev), 2);
1139 daq.setDouble(String.Format("/{0}/scopes/0/triglevel", dev), -0.600);
1140
1141 // Set hysteresis triggering threshold to avoid triggering on noise
1142 // 'trighysteresis/mode' :
1143 // 0 - absolute, use an absolute value ('scopes/0/trighysteresis/absolute')
1144 // 1 - relative, use a relative value ('scopes/0trighysteresis/relative') of the trigchannel's input range
1145 // (0.1=10%).
1146 daq.setDouble(String.Format("/{0}/scopes/0/trighysteresis/mode", dev), 0);
1147 daq.setDouble(String.Format("/{0}/scopes/0/trighysteresis/relative", dev), 0.025);
1148
1149 // Set a negative trigdelay to capture the beginning of the waveform.
1150 daq.setDouble(String.Format("/{0}/scopes/0/trigdelay", dev), -1.0e-6);
1151 }
1152 else
1153 {
1154 // Assume we're using an AWG Trigger, then the scope configuration is simple: Trigger on rising edge.
1155 daq.setInt(String.Format("/{0}/scopes/0/trigslope", dev), 1);
1156
1157 // Set trigdelay to 0.0: Start recording from when the trigger is activated.
1158 daq.setDouble(String.Format("/{0}/scopes/0/trigdelay", dev), 0.0);
1159 }
1160
1161 // the trigger reference position relative within the wave, a value of 0.5 corresponds to the center of the wave
1162 daq.setDouble(String.Format("/{0}/scopes/0/trigreference", dev), 0.0);
1163
1164 // Set the hold off time in-between triggers.
1165 daq.setDouble(String.Format("/{0}/scopes/0/trigholdoff", dev), 0.025);
1166
1167 // Set up the Scope Module.
1168 ziModule scopeModule = daq.scopeModule();
1169 scopeModule.setInt("mode", 1);
1170 scopeModule.subscribe(String.Format("/{0}/scopes/0/wave", dev));
1171 daq.setInt(String.Format("/{0}/scopes/0/single", dev), 1);
1172 scopeModule.execute();
1173 daq.setInt(String.Format("/{0}/scopes/0/enable", dev), 1);
1174 daq.sync();
1175 System.Threading.Thread.Sleep(100);
1176
1177 // Start the AWG in single-shot mode
1178 daq.setInt(String.Format("/{0}/awgs/0/single", dev), 1);
1179 daq.setInt(String.Format("/{0}/awgs/0/enable", dev), 1);
1180
1181 // Read the scope data (manual timeout of 1 second)
1182 double local_timeout = 1.0;
1183 while (scopeModule.progress() < 1.0 && local_timeout > 0.0)
1184 {
1185 System.Diagnostics.Trace.WriteLine(
1186 scopeModule.progress() * 100.0, "Scope Progress");
1187 System.Threading.Thread.Sleep(20);
1188 local_timeout -= 0.02;
1189 }
1190 string path = String.Format("/{0}/scopes/0/wave", dev);
1191 Lookup lookup = scopeModule.read();
1192 ZIScopeWave[] scopeWaves1 = lookup[path][0].scopeWaves;
1193 float[,] y_measured_in = SimpleValue.getFloatVec2D(scopeWaves1[0].wave);
1194 float[] y_measured = new float[y_measured_in.Length];
1195 for (int i = 0; i < y_measured_in.Length; ++i)
1196 {
1197 y_measured[i] = y_measured_in[0, i];
1198 }
1199
1200 var x_measured = Enumerable.Range(0, y_measured.Length).Select(
1201 v => -(long)v * scopeWaves1[0].header.dt +
1202 (scopeWaves1[0].header.timeStamp -
1203 scopeWaves1[0].header.triggerTimeStamp) / f_s
1204 ).ToArray();
1205
1206 // write signals to files
1207 String fileName = Environment.CurrentDirectory + "/awg_measured.txt";
1208 System.IO.StreamWriter file = new System.IO.StreamWriter(fileName);
1209 file.WriteLine("t [ns], measured signal [V]");
1210 for (int i = 0; i < y_measured.Length; ++i)
1211 {
1212 file.WriteLine("{0} {1}", x_measured[i] * 1e9, y_measured[i]);
1213 }
1214 file.Close();
1215
1216 fileName = Environment.CurrentDirectory + "/awg_expected.txt";
1217 file = new System.IO.StreamWriter(fileName);
1218 file.WriteLine("t [ns], expected signal [V]");
1219 for (int i = 0; i < y_expected.Length; ++i)
1220 {
1221 file.WriteLine("{0} {1}", x_expected[i] * 1e9, y_expected[i]);
1222 }
1223 file.Close();
1224
1225 // checks
1226 AssertNotEqual(0, x_measured.Length);
1227 AssertNotEqual(0, y_measured.Length);
1228
1229 // find minimal difference
1230 double dMinMax = 1e10;
1231 for (int i = 0; i < x_measured.Length - x_expected.Length; i++)
1232 {
1233 double dMax = 0;
1234 for (int k = 0; k < x_expected.Length; k++)
1235 {
1236 double d = Math.Abs(y_expected[k] - y_measured[k + i]);
1237 if (d > dMax)
1238 {
1239 dMax = d;
1240 }
1241 }
1242
1243 if (dMax < dMinMax)
1244 {
1245 dMinMax = dMax;
1246 }
1247 }
1248 Debug.Assert(dMinMax < 0.1);
1249
1250 scopeModule.clear(); // Release module resources. Especially important if modules are created
1251 // inside a loop to prevent excessive resource consumption.
1252 awgModule.clear();
1253 daq.disconnect();
1254 }
1255
1256 // ExampleAutorangingImpedance shows how to perform a manually triggered autoranging for impedance while working in manual range mode.
1257 public static void ExampleAutorangingImpedance(string dev = DEFAULT_DEVICE) // Timeout(25000)
1258 {
1259 ziDotNET daq = connect(dev);
1260 // check device type, option
1261 SkipRequiresOption(daq, dev, "IA");
1262
1263 resetDeviceToDefault(daq, dev);
1264 // Create instrument configuration: disable all outputs, demods and scopes.
1265 daq.setInt(String.Format("/{0}/demods/*/enable", dev), 0);
1266 daq.setInt(String.Format("/{0}/demods/*/trigger", dev), 0);
1267 daq.setInt(String.Format("/{0}/sigouts/*/enables/*", dev), 0);
1268 daq.setInt(String.Format("/{0}/scopes/*/enable", dev), 0);
1269 daq.setInt(String.Format("/{0}/imps/*/enable", dev), 0);
1270 daq.sync();
1271
1272 int imp = 0;
1273 long curr = daq.getInt(String.Format("/{0}/imps/{1}/current/inputselect", dev, imp));
1274 long volt = daq.getInt(String.Format("/{0}/imps/{1}/voltage/inputselect", dev, imp));
1275 double manCurrRange = 10e-3;
1276 double manVoltRange = 10e-3;
1277
1278 // Now configure the instrument for this experiment. The following channels and indices work on all devices with IA option.
1279 // The values below may be changed if the instrument has multiple IA modules.
1280 daq.setInt(String.Format("/{0}/imps/{1}/enable", dev, imp), 1);
1281 daq.setInt(String.Format("/{0}/imps/{1}/mode", dev, imp), 0);
1282 daq.setInt(String.Format("/{0}/imps/{1}/auto/output", dev, imp), 1);
1283 daq.setInt(String.Format("/{0}/imps/{1}/auto/bw", dev, imp), 1);
1284 daq.setDouble(String.Format("/{0}/imps/{1}/freq", dev, imp), 500);
1285 daq.setInt(String.Format("/{0}/imps/{1}/auto/inputrange", dev, imp), 0);
1286 daq.setDouble(String.Format("/{0}/currins/{1}/range", dev, curr), manCurrRange);
1287 daq.setDouble(String.Format("/{0}/sigins/{1}/range", dev, volt), manVoltRange);
1288 daq.sync();
1289
1290 // After setting the device in manual ranging mode we want to trigger manually a one time auto ranging to find a suitable range.
1291 // Therefore, we trigger the auto ranging for the current input as well as for the voltage input.
1292 daq.setInt(String.Format("/{0}/currins/{1}/autorange", dev, curr), 1);
1293 daq.setInt(String.Format("/{0}/sigins/{1}/autorange", dev, volt), 1);
1294
1295 // The auto ranging takes some time. We do not want to continue before the best range is found.
1296 // Therefore, we implement a loop to check if the auto ranging is finished.
1297 int count = 0;
1298 System.Threading.Thread.Sleep(100);
1299 bool finished = false;
1300 var watch = System.Diagnostics.Stopwatch.StartNew();
1301 while (!finished)
1302 {
1303 ++count;
1304 System.Threading.Thread.Sleep(500);
1305 finished = (daq.getInt(String.Format("/{0}/currins/{1}/autorange", dev, curr)) == 0 &&
1306 daq.getInt(String.Format("/{0}/sigins/{1}/autorange", dev, volt)) == 0);
1307 }
1308 watch.Stop();
1309 System.Diagnostics.Trace.WriteLine(
1310 String.Format("Auto ranging finished after {0} s.", watch.ElapsedMilliseconds / 1e3));
1311
1312 double autoCurrRange = daq.getDouble(String.Format("/{0}/currins/{1}/range", dev, curr));
1313 double autoVoltRange = daq.getDouble(String.Format("/{0}/sigins/{1}/range", dev, volt));
1314 System.Diagnostics.Trace.WriteLine(
1315 String.Format("Current range changed from {0} A to {1} A.", manCurrRange, autoCurrRange));
1316 System.Diagnostics.Trace.WriteLine(
1317 String.Format("Voltage range changed from {0} A to {1} A.", manVoltRange, autoVoltRange));
1318 Debug.Assert(count > 1);
1319 }
1320
1321 // ExampleDataAcquisition uses the new data acquisition module to record data
1322 // and writes the result in to a file.
1323 public static void ExampleDataAcquisition(string dev = DEFAULT_DEVICE) // Timeout(20000)
1324 {
1325 ziDotNET daq = connect(dev);
1326
1327 SkipForDeviceFamilyAndOption(daq, dev, "MF", "MD");
1328 SkipForDeviceFamilyAndOption(daq, dev, "HF2", "MD");
1329 SkipForDeviceFamily(daq, dev, "HDAWG");
1330
1331 resetDeviceToDefault(daq, dev);
1332 daq.setInt(String.Format("/{0}/demods/0/oscselect", dev), 0);
1333 daq.setInt(String.Format("/{0}/demods/1/oscselect", dev), 1);
1334 daq.setDouble(String.Format("/{0}/oscs/0/freq", dev), 2e6);
1335 daq.setDouble(String.Format("/{0}/oscs/1/freq", dev), 2.0001e6);
1336 daq.setInt(String.Format("/{0}/sigouts/0/enables/*", dev), 0);
1337 daq.setInt(String.Format("/{0}/sigouts/0/enables/0", dev), 1);
1338 daq.setInt(String.Format("/{0}/sigouts/0/enables/1", dev), 1);
1339 daq.setInt(String.Format("/{0}/sigouts/0/on", dev), 1);
1340 daq.setDouble(String.Format("/{0}/sigouts/0/amplitudes/0", dev), 0.2);
1341 daq.setDouble(String.Format("/{0}/sigouts/0/amplitudes/1", dev), 0.2);
1342 ziModule trigger = daq.dataAcquisitionModule();
1343 trigger.setInt("grid/mode", 4);
1344 double demodRate = daq.getDouble(String.Format("/{0}/demods/0/rate", dev));
1345 double duration = trigger.getDouble("duration");
1346 Int64 sampleCount = System.Convert.ToInt64(demodRate * duration);
1347 trigger.setInt("grid/cols", sampleCount);
1348 trigger.setByte("device", dev);
1349 trigger.setInt("type", 1);
1350 trigger.setDouble("level", 0.1);
1351 trigger.setDouble("hysteresis", 0.01);
1352 trigger.setDouble("bandwidth", 0.0);
1353 String path = String.Format("/{0}/demods/0/sample.r", dev);
1354 trigger.subscribe(path);
1355 String triggerPath = String.Format("/{0}/demods/0/sample.R", dev);
1356 trigger.setByte("triggernode", triggerPath);
1357 trigger.execute();
1358 while (!trigger.finished())
1359 {
1360 System.Threading.Thread.Sleep(100);
1361 double progress = trigger.progress() * 100;
1362 System.Diagnostics.Trace.WriteLine(progress, "Progress");
1363 }
1364 Lookup lookup = trigger.read();
1365 ZIDoubleData[] demodSample = lookup[path][0].doubleData;
1366 String fileName = Environment.CurrentDirectory + "/dataacquisition.txt";
1367 System.IO.StreamWriter file = new System.IO.StreamWriter(fileName);
1368 ZIChunkHeader header = lookup[path][0].header;
1369 // Raw system time is the number of microseconds since linux epoch
1370 file.WriteLine("Raw System Time: {0}", header.systemTime);
1371 // Use the utility function ziSystemTimeToDateTime to convert to DateTime of .NET
1372 file.WriteLine("Converted System Time: {0}", ziUtility.ziSystemTimeToDateTime(lookup[path][0].header.systemTime));
1373 file.WriteLine("Created Timestamp: {0}", header.createdTimeStamp);
1374 file.WriteLine("Changed Timestamp: {0}", header.changedTimeStamp);
1375 file.WriteLine("Flags: {0}", header.flags);
1376 file.WriteLine("Name: {0}", header.name);
1377 file.WriteLine("Status: {0}", header.status);
1378 file.WriteLine("Group Index: {0}", header.groupIndex);
1379 file.WriteLine("Color: {0}", header.color);
1380 file.WriteLine("Active Row: {0}", header.activeRow);
1381 file.WriteLine("Trigger Number: {0}", header.triggerNumber);
1382 file.WriteLine("Grid Rows: {0}", header.gridRows);
1383 file.WriteLine("Grid Cols: {0}", header.gridCols);
1384 file.WriteLine("Grid Mode: {0}", header.gridMode);
1385 file.WriteLine("Grid Operation: {0}", header.gridOperation);
1386 file.WriteLine("Grid Direction: {0}", header.gridDirection);
1387 file.WriteLine("Grid Repetitions: {0}", header.gridRepetitions);
1388 file.WriteLine("Grid Col Delta: {0}", header.gridColDelta);
1389 file.WriteLine("Grid Col Offset: {0}", header.gridColOffset);
1390 file.WriteLine("Bandwidth: {0}", header.bandwidth);
1391 file.WriteLine("Center: {0}", header.center);
1392 file.WriteLine("NENBW: {0}", header.nenbw);
1393 for (int i = 0; i < demodSample.Length; ++i)
1394 {
1395 file.WriteLine("{0}", demodSample[i].value);
1396 }
1397 file.Close();
1398
1399 AssertEqual(1, trigger.progress());
1400 AssertNotEqual(0, demodSample.Length);
1401
1402 trigger.clear(); // Release module resources. Especially important if modules are created
1403 // inside a loop to prevent excessive resource consumption.
1404 daq.disconnect();
1405 }
1406
1407 // ExampleMultiDeviceDataAcquisition
1408 //
1409 // Run the example: Capture demodulator data from two devices using the Data Acquisition module.
1410 // The devices are first synchronized using the MultiDeviceSync Module.
1411 //
1412 // Hardware configuration:
1413 // The cabling of the instruments must follow the MDS cabling depicted in
1414 // the MDS tab of LabOne.
1415 // Additionally, Signal Out 1 of the leader device is split into Signal In 1 of the leader and follower.
1416 //
1417 // ATTENTION: test ignored because it requires special device setup
1418 public void SKIP_MULTIDEVICE_ExampleMultiDeviceDataAcquisition() // Timeout(25000)
1419 {
1420 String[] device_ids = { "dev3133", "dev3144" };
1421
1422 ziDotNET daq = new ziDotNET();
1423 daq.init("localhost", 8004, zhinst.ZIAPIVersion_enum.ZI_API_VERSION_6);
1424 apiServerVersionCheck(daq);
1425 daq.connectDevice(device_ids[0], "1gbe", "");
1426 daq.connectDevice(device_ids[1], "1gbe", "");
1427
1428 // Create instrument configuration: disable all outputs, demods and scopes.
1429 foreach (String dev in device_ids)
1430 {
1431 daq.setInt(String.Format("/{0}/demods/*/enable", dev), 0);
1432 daq.setInt(String.Format("/{0}/demods/*/trigger", dev), 0);
1433 daq.setInt(String.Format("/{0}/sigouts/*/enables/*", dev), 0);
1434 daq.setInt(String.Format("/{0}/scopes/*/enable", dev), 0);
1435 daq.setInt(String.Format("/{0}/imps/*/enable", dev), 0);
1436 daq.sync();
1437 }
1438
1439 System.Diagnostics.Trace.WriteLine("Synchronizing devices " + String.Join(",", device_ids) + "...\n");
1440
1441 ziModule mds = daq.multiDeviceSyncModule();
1442 mds.setInt("start", 0);
1443 mds.setInt("group", 0);
1444 mds.execute();
1445 mds.setString("devices", String.Join(",", device_ids));
1446 mds.setInt("start", 1);
1447
1448 // Wait for MDS to complete
1449 double local_timeout = 20.0;
1450 long status = 0;
1451 while (status != 2 && local_timeout > 0.0)
1452 {
1453 status = mds.getInt("status");
1454 System.Threading.Thread.Sleep(100);
1455 local_timeout -= 0.1;
1456 }
1457
1458 if (status != 2)
1459 {
1460 System.Diagnostics.Trace.WriteLine("Error during synchronization.\n");
1461 Fail();
1462 }
1463 System.Diagnostics.Trace.WriteLine("Devices successfully synchronized.");
1464
1465 // Device settings
1466 int demod_c = 0; // demod channel, for paths on the device
1467 int out_c = 0; // signal output channel
1468 int out_mixer_c = 0;
1469 int in_c = 0; // signal input channel
1470 int osc_c = 0; // oscillator
1471
1472 double time_constant = 1.0e-3; // [s]
1473 double demod_rate = 10e3; // [Sa/s]
1474 int filter_order = 8;
1475 double osc_freq = 1e3; // [Hz]
1476 double out_amp = 0.600; // [V]
1477
1478 // Device settings
1479 foreach (String dev in device_ids)
1480 {
1481 daq.setDouble(String.Format("/{0}/demods/{1}/phaseshift", dev, demod_c), 0);
1482 daq.setInt(String.Format("/{0}/demods/{1}/order", dev, demod_c), filter_order);
1483 daq.setDouble(String.Format("/{0}/demods/{1}/rate", dev, demod_c), demod_rate);
1484 daq.setInt(String.Format("/{0}/demods/{1}/harmonic", dev, demod_c), 1);
1485 daq.setInt(String.Format("/{0}/demods/{1}/enable", dev, demod_c), 1);
1486 daq.setInt(String.Format("/{0}/demods/{1}/oscselect", dev, demod_c), osc_c);
1487 daq.setInt(String.Format("/{0}/demods/{1}/adcselect", dev, demod_c), in_c);
1488 daq.setDouble(String.Format("/{0}/demods/{1}/timeconstant", dev, demod_c), time_constant);
1489 daq.setDouble(String.Format("/{0}/oscs/{1}/freq", dev, osc_c), osc_freq);
1490 daq.setInt(String.Format("/{0}/sigins/{1}/imp50", dev, in_c), 1);
1491 daq.setInt(String.Format("/{0}/sigins/{1}/ac", dev, in_c), 0);
1492 daq.setDouble(String.Format("/{0}/sigins/{1}/range", dev, in_c), out_amp / 2);
1493
1494 }
1495 // settings on leader
1496 daq.setInt(String.Format("/{0}/sigouts/{1}/on", device_ids[0], out_c), 1);
1497 daq.setDouble(String.Format("/{0}/sigouts/{1}/range", device_ids[0], out_c), 1);
1498 daq.setDouble(String.Format("/{0}/sigouts/{1}/amplitudes/{2}", device_ids[0], out_c, out_mixer_c), out_amp);
1499 daq.setDouble(String.Format("/{0}/sigouts/{1}/enables/{2}", device_ids[0], out_c, out_mixer_c), 0);
1500
1501 // Synchronization
1502 daq.sync();
1503
1504 // measuring the transient state of demodulator filters using DAQ module
1505
1506 // DAQ module
1507 // Create a Data Acquisition Module instance, the return argument is a handle to the module
1508 ziModule daqMod = daq.dataAcquisitionModule();
1509 // Configure the Data Acquisition Module
1510 // Device on which trigger will be performed
1511 daqMod.setString("device", device_ids[0]);
1512 // The number of triggers to capture (if not running in endless mode).
1513 daqMod.setInt("count", 1);
1514 daqMod.setInt("endless", 0);
1515 // 'grid/mode' - Specify the interpolation method of
1516 // the returned data samples.
1517 //
1518 // 1 = Nearest. If the interval between samples on the grid does not match
1519 // the interval between samples sent from the device exactly, the nearest
1520 // sample (in time) is taken.
1521 //
1522 // 2 = Linear interpolation. If the interval between samples on the grid does
1523 // not match the interval between samples sent from the device exactly,
1524 // linear interpolation is performed between the two neighbouring
1525 // samples.
1526 //
1527 // 4 = Exact. The subscribed signal with the highest sampling rate (as sent
1528 // from the device) defines the interval between samples on the DAQ
1529 // Module's grid. If multiple signals are subscribed, these are
1530 // interpolated onto the grid (defined by the signal with the highest
1531 // rate, "highest_rate"). In this mode, duration is
1532 // read-only and is defined as num_cols/highest_rate.
1533 int grid_mode = 2;
1534 daqMod.setInt("grid/mode", grid_mode);
1535 // type:
1536 // NO_TRIGGER = 0
1537 // EDGE_TRIGGER = 1
1538 // DIGITAL_TRIGGER = 2
1539 // PULSE_TRIGGER = 3
1540 // TRACKING_TRIGGER = 4
1541 // HW_TRIGGER = 6
1542 // TRACKING_PULSE_TRIGGER = 7
1543 // EVENT_COUNT_TRIGGER = 8
1544 daqMod.setInt("type", 1);
1545 // triggernode, specify the triggernode to trigger on.
1546 // SAMPLE.X = Demodulator X value
1547 // SAMPLE.Y = Demodulator Y value
1548 // SAMPLE.R = Demodulator Magnitude
1549 // SAMPLE.THETA = Demodulator Phase
1550 // SAMPLE.AUXIN0 = Auxilliary input 1 value
1551 // SAMPLE.AUXIN1 = Auxilliary input 2 value
1552 // SAMPLE.DIO = Digital I/O value
1553 string triggernode = String.Format("/{0}/demods/{1}/sample.r", device_ids[0], demod_c);
1554 daqMod.setString("triggernode", triggernode);
1555 // edge:
1556 // POS_EDGE = 1
1557 // NEG_EDGE = 2
1558 // BOTH_EDGE = 3
1559 daqMod.setInt("edge", 1);
1560 demod_rate = daq.getDouble(String.Format("/{0}/demods/{1}/rate", device_ids[0], demod_c));
1561 // Exact mode: To preserve our desired trigger duration, we have to set
1562 // the number of grid columns to exactly match.
1563 double trigger_duration = time_constant * 30;
1564 int sample_count = Convert.ToInt32(demod_rate * trigger_duration);
1565 daqMod.setInt("grid/cols", sample_count);
1566 // The length of each trigger to record (in seconds).
1567 daqMod.setDouble("duration", trigger_duration);
1568 daqMod.setDouble("delay", -trigger_duration / 4);
1569 // Do not return overlapped trigger events.
1570 daqMod.setDouble("holdoff/time", 0);
1571 daqMod.setDouble("holdoff/count", 0);
1572 daqMod.setDouble("level", out_amp / 6);
1573 // The hysterisis is effectively a second criteria (if non-zero) for triggering
1574 // and makes triggering more robust in noisy signals. When the trigger `level`
1575 // is violated, then the signal must return beneath (for positive trigger edge)
1576 // the hysteresis value in order to trigger.
1577 daqMod.setDouble("hysteresis", 0.01);
1578 // synchronizing the settings
1579 daq.sync();
1580
1581 // Recording
1582
1583 // Subscribe to the demodulators
1584 daqMod.unsubscribe("*");
1585 foreach (String dev in device_ids)
1586 {
1587 string node = String.Format("/{0}/demods/{1}/sample.r", dev, demod_c);
1588 daqMod.subscribe(node);
1589 }
1590
1591 // Execute the module
1592 daqMod.execute();
1593 // Send a trigger
1594 daq.setDouble(String.Format("/{0}/sigouts/{1}/enables/{2}", device_ids[0], out_c, out_mixer_c), 1);
1595 while (!daqMod.finished())
1596 {
1597 System.Threading.Thread.Sleep(1000);
1598 System.Diagnostics.Trace.WriteLine(String.Format("Progress {0}", daqMod.progress()));
1599 }
1600
1601 // Read the result
1602 Lookup result = daqMod.read();
1603
1604 // Turn off the trigger
1605 daq.setDouble(String.Format("/{0}/sigouts/{1}/enables/{2}", device_ids[0], out_c, out_mixer_c), 0);
1606 // Finish the DAQ module
1607 daqMod.finish();
1608
1609 daqMod.clear(); // Release module resources. Especially important if modules are created
1610 // inside a loop to prevent excessive resource consumption.
1611
1612 // Stop the MDS module, release memory and resources
1613 mds.clear();
1614
1615 // Extracting and saving the data
1616 double mClockbase = daq.getDouble(String.Format("/{0}/clockbase", device_ids[0]));
1617
1618 List<ZIDoubleData[]> data = new List<ZIDoubleData[]>();
1619 foreach (String dev in device_ids)
1620 {
1621 string node = string.Format("/{0}/demods/{1}/sample.r", dev, demod_c);
1622 data.Add(result[node][0].doubleData);
1623 }
1624
1625 String fileName = Environment.CurrentDirectory + "/mds_dataacquisition.txt";
1626 System.IO.StreamWriter file = new System.IO.StreamWriter(fileName);
1627
1628 for (int i = 0; i < data[0].Length; ++i)
1629 {
1630 file.WriteLine("{0},{1},{2}", (data[0][i].timeStamp - data[0][0].timeStamp) / mClockbase,
1631 data[0][i].value, data[1][i].value);
1632 }
1633 file.Close();
1634
1635 daq.disconnect();
1636 }
1637 }
1638
1639}