pps/pps_example.c

Reference Documentation

product_line_custom
IntelĀ® PAC
Napatech SmartNIC
category
Reference Information
Napatech Software Suite: pps/pps_example.c
pps/pps_example.c

Description

This source file is an example of how to use the PPS functionality.

The following NTAPI functions are used:


Prerequisites

A working system is needed.

Program flow

The following is required to perform read and write operations on the configuration stream:

  • #include/nt.h - Applications/Tools only need to include nt.h to obtain prototypes, macros etc. from NTAPI.
  • NT_Init(NTAPI_VERSION) - Initialize the NTAPI library. NTAPI_VERSION is a define that describes the version of the API described in the header files included by nt.h. NT_Init() will ask the NTAPI library to convert return data to the NTAPI_VERSION if possible. This will ensure that applications can run on NTAPI libraries of newer versions.
  • NT_ConfigOpen() - Open a configuration stream.
  • NT_ConfigRead() - Read configuration.
  • NT_ConfigWrite() - Write configuration.
  • NT_ConfigClose() - Close the stream when terminating.

/*
*
* Copyright 2020 Napatech A/S. All Rights Reserved.
*
* 1. Copying, modification, and distribution of this file, or executable
* versions of this file, is governed by the terms of the Napatech Software
* license agreement under which this file was made available. If you do not
* agree to the terms of the license do not install, copy, access or
* otherwise use this file.
*
* 2. Under the Napatech Software license agreement you are granted a
* limited, non-exclusive, non-assignable, copyright license to copy, modify
* and distribute this file in conjunction with Napatech SmartNIC's and
* similar hardware manufactured or supplied by Napatech A/S.
*
* 3. The full Napatech Software license agreement is included in this
* distribution, please see "NP-0405 Napatech Software license
* agreement.pdf"
*
* 4. Redistributions of source code must retain this copyright notice,
* list of conditions and the following disclaimer.
*
* THIS SOFTWARE IS PROVIDED "AS IS" WITHOUT ANY WARRANTIES, EXPRESS OR
* IMPLIED, AND NAPATECH DISCLAIMS ALL IMPLIED WARRANTIES INCLUDING ANY
* IMPLIED WARRANTY OF TITLE, MERCHANTABILITY, NONINFRINGEMENT, OR OF
* FITNESS FOR A PARTICULAR PURPOSE. TO THE EXTENT NOT PROHIBITED BY
* APPLICABLE LAW, IN NO EVENT SHALL NAPATECH BE LIABLE FOR PERSONAL INJURY,
* OR ANY INCIDENTAL, SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES WHATSOEVER,
* INCLUDING, WITHOUT LIMITATION, DAMAGES FOR LOSS OF PROFITS, CORRUPTION OR
* LOSS OF DATA, FAILURE TO TRANSMIT OR RECEIVE ANY DATA OR INFORMATION,
* BUSINESS INTERRUPTION OR ANY OTHER COMMERCIAL DAMAGES OR LOSSES, ARISING
* OUT OF OR RELATED TO YOUR USE OR INABILITY TO USE NAPATECH SOFTWARE OR
* SERVICES OR ANY THIRD PARTY SOFTWARE OR APPLICATIONS IN CONJUNCTION WITH
* THE NAPATECH SOFTWARE OR SERVICES, HOWEVER CAUSED, REGARDLESS OF THE THEORY
* OF LIABILITY (CONTRACT, TORT OR OTHERWISE) AND EVEN IF NAPATECH HAS BEEN
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. SOME JURISDICTIONS DO NOT ALLOW
* THE EXCLUSION OR LIMITATION OF LIABILITY FOR PERSONAL INJURY, OR OF
* INCIDENTAL OR CONSEQUENTIAL DAMAGES, SO THIS LIMITATION MAY NOT APPLY TO YOU.
*
*
*/
/**
* @example pps/pps_example.c
* @section pps_example_description Description
*
* This source file is an example of how to use the PPS functionality.
*
* The following NTAPI functions are used:
* - @ref NT_Init()
* - @ref NT_ConfigOpen()
* - @ref NT_ConfigWrite()
* - @ref NT_ConfigRead()
* - @ref NT_ConfigClose()
* - @ref NT_ExplainError()
*
* <hr>
* @section pps_example_prerequisites Prerequisites
* A working system is needed.
*
* @section pps_example_flow Program flow
* @{
* The following is required to perform read and write operations on
* the @ref ConfigStream "configuration stream":
* - \#include/nt.h - Applications/Tools only need to include @ref
* nt.h to obtain prototypes, macros etc. from NTAPI.
* - @ref NT_Init(@ref NTAPI_VERSION) - Initialize the NTAPI
* library. @ref NTAPI_VERSION is a define that describes the version
* of the API described in the header files included by @ref
* nt.h. NT_Init() will ask the NTAPI library to convert return data
* to the @ref NTAPI_VERSION if possible. This will ensure that
* applications can run on NTAPI libraries of newer versions.
* - @ref NT_ConfigOpen() - Open a configuration stream.
* - @ref NT_ConfigRead() - Read configuration.
* - @ref NT_ConfigWrite() - Write configuration.
* - @ref NT_ConfigClose() - Close the stream when terminating.
*
*<hr>
* @}
*/
#if defined(__linux__) || defined(__FreeBSD__)
#include <signal.h>
#include <sys/time.h>
#include <unistd.h> // sleep()
#include <time.h>
#elif defined(WIN32) || defined (WIN64)
#include <winsock2.h>
#include <time.h>
#include <sys/timeb.h>
#endif
#include "nt.h"
#define MAX_TS_CARDS 4
typedef struct TimeSync_s{
unsigned timeSyncEnabled;
int stopApplication=0; /* Flag to signal that application should stop. */
#if defined(WIN32) || defined (WIN64)
struct timezone {
int tz_minuteswest; /* minutes west of Greenwich */
int tz_dsttime; /* type of DST correction */
};
int gettimeofday(struct timeval *tv, struct timezone *tz)
{
struct __timeb64 timebuffer;
_ftime64_s(&timebuffer);
tv->tv_sec = (long)timebuffer.time;
tv->tv_usec = timebuffer.millitm * 1000;
return 0;
}
#endif
#define NT_CONST64(a) a##ULL
#define UNIX_EPOCH_OFFSET_MAGIC 116444736000000000ULL
/**
* 32bit struct timeval. Used because our HW is only 32bit PCAP compatible
*/
struct nttimeval
{
uint32_t tv_sec;
uint32_t tv_usec;
};
// Forward declarations
int PpsGetOsTime(uint64_t *osTime);
int PrintTimestamp(uint64_t timeValue);
int PpsPrintStatusAndTime(int i, uint64_t timeSyncPpsSampled, int64_t timeSyncTimeSkew);
/*
* The function called when user is pressing CTRL-C
*/
#if defined(WIN32) || defined (WIN64)
static BOOL WINAPI StopApplication(int sig)
#else
static void StopApplication(int sig)
#endif
{
printf("Stopping application (%d)\n", sig);
#ifdef WIN32
return TRUE;
#else
if (sig == SIGINT)
#endif
}
/* This function returns the OS time in seconds.
* ----------------------------------------------------------------------*/
int /* Ret Result. */
PpsGetOsTime(uint64_t *osTime) /* Seconds since 1. jan 1970. */
{
struct timeval timeofday;
struct timezone timeZone;
int result;
result = gettimeofday(&timeofday, &timeZone);
*osTime = (uint64_t)(timeofday.tv_sec & 0xFFFFFFFF);
return result;
}
int PrintTimestamp(uint64_t timeValue)
{
time_t timeT;
struct tm *ptm;
char wday[7][4]={"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"};
char wmon[12][4]={"Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"};
timeT = (time_t)(timeValue/1000000000ULL);
ptm = gmtime(&timeT);
if (ptm == NULL) {
printf("Time is invalid");
} else {
printf("UTC Time : %s %d-%s-%d %02d:%02d:%02d.%09lld",
wday[ptm->tm_wday], ptm->tm_mday, wmon[ptm->tm_mon], ptm->tm_year+1900,
ptm->tm_hour, ptm->tm_min, ptm->tm_sec, ((unsigned long long)timeValue%100000000));
}
return 0;
}
int PpsPrintStatusAndTime(int i, uint64_t timeSyncPpsSampled, int64_t timeSyncTimeSkew)
{
/* Check if more than 100 milli seconds off and conclude loss of SYNC */
if ((timeSyncTimeSkew < -100000000) ||
(timeSyncTimeSkew > 100000000)) {
printf("%d: PPS not in SYNC: ", i);
} else {
/* Check if more than 1 milli second off and conclude trying to SYNC */
if ((timeSyncTimeSkew < -1000000) ||
(timeSyncTimeSkew > 1000000)) {
printf("%d PPS SYNCING: ", i);
} else {
/* Less than 1 milli second off, conclude it's in SYNC */
printf("%d PPS in SYNC: ", i);
}
}
PrintTimestamp(timeSyncPpsSampled);
printf(" ClockSkew : %13lld nano seconds \n", (long long)(timeSyncTimeSkew));
return 0;
}
int main(void)
{
int i;
int32_t status;
int timeout = 1000;
uint64_t osTime;
uint64_t helpTime;
uint64_t printTime=0;
NtInfoStream_t hInfoStream; // Info stream handle
NtEventStream_t hEventStream;
NtConfigStream_t hConfigStream;
NtInfo_t hInfo; // Info handle
NtEvent_t hEvent;
NtConfig_t configInfo;
int numAdapters=0;
int found=0;
// Set up ctrl+c handler
#if defined(WIN32) || defined (WIN64)
SetConsoleCtrlHandler((PHANDLER_ROUTINE)StopApplication, TRUE);
#else
struct sigaction newaction; // Ctrl+c handle
memset(&newaction, 0, sizeof(newaction));
newaction.sa_handler = StopApplication;
if (sigaction(SIGINT, &newaction, NULL) < 0) {
fprintf(stderr, "Failed to register sigaction.\n");
return -1;
}
#endif
printf("Running the PPS example.\n\n");
if ((status = NT_Init(NTAPI_VERSION)) != NT_SUCCESS) {
// Get the status code as text
NT_ExplainError(status, errorBuffer, sizeof(errorBuffer));
fprintf(stderr, "NT_ConfigRead() failed: %s\n", errorBuffer);
return -1;
}
/* Init local variables */
for (i=0;i<MAX_TS_CARDS;i++) {
}
printf("Opening info stream\n");
/* Open the info stream */
if ((status = NT_InfoOpen(&hInfoStream, "InfoPps")) != NT_SUCCESS) {
// Get the status code as text
NT_ExplainError(status, errorBuffer, sizeof(errorBuffer));
fprintf(stderr, "NT_ConfigRead() failed: %s\n", errorBuffer);
return -1;
}
printf("Info stream opened successfully\n");
/* Read number of adapters */
if((status = NT_InfoRead(hInfoStream, &hInfo)) != 0) {
// Get the status code as text
NT_ExplainError(status, errorBuffer, sizeof(errorBuffer));
fprintf(stderr, "NT_ConfigRead() failed: %s\n", errorBuffer);
return -1;
}
numAdapters = hInfo.u.system.data.numAdapters;
printf("Found %d adapters\n", numAdapters);
for (i=0;i<numAdapters;i++) {
/* Read timesync info */
hInfo.u.adapter_v6.adapterNo = (uint8_t) i;
if ((status = NT_InfoRead(hInfoStream, &hInfo)) != NT_SUCCESS) {
fprintf(stderr, "Failed to read timesync info.\n");
return -1;
}
/* Print timesync info */
printf("Adapter %d:\n", i);
printf(" TimeSync reference priority: %d, %d, %d\n", hInfo.u.timeSync_v4.data.tsRefPrio[0],
printf(" TimeSync reference: %d\n", hInfo.u.timeSync_v4.data.timeRef);
printf(" TimeSync connector Ext1: %d\n", hInfo.u.timeSync_v4.data.timeSyncConnectorExt1);
printf(" TimeSync connector Int1: %d\n", hInfo.u.timeSync_v4.data.timeSyncConnectorInt1);
printf(" TimeSync connector Int2: %d\n", hInfo.u.timeSync_v4.data.timeSyncConnectorInt2);
printf(" TimeSync Time Jump (s): %d\n", hInfo.u.timeSync_v4.data.timeSyncTimeJumpThreshold);
} else {
printf(" TimeSync Time Jump Allowed : %d\n", hInfo.u.timeSync_v4.data.timeSyncHardReset);
}
printf(" TimeSync Time Offset (ns): %d\n\n", hInfo.u.timeSync_v4.data.timeSyncTimeOffset);
// Check if the adapter is configured for PPS
found = 1;
TS_os[i].timeSyncEnabled = 1;
// Read the Timestamp type from the adapter
hInfo.u.adapter_v6.adapterNo = (uint8_t) i;
if((status = NT_InfoRead(hInfoStream, &hInfo)) != NT_SUCCESS) {
// Get the status code as text
NT_ExplainError(status, errorBuffer, sizeof(errorBuffer));
fprintf(stderr, "NT_InfoRead() failed: %s\n", errorBuffer);
return -1;
}
// Check that Timestamp type is set to Native UNIX
fprintf(stderr, "Timestamp should be Native UNIX, please configure in the Ntservice.ini file.\n");
return -1;
}
}
}
if (found == 0) {
fprintf(stderr, "No PPS Enabled adapters found! Please configure PPS in ntservice.ini\n");
return -1;
}
/* Open the event stream */
if ((status = NT_EventOpen(&hEventStream, "PpsEvent", NT_EVENT_SOURCE_TIMESYNC)) != NT_SUCCESS) {
// Get the status code as text
NT_ExplainError(status, errorBuffer, sizeof(errorBuffer));
fprintf(stderr, "NT_EventOpen() failed: %s\n", errorBuffer);
return -1;
}
// Open the config stream
if((status = NT_ConfigOpen(&hConfigStream, "CONFIG"))) {
// Get the status code as text
NT_ExplainError(status, errorBuffer, sizeof(errorBuffer));
fprintf(stderr, "NT_ConfigOpen() failed: %s\n", errorBuffer);
return -1;
}
// Enable PPS
for (i=0;i<numAdapters;i++) {
if (TS_os[i].timeSyncEnabled == 1) {
configInfo.u.timesyncWrite.adapter = (uint8_t) i;
/* Set Initial PPS time */
PpsGetOsTime(&osTime);
configInfo.u.timesyncWrite.data.refTime = osTime;
if ((status = NT_ConfigWrite(hConfigStream, &configInfo)) != NT_SUCCESS) {
// Get the status code as text
NT_ExplainError(status, errorBuffer, sizeof(errorBuffer));
fprintf(stderr, "NT_ConfigWrite() failed: %s\n", errorBuffer);
return -1;
}
}
}
fprintf(stderr, "PPS example starting in 5 seconds\n");
// Sleep 5 sec
#if defined(__linux__) || defined(__FreeBSD__)
sleep(5);
#elif defined(WIN32) || defined (WIN64)
Sleep(5000); // sleep 5000 milliseconds = 5 second
#endif
#if defined(WIN32) || defined (WIN64)
/* Clear screen */
if(system("cls")==0) {
// Avoid compiler warning
}
#else
/* Clear screen */
if(system("clear")==0) {
// Avoid compiler warning
}
#endif
printf("PPS Example running (press CTRL-C to exit)\n");
while (stopApplication == 0) {
status = NT_EventRead(hEventStream, &hEvent, timeout);
if (status == NT_STATUS_TIMEOUT) {
// Timeout try again
} else {
if (status != NT_SUCCESS) {
// Get the status code as text
NT_ExplainError(status, errorBuffer, sizeof(errorBuffer));
fprintf(stderr, "NT_EventRead() failed: %s\n", errorBuffer);
return -1;
}
// Handle event
if (hEvent.type == NT_EVENT_SOURCE_TIMESYNC) {
// Send PPS Reference Time
/* Set PPS Reference Time */
PpsGetOsTime(&osTime);
configInfo.u.timesyncWrite.data.refTime = osTime;
if ((status = NT_ConfigWrite(hConfigStream, &configInfo)) != NT_SUCCESS) {
// Get the status code as text
NT_ExplainError(status, errorBuffer, sizeof(errorBuffer));
fprintf(stderr, "NT_ConfigWrite() failed: %s\n", errorBuffer);
return -1;
}
}
}
}
PpsGetOsTime(&helpTime);
if (helpTime != printTime) {
printTime=helpTime;
#if defined(WIN32) || defined (WIN64)
/* Clear screen */
if(system("cls")==0) {
// Avoid compiler warning
}
#else
/* Clear screen */
if(system("clear")==0) {
// Avoid compiler warning
}
#endif
/* Read timesync info */
for (i=0;i<numAdapters;i++) {
if (TS_os[i].timeSyncEnabled == 1) {
/* Read timesync info */
hInfo.u.timeSync_v4.adapterNo = (uint8_t) i;
if ((status = NT_InfoRead(hInfoStream, &hInfo)) != NT_SUCCESS) {
fprintf(stderr, "Failed to read timesync info.\n");
return -1;
}
}
printf("PPS signal lost on adapter %d\n", i);
} else
}
printf("PPS signal found on adapter %d\n", i);
/* Print timesync info */
}
}
}
}
}
// Disable PPS
for (i=0;i<numAdapters;i++) {
if (TS_os[i].timeSyncEnabled == 1) {
configInfo.u.timesyncWrite.adapter = (uint8_t)i;
configInfo.u.timesyncWrite.data.refTime = 0;
if ((status = NT_ConfigWrite(hConfigStream, &configInfo)) != NT_SUCCESS) {
// Get the status code as text
NT_ExplainError(status, errorBuffer, sizeof(errorBuffer));
fprintf(stderr, "NT_ConfigWrite() failed: %s\n", errorBuffer);
return -1;
}
}
}
// Close the configuration stream
if ((status = NT_ConfigClose(hConfigStream)) != NT_SUCCESS) {
// Get the status code as text
NT_ExplainError(status, errorBuffer, sizeof(errorBuffer));
fprintf(stderr, "NT_ConfigClose() failed: %s\n", errorBuffer);
return -1;
}
// Close the event stream
if ((status = NT_EventClose(hEventStream)) != NT_SUCCESS) {
// Get the status code as text
NT_ExplainError(status, errorBuffer, sizeof(errorBuffer));
fprintf(stderr, "NT_EventClose() failed: %s\n", errorBuffer);
return -1;
}
/* Close the info stream */
if ((status = NT_InfoClose(hInfoStream)) != NT_SUCCESS) {
// Get the status code as text
NT_ExplainError(status, errorBuffer, sizeof(errorBuffer));
fprintf(stderr, "NT_InfoClose() failed: %s\n", errorBuffer);
return -1;
}
printf("Streams closed\n");
return 0;
}