Examples#

The following Examples are available in the Examples.cs file:

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}