pps_example.c Source File

Reference Documentation

Platform
Napatech SmartNIC
Content Type
Reference Information
Capture Software Version
Link™ Capture Software 12.15
Napatech Software Suite: examples/pps/pps_example.c Source File
pps_example.c
Go to the documentation of this file.
1 /*
2  *
3  * Copyright 2025 Napatech A/S. All Rights Reserved.
4  *
5  * 1. Copying, modification, and distribution of this file, or executable
6  * versions of this file, is governed by the terms of the Napatech Software
7  * license agreement under which this file was made available. If you do not
8  * agree to the terms of the license do not install, copy, access or
9  * otherwise use this file.
10  *
11  * 2. Under the Napatech Software license agreement you are granted a
12  * limited, non-exclusive, non-assignable, copyright license to copy, modify
13  * and distribute this file in conjunction with Napatech SmartNIC's and
14  * similar hardware manufactured or supplied by Napatech A/S.
15  *
16  * 3. The full Napatech Software License Agreement is included in this
17  * distribution, please see "NA-0009 Software License Agreement.pdf"
18  *
19  * 4. Redistributions of source code must retain this copyright notice,
20  * list of conditions and the following disclaimer.
21  *
22  * THIS SOFTWARE IS PROVIDED "AS IS" WITHOUT ANY WARRANTIES, EXPRESS OR
23  * IMPLIED, AND NAPATECH DISCLAIMS ALL IMPLIED WARRANTIES INCLUDING ANY
24  * IMPLIED WARRANTY OF TITLE, MERCHANTABILITY, NONINFRINGEMENT, OR OF
25  * FITNESS FOR A PARTICULAR PURPOSE. TO THE EXTENT NOT PROHIBITED BY
26  * APPLICABLE LAW, IN NO EVENT SHALL NAPATECH BE LIABLE FOR PERSONAL INJURY,
27  * OR ANY INCIDENTAL, SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES WHATSOEVER,
28  * INCLUDING, WITHOUT LIMITATION, DAMAGES FOR LOSS OF PROFITS, CORRUPTION OR
29  * LOSS OF DATA, FAILURE TO TRANSMIT OR RECEIVE ANY DATA OR INFORMATION,
30  * BUSINESS INTERRUPTION OR ANY OTHER COMMERCIAL DAMAGES OR LOSSES, ARISING
31  * OUT OF OR RELATED TO YOUR USE OR INABILITY TO USE NAPATECH SOFTWARE OR
32  * SERVICES OR ANY THIRD PARTY SOFTWARE OR APPLICATIONS IN CONJUNCTION WITH
33  * THE NAPATECH SOFTWARE OR SERVICES, HOWEVER CAUSED, REGARDLESS OF THE THEORY
34  * OF LIABILITY (CONTRACT, TORT OR OTHERWISE) AND EVEN IF NAPATECH HAS BEEN
35  * ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. SOME JURISDICTIONS DO NOT ALLOW
36  * THE EXCLUSION OR LIMITATION OF LIABILITY FOR PERSONAL INJURY, OR OF
37  * INCIDENTAL OR CONSEQUENTIAL DAMAGES, SO THIS LIMITATION MAY NOT APPLY TO YOU.
38  *
39  *
40 
41  */
42 
43 /**
44  * @example pps/pps_example.c
45  * @section pps_example_description Description
46  *
47  * This source file is an example of how to use the PPS functionality.
48  *
49  * The following NTAPI functions are used:
50  * - @ref NT_Init()
51  * - @ref NT_ConfigOpen()
52  * - @ref NT_ConfigWrite()
53  * - @ref NT_ConfigRead()
54  * - @ref NT_ConfigClose()
55  * - @ref NT_ExplainError()
56  *
57  * <hr>
58  * @section pps_example_prerequisites Prerequisites
59  * A working system is needed.
60  *
61  * @section pps_example_flow Program flow
62  * @{
63  * The following is required to perform read and write operations on
64  * the @ref ConfigStream "configuration stream":
65  * - \#include/nt.h - Applications/Tools only need to include @ref
66  * nt.h to obtain prototypes, macros etc. from NTAPI.
67  * - @ref NT_Init(@ref NTAPI_VERSION) - Initialize the NTAPI
68  * library. @ref NTAPI_VERSION is a define that describes the version
69  * of the API described in the header files included by @ref
70  * nt.h. NT_Init() will ask the NTAPI library to convert return data
71  * to the @ref NTAPI_VERSION if possible. This will ensure that
72  * applications can run on NTAPI libraries of newer versions.
73  * - @ref NT_ConfigOpen() - Open a configuration stream.
74  * - @ref NT_ConfigRead() - Read configuration.
75  * - @ref NT_ConfigWrite() - Write configuration.
76  * - @ref NT_ConfigClose() - Close the stream when terminating.
77  *
78  *<hr>
79  * @}
80  */
81 
82 // Include this in order to access the Napatech API
83 #include <nt.h>
84 
85 #if defined(WIN32) || defined (WIN64)
86  #include <winsock2.h>
87  #include <sys/timeb.h>
88 #else
89  #include <signal.h>
90  #include <sys/time.h>
91  #include <unistd.h> // sleep()
92  #include <stdatomic.h>
93 #endif
94 
95 #include <stdlib.h>
96 #include <time.h>
97 
98 #define MAX_TS_CARDS 4
99 
100 typedef struct TimeSync_s{
101  unsigned timeSyncEnabled;
103 } TimeSync_t;
104 
106 
107 
108 #if defined(WIN32) || defined (WIN64)
109 static volatile int appRunning = 1; // The application will run as long as appRunning == 1
110 #else
111 static atomic_int appRunning = 1; // The application will run as long as appRunning == 1
112 #endif
113 
114 
115 #if defined(WIN32) || defined (WIN64)
116 
117 #if defined(_MSC_VER)
118  struct timezone {
119  int tz_minuteswest; /* minutes west of Greenwich */
120  int tz_dsttime; /* type of DST correction */
121  };
122 #endif // _MSC_VER
123 
124  int gettimeofday(struct timeval *tv, struct timezone *tz);
125 
126 #endif
127 
128 
129 #define NT_CONST64(a) a##ULL
130 #define UNIX_EPOCH_OFFSET_MAGIC 116444736000000000ULL
131 
132 /**
133  * 32bit struct timeval. Used because our HW is only 32bit PCAP compatible
134  */
135 struct nttimeval
136 {
137  uint32_t tv_sec;
138  uint32_t tv_usec;
139 };
140 
141 // Forward declarations
142 int PpsGetOsTime(uint64_t *osTime);
143 int PrintTimestamp(uint64_t timeValue);
144 int PpsPrintStatusAndTime(int i, uint64_t timeSyncPpsSampled, int64_t timeSyncTimeSkew);
145 
146 
147 /*
148  * The function called when user is pressing CTRL-C
149  */
150 #if defined(WIN32) || defined (WIN64)
151 static BOOL WINAPI StopApplication(int sig)
152 {
153  (void) sig;
154  appRunning = 0;
155  return TRUE;
156 }
157 #else
158 static void StopApplication(int sig)
159 {
160  if (sig == SIGINT)
161  appRunning = 0;
162 }
163 #endif
164 
165 
166 /*
167  * This function returns the OS time in seconds.
168  * ----------------------------------------------------------------------*/
169 int /* Ret Result. */
170 PpsGetOsTime(uint64_t *osTime) /* Seconds since 1. jan 1970. */
171 {
172  struct timeval timeofday;
173  struct timezone timeZone;
174  int result;
175 
176  result = gettimeofday(&timeofday, &timeZone);
177 
178  *osTime = (uint64_t)(timeofday.tv_sec & 0xFFFFFFFF);
179  return result;
180 }
181 
182 
183 
184 int PrintTimestamp(uint64_t timeValue)
185 {
186  time_t timeT;
187  struct tm *ptm;
188  char wday[7][4]={"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"};
189  char wmon[12][4]={"Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"};
190 
191  timeT = (time_t)(timeValue/1000000000ULL);
192  ptm = gmtime(&timeT);
193  if (ptm == NULL) {
194  printf("Time is invalid");
195  } else {
196  printf("UTC Time : %s %d-%s-%d %02d:%02d:%02d.%09lld",
197  wday[ptm->tm_wday], ptm->tm_mday, wmon[ptm->tm_mon], ptm->tm_year+1900,
198  ptm->tm_hour, ptm->tm_min, ptm->tm_sec, ((unsigned long long)timeValue%100000000));
199  }
200  return 0;
201 }
202 
203 
204 int PpsPrintStatusAndTime(int i, uint64_t timeSyncPpsSampled, int64_t timeSyncTimeSkew)
205 {
206  /* Check if more than 100 milli seconds off and conclude loss of SYNC */
207  if ((timeSyncTimeSkew < -100000000) ||
208  (timeSyncTimeSkew > 100000000)) {
209  printf("%d: PPS not in SYNC: ", i);
210  } else {
211  /* Check if more than 1 milli second off and conclude trying to SYNC */
212  if ((timeSyncTimeSkew < -1000000) ||
213  (timeSyncTimeSkew > 1000000)) {
214  printf("%d PPS SYNCING: ", i);
215  } else {
216  /* Less than 1 milli second off, conclude it's in SYNC */
217  printf("%d PPS in SYNC: ", i);
218  }
219  }
220 
221  PrintTimestamp(timeSyncPpsSampled);
222  printf(" ClockSkew : %13lld nano seconds \n", (long long)(timeSyncTimeSkew));
223  return 0;
224 }
225 
226 
227 int main(void)
228 {
229  int i;
230  int32_t status;
231  uint32_t timeout = 1000;
232  uint64_t osTime;
233  uint64_t helpTime;
234  uint64_t printTime=0;
235  NtInfoStream_t hInfoStream; // Info stream handle
236  NtEventStream_t hEventStream;
237  NtConfigStream_t hConfigStream;
238  NtInfo_t hInfo; // Info handle
239  NtEvent_t hEvent;
240  NtConfig_t configInfo;
241  char errorBuffer[NT_ERRBUF_SIZE];
242  int numAdapters=0;
243  int found=0;
244 
245  // Register ctrl+c handler so we are able to stop again
246 #if defined(WIN32) || defined (WIN64)
247  SetConsoleCtrlHandler((PHANDLER_ROUTINE)StopApplication, TRUE);
248 #else
249  struct sigaction newaction; // Ctrl+c signal handler container
250  memset(&newaction, 0, sizeof(newaction));
251  newaction.sa_handler = StopApplication;
252  if (sigaction(SIGINT, &newaction, NULL) < 0) {
253  fprintf(stderr, "Failed to register SIGINT sigaction.\n");
254  exit(EXIT_FAILURE);
255  }
256 #endif
257 
258  printf("Running the PPS example.\n\n");
259 
260  if ((status = NT_Init(NTAPI_VERSION)) != NT_SUCCESS) {
261  // Get the status code as text
262  NT_ExplainError(status, errorBuffer, sizeof(errorBuffer));
263  fprintf(stderr, "NT_Init() failed: %s\n", errorBuffer);
264  return -1;
265  }
266 
267  /* Init local variables */
268  for (i=0;i<MAX_TS_CARDS;i++) {
270  }
271 
272  printf("Opening info stream\n");
273  /* Open the info stream */
274  if ((status = NT_InfoOpen(&hInfoStream, "InfoPps")) != NT_SUCCESS) {
275  // Get the status code as text
276  NT_ExplainError(status, errorBuffer, sizeof(errorBuffer));
277  fprintf(stderr, "NT_ConfigRead() failed: %s\n", errorBuffer);
278  return -1;
279  }
280  printf("Info stream opened successfully\n");
281 
282  /* Read number of adapters */
284  if ((status = NT_InfoRead(hInfoStream, &hInfo)) != 0) {
285  // Get the status code as text
286  NT_ExplainError(status, errorBuffer, sizeof(errorBuffer));
287  fprintf(stderr, "NT_ConfigRead() failed: %s\n", errorBuffer);
288  return -1;
289  }
290 
291  numAdapters = hInfo.u.system.data.numAdapters;
292 
293  printf("Found %d adapters\n", numAdapters);
294 
295  for (i=0;i<numAdapters;i++) {
296  /* Read timesync info */
298  hInfo.u.timeSync_v4.adapterNo = (uint8_t)i;
299  if ((status = NT_InfoRead(hInfoStream, &hInfo)) != NT_SUCCESS) {
300  // Get the status code as text
301  NT_ExplainError(status, errorBuffer, sizeof(errorBuffer));
302  fprintf(stderr, "Failed to read timesync info: %s\n", errorBuffer);
303  return -1;
304  }
305 
306  /* Print timesync info */
307  printf("Adapter %d:\n", i);
308  printf(" TimeSync reference priority: %d, %d, %d\n", hInfo.u.timeSync_v4.data.tsRefPrio[0],
309  hInfo.u.timeSync_v4.data.tsRefPrio[1], hInfo.u.timeSync_v4.data.tsRefPrio[2]);
310  printf(" TimeSync reference: %d\n", hInfo.u.timeSync_v4.data.timeRef);
311  printf(" TimeSync connector Ext1: %d\n", hInfo.u.timeSync_v4.data.timeSyncConnectorExt1);
312  printf(" TimeSync connector Int1: %d\n", hInfo.u.timeSync_v4.data.timeSyncConnectorInt1);
313  printf(" TimeSync connector Int2: %d\n", hInfo.u.timeSync_v4.data.timeSyncConnectorInt2);
315  printf(" TimeSync Time Jump (s): %d\n", hInfo.u.timeSync_v4.data.timeSyncTimeJumpThreshold);
316  } else {
317  printf(" TimeSync Time Jump Allowed : %d\n", hInfo.u.timeSync_v4.data.timeSyncHardReset);
318  }
319  printf(" TimeSync Time Offset (ns): %d\n\n", hInfo.u.timeSync_v4.data.timeSyncTimeOffset);
320 
321  // Check if the adapter is configured for PPS
328  found = 1;
329  TS_os[i].timeSyncEnabled = 1;
330 
331  // Read the Timestamp type from the adapter
333  hInfo.u.adapter_v7.adapterNo = (uint8_t) i;
334  if ((status = NT_InfoRead(hInfoStream, &hInfo)) != NT_SUCCESS) {
335  // Get the status code as text
336  NT_ExplainError(status, errorBuffer, sizeof(errorBuffer));
337  fprintf(stderr, "NT_InfoRead() failed: %s\n", errorBuffer);
338  return -1;
339  }
340 
341  // Check that Timestamp type is set to Native UNIX
343  fprintf(stderr, "Timestamp should be Native UNIX, please configure in the Ntservice.ini file.\n");
344  return -1;
345  }
346  }
347  }
348 
349  if (found == 0) {
350  fprintf(stderr, "No PPS Enabled adapters found! Please configure PPS in ntservice.ini\n");
351  return -1;
352  }
353 
354  /* Open the event stream */
355  if ((status = NT_EventOpen(&hEventStream, "PpsEvent", NT_EVENT_SOURCE_TIMESYNC)) != NT_SUCCESS) {
356  // Get the status code as text
357  NT_ExplainError(status, errorBuffer, sizeof(errorBuffer));
358  fprintf(stderr, "NT_EventOpen() failed: %s\n", errorBuffer);
359  return -1;
360  }
361 
362  // Open the config stream
363  if ((status = NT_ConfigOpen(&hConfigStream, "CONFIG"))) {
364  // Get the status code as text
365  NT_ExplainError(status, errorBuffer, sizeof(errorBuffer));
366  fprintf(stderr, "NT_ConfigOpen() failed: %s\n", errorBuffer);
367  return -1;
368  }
369 
370  // Enable PPS
371  for (i=0;i<numAdapters;i++) {
372  if (TS_os[i].timeSyncEnabled == 1) {
374  configInfo.u.timesyncWrite.adapter = (uint8_t) i;
376 
377  /* Set Initial PPS time */
378  PpsGetOsTime(&osTime);
379  configInfo.u.timesyncWrite.data.refTime = osTime;
380 
381  if ((status = NT_ConfigWrite(hConfigStream, &configInfo)) != NT_SUCCESS) {
382  // Get the status code as text
383  NT_ExplainError(status, errorBuffer, sizeof(errorBuffer));
384  fprintf(stderr, "NT_ConfigWrite() failed: %s\n", errorBuffer);
385  return -1;
386  }
387  }
388  }
389 
390  fprintf(stderr, "PPS example starting in 5 seconds\n");
391 #if defined(WIN32) || defined (WIN64)
392  Sleep(5000); // sleep 5000 milliseconds = 5 second
393 #else
394  sleep(5); // sleep 5 sec
395 #endif
396 
397 #if defined(WIN32) || defined (WIN64)
398  /* Clear screen */
399  if (system("cls")==0) {
400  // Avoid compiler warning
401  }
402 #else
403  /* Clear screen */
404  if (system("clear")==0) {
405  // Avoid compiler warning
406  }
407 #endif
408  printf("PPS Example running (press CTRL-C to exit)\n");
409 
410  while (appRunning == 1) {
411 
412  status = NT_EventRead(hEventStream, &hEvent, timeout);
413  if (status == NT_STATUS_TIMEOUT) {
414  // Timeout try again
415  } else {
416  if (status != NT_SUCCESS) {
417  // Get the status code as text
418  NT_ExplainError(status, errorBuffer, sizeof(errorBuffer));
419  fprintf(stderr, "NT_EventRead() failed: %s\n", errorBuffer);
420  return -1;
421  }
422 
423  // Handle event
424  if (hEvent.type == NT_EVENT_SOURCE_TIMESYNC) {
426  // Send PPS Reference Time
428  configInfo.u.timesyncWrite.adapter = hEvent.u.timeSyncEvent.adapter;
430 
431  /* Set PPS Reference Time */
432  PpsGetOsTime(&osTime);
433  configInfo.u.timesyncWrite.data.refTime = osTime;
434 
435  if ((status = NT_ConfigWrite(hConfigStream, &configInfo)) != NT_SUCCESS) {
436  // Get the status code as text
437  NT_ExplainError(status, errorBuffer, sizeof(errorBuffer));
438  fprintf(stderr, "NT_ConfigWrite() failed: %s\n", errorBuffer);
439  return -1;
440  }
441  }
442  }
443  }
444  PpsGetOsTime(&helpTime);
445  if (helpTime != printTime) {
446  printTime=helpTime;
447 
448 #if defined(WIN32) || defined (WIN64)
449  /* Clear screen */
450  if (system("cls")==0) {
451  // Avoid compiler warning
452  }
453 #else
454  /* Clear screen */
455  if (system("clear")==0) {
456  // Avoid compiler warning
457  }
458 #endif
459 
460  /* Read timesync info */
461  for (i=0;i<numAdapters;i++) {
462  if (TS_os[i].timeSyncEnabled == 1) {
463  /* Read timesync info */
465  hInfo.u.timeSync_v4.adapterNo = (uint8_t) i;
466  if ((status = NT_InfoRead(hInfoStream, &hInfo)) != NT_SUCCESS) {
467  // Get the status code as text
468  NT_ExplainError(status, errorBuffer, sizeof(errorBuffer));
469  fprintf(stderr, "Failed to read timesync info: %s\n", errorBuffer);
470  return -1;
471  }
475  }
476  printf("PPS signal lost on adapter %d\n", i);
477  } else
481  }
482  printf("PPS signal found on adapter %d\n", i);
483  /* Print timesync info */
485  }
486  }
487  }
488  }
489  }
490 
491  // Disable PPS
492  for (i=0;i<numAdapters;i++) {
493  if (TS_os[i].timeSyncEnabled == 1) {
495  configInfo.u.timesyncWrite.adapter = (uint8_t)i;
497  configInfo.u.timesyncWrite.data.refTime = 0;
498 
499  if ((status = NT_ConfigWrite(hConfigStream, &configInfo)) != NT_SUCCESS) {
500  // Get the status code as text
501  NT_ExplainError(status, errorBuffer, sizeof(errorBuffer));
502  fprintf(stderr, "NT_ConfigWrite() failed: %s\n", errorBuffer);
503  return -1;
504  }
505  }
506  }
507 
508  // Close the configuration stream
509  if ((status = NT_ConfigClose(hConfigStream)) != NT_SUCCESS) {
510  // Get the status code as text
511  NT_ExplainError(status, errorBuffer, sizeof(errorBuffer));
512  fprintf(stderr, "NT_ConfigClose() failed: %s\n", errorBuffer);
513  return -1;
514  }
515 
516  // Close the event stream
517  if ((status = NT_EventClose(hEventStream)) != NT_SUCCESS) {
518  // Get the status code as text
519  NT_ExplainError(status, errorBuffer, sizeof(errorBuffer));
520  fprintf(stderr, "NT_EventClose() failed: %s\n", errorBuffer);
521  return -1;
522  }
523 
524  /* Close the info stream */
525  if ((status = NT_InfoClose(hInfoStream)) != NT_SUCCESS) {
526  // Get the status code as text
527  NT_ExplainError(status, errorBuffer, sizeof(errorBuffer));
528  fprintf(stderr, "NT_InfoClose() failed: %s\n", errorBuffer);
529  return -1;
530  }
531 
532  printf("Streams closed\n");
533  return 0;
534 }