Example PIDAdvisor#

  1// Copyright [2017] Zurich Instruments AG
  2#include <stdlib.h>
  3
  4#include "ziAPI.h"
  5#include "ziUtils.hpp"
  6
  7
  8int main()
  9{
 10  // Define the device address of the device to run the example on.
 11  const char* deviceAddress = ziUtilsGetEnv("LABONE_DEVICE", "dev3123");
 12  printf("ENV LABONE_DEVICE=%s\n", deviceAddress);
 13
 14  // Address of the data server.
 15  const char* dataServer = ziUtilsGetEnv("LABONE_SERVER", "localhost");
 16
 17  // Port of the data server.
 18  const uint16_t port = 8004;
 19
 20  // Over which interface the connection to the
 21  // device should be established. Can be either
 22  // - 1: "1GbE" - Ethernet
 23  // - 2: "USB" - USB
 24  // - 3: "PCIe" - PCIe
 25  const char* deviceInterface = "1GbE";
 26
 27  ZIConnection conn = nullptr;
 28
 29  if (isError(ziAPIInit(&conn)))
 30  {
 31    return 1;
 32  }
 33
 34  ziAPISetDebugLevel(0);
 35  ziAPIWriteDebugLog(0, "Logging enabled.");
 36
 37  try
 38  {
 39    checkError(
 40        ziAPIConnectEx(conn, dataServer, port, ZI_API_VERSION_6, nullptr));
 41    ziApiServerVersionCheck(conn);
 42    checkError(
 43        ziAPIConnectDevice(conn, deviceAddress, deviceInterface, nullptr));
 44    // The required parameters that will be cauclated by the PID Advisor.
 45    ZIDoubleData pAdvisor = 0.0;
 46    ZIDoubleData iAdvisor = 0.0;
 47    ZIDoubleData dAdvisor = 0.0;
 48    ZIDoubleData dlimittimeconstantAdvisor = 0.0;
 49    ZIDoubleData rateAdvisor = 0.0;
 50    ZIDoubleData bandwidthAdvisor = 0.0;
 51
 52    ZIModuleHandle pidAdvisor;
 53    checkError(ziAPIModCreate(conn, &pidAdvisor, "pidAdvisor"));
 54    printf("Module created, handle=%" PRIu64 ".\n", pidAdvisor);
 55
 56    int pidIndex = 0;
 57    checkError(ziAPIModSetString(conn, pidAdvisor, "device", deviceAddress));
 58
 59    // Turn off auto-calc on param change. Enabled auto calculation can be used
 60    // to automatically update response data based on user input.
 61    checkError(ziAPIModSetIntegerData(conn, pidAdvisor, "auto", 0));
 62    checkError(ziAPIModSetDoubleData(conn, pidAdvisor, "pid/targetbw", 100e3));
 63    // PID advising mode (bit coded)
 64    // bit 0: optimize/tune P
 65    // bit 1: optimize/tune I
 66    // bit 2: optimize/tune D
 67    // Example: mode = 7: Optimize/tune PID
 68    checkError(ziAPIModSetIntegerData(conn, pidAdvisor, "pid/mode", 7));
 69    checkError(ziAPIModSetIntegerData(conn, pidAdvisor, "index", pidIndex));
 70    // DUT model
 71    // source = 1: Lowpass first order
 72    // source = 2: Lowpass second order
 73    // source = 3: Resonator frequency
 74    // source = 4: Internal PLL
 75    // source = 5: VCO
 76    // source = 6: Resonator amplitude
 77    checkError(ziAPIModSetIntegerData(conn, pidAdvisor, "dut/source", 4));
 78    // Note, if on HF2: Since the PLL and PID are 2 separate hardware units on
 79    // the device, we need to additionally specify that the PID Advisor should
 80    // model the HF2's PLL. checkError(ziAPIModSetString(conn, pidAdvisor,
 81    // "pid/type", "pll"));
 82    checkError(ziAPIModSetDoubleData(conn, pidAdvisor, "dut/delay", 0.0));
 83    // Other DUT parameters (not required for the internal PLL model)
 84    // checkError(ziAPIModSetDoubleData(conn, pidAdvisor, "dut/gain", 1.0));
 85    // checkError(ziAPIModSetDoubleData(conn, pidAdvisor, "dut/bw", 1000));
 86    // checkError(ziAPIModSetDoubleData(conn, pidAdvisor, "dut/fcenter", 15e6));
 87    // checkError(ziAPIModSetDoubleData(conn, pidAdvisor, "dut/damping", 0.1));
 88    // checkError(ziAPIModSetDoubleData(conn, pidAdvisor, "dut/q", 10e3));
 89
 90    // Start values for the PID optimization. Zero values will imitate a
 91    // guess. Other values can be used as hints for the optimization process.
 92    checkError(ziAPIModSetDoubleData(conn, pidAdvisor, "pid/p", 0));
 93    checkError(ziAPIModSetDoubleData(conn, pidAdvisor, "pid/i", 0));
 94    checkError(ziAPIModSetDoubleData(conn, pidAdvisor, "pid/d", 0));
 95    checkError(ziAPIModSetIntegerData(conn, pidAdvisor, "calculate", 0));
 96
 97    printf("Finished writing pidAdvisor parameters.\n");
 98
 99    // Start the module thread.
100    printf("Executing...\n");
101    checkError(ziAPIModExecute(conn, pidAdvisor));
102
103    printf(
104        "Starting advising. Optimization process may run up to a minute...\n");
105    checkError(ziAPIModSetIntegerData(conn, pidAdvisor, "calculate", 1));
106
107    ZIIntegerData pidAdvisorCalculate = 1;
108    while (pidAdvisorCalculate == 1)
109    {
110      sleep(500);  // [ms]
111      checkError(ziAPIModGetInteger(
112          conn, pidAdvisor, "calculate", &pidAdvisorCalculate));
113    }
114    printf("Advising finished.\n");
115
116    ZIModuleEventPtr ev = NULL;
117    char path[1024];
118    ZIValueType_enum valueType;
119    uint64_t chunks;
120    checkError(ziAPIModRead(conn, pidAdvisor, ""));
121    ZIResult_enum res =
122        ziAPIModNextNode(conn, pidAdvisor, path, 1024, &valueType, &chunks);
123    while (res == ZI_INFO_SUCCESS)
124    {
125      printf("Got node: %s with %" PRIu64 " chunks of type ", path, chunks);
126      printf("%d\n", valueType);
127      for (uint64_t chunk = 0; chunk < chunks; ++chunk)
128      {
129        checkError(ziAPIModGetChunk(conn, pidAdvisor, chunk, &ev));
130        printf(
131            "Data of chunk %" PRIu64 ": type %d, header time %" PRId64 "\n",
132            chunk,
133            ev->value->valueType,
134            ev->header->systemTime);
135        printf(
136            "  - Chunk header: created=%" PRId64 ", changed=%" PRId64 "\n",
137            ev->header->createdTimeStamp,
138            ev->header->changedTimeStamp);
139        switch (ev->value->valueType)
140        {
141        case ZI_VALUE_TYPE_NONE:  // 0
142          printf("  - Type 'None' can't be decoded and should not appear in "
143                 "the lookup!\n");
144          break;
145        case ZI_VALUE_TYPE_DOUBLE_DATA:  // 1
146        {
147          ZIEvent& e = *ev->value;
148          printf("  - %d samples (double):\n", e.count);
149          for (size_t i = 0; i < e.count; ++i)
150          {
151            printf(
152                "    - sample %" PRsize_t "d = %f\n", i, e.value.doubleData[i]);
153          }
154          if (strcmp(path, "/pid/p") == 0)
155          {
156            pAdvisor = e.value.doubleData[0];
157            printf("Got value of /pid/p: %f.\n", pAdvisor);
158          }
159          else if (strcmp(path, "/pid/i") == 0)
160          {
161            iAdvisor = e.value.doubleData[0];
162            printf("Got value of /pid/i: %f.\n", iAdvisor);
163          }
164          else if (strcmp(path, "/pid/d") == 0)
165          {
166            dAdvisor = e.value.doubleData[0];
167            printf("Got value of /pid/d: %f.\n", dAdvisor);
168            break;
169          }
170          else if (strcmp(path, "/pid/dlimittimeconstant") == 0)
171          {
172            dlimittimeconstantAdvisor = e.value.doubleData[0];
173            printf(
174                "Got value of /pid/dlimittimeconstant: %f.\n",
175                dlimittimeconstantAdvisor);
176            break;
177          }
178          else if (strcmp(path, "/pid/rate") == 0)
179          {
180            rateAdvisor = e.value.doubleData[0];
181            printf("Got value of /pid/rate: %f.\n", rateAdvisor);
182            break;
183          }
184          else if (strcmp(path, "/bw") == 0)
185          {
186            bandwidthAdvisor = e.value.doubleData[0];
187            printf("Got value of /pid/bw: %f.\n", bandwidthAdvisor);
188            break;
189          }
190        }
191        break;
192        case ZI_VALUE_TYPE_INTEGER_DATA:  // 2
193        {
194          ZIEvent& e = *ev->value;
195          printf("  - %d samples (integer):\n", e.count);
196          for (size_t i = 0; i < e.count; ++i)
197          {
198            printf(
199                "    - sample %" PRsize_t "d = %" PRId64 "\n",
200                i,
201                e.value.integerData[i]);
202          }
203        }
204        break;
205        case ZI_VALUE_TYPE_BYTE_ARRAY:  // 7
206        {
207          ZIByteArray& v = *ev->value->value.byteArray;
208          printf("  - length = %u\n", v.length);
209          char* str = reinterpret_cast<char*>(malloc(v.length + 1));
210          strncpy(str, reinterpret_cast<char*>(v.bytes), v.length);
211          str[v.length] = '\0';
212          printf("  - value = '%s'\n", str);
213          free(str);
214        }
215        break;
216        case ZI_VALUE_TYPE_ADVISOR_WAVE:  // 66
217        {
218          ZIAdvisorWave& v = *ev->value->value.advisorWave;
219          printf("  - timeStamp = %" PRIu64 "\n", v.timeStamp);
220          printf("  - sampleCount = %" PRIu64 "\n", v.header.sampleCount);
221          printf("  - flags = %d\n", v.header.flags);
222          // Sample format
223          // Bode = 0, Step = 1, Impulse = 2.
224          switch (v.header.sampleFormat)
225          {
226          case 0:
227            printf(
228                "    sampleFormat : %d (Bode plot data).\n",
229                v.header.sampleFormat);
230            break;
231          case 1:
232            printf(
233                "    sampleFormat : %d (Step plot data).\n",
234                v.header.sampleFormat);
235            break;
236          case 2:
237            printf(
238                "    sampleFormat : %d (Impulse plot data).\n",
239                v.header.sampleFormat);
240            break;
241          default:
242            printf("  - Unknown sample format!\n");
243          }
244
245          printf("  - sampleFormat = %d\n", v.header.sampleFormat);
246          for (size_t i = 0; i < v.header.sampleCount; ++i)
247          {
248            printf("  - sample %" PRsize_t "d:\n", i);
249            ZIAdvisorSample& s = v.data.data[i];
250            // Note: 1. For Bode plot data, the complex result, bodeComplexData
251            // = x + j*y; the magnitude and phase must be calculated from this
252            // complex value. The unit of grid is Hz.
253            // 2. For Step plot data, x provides the value of the step response
254            // (y is always 0). The unit of grid is seconds.
255            printf("    - grid = %f\n", s.grid);
256            printf("    - x = %f\n", s.x);
257            printf("    - y = %f\n", s.y);
258          }
259        }
260        break;
261        default:
262          printf("  - Unexpected data type!\n");
263        }
264      }
265      printf("\n");
266      res = ziAPIModNextNode(conn, pidAdvisor, path, 1024, &valueType, &chunks);
267    }
268    if (res != ZI_WARNING_NOTFOUND)
269    {
270      checkError(res);
271    }
272
273    // Now we copy the values from the PID Advisor to the PID and enable the
274    // PID. Note on HF2, this sets the respective nodes in the PLLS branch, for
275    // other device classes it sets the nodes in the PIDS branch (on other
276    // device classes the PLL is implemented in the device's PID).
277    printf("Copying PID Advisor values to PID %d.\n", pidIndex);
278    checkError(ziAPIModSetIntegerData(conn, pidAdvisor, "todevice", 1));
279    checkError(ziAPIModFinish(conn, pidAdvisor));
280
281    checkError(ziAPIModEventDeallocate(conn, pidAdvisor, ev));
282
283    // Release module resources. Especially important if modules are created
284    // inside a loop to prevent excessive resource consumption.
285    checkError(ziAPIModClear(conn, pidAdvisor));
286
287    snprintf(path, sizeof(path), "/%s/pids/%d/rate", deviceAddress, pidIndex);
288    // checkError(ziAPISetValueD(conn, path, rateAdvisor));
289    printf("Enabling PID %d.\n", pidIndex);
290    snprintf(path, sizeof(path), "/%s/pids/%d/enable", deviceAddress, pidIndex);
291    checkError(ziAPISetValueI(conn, path, 1));
292    ziAPIDisconnect(conn);
293  }
294  catch (std::runtime_error& e)
295  {
296    char extErrorMessage[1024] = "";
297    ziAPIGetLastError(conn, extErrorMessage, 1024);
298    fprintf(stderr, "Error: %s\ndetails: %s\n", e.what(), extErrorMessage);
299    return 1;
300  }
301
302  ziAPIDestroy(conn);
303  return 0;
304}