CIsimpleTwoVFG.c

/*****************************************************************************

	CIsimpleTwoVFG.c	Source for BitFlow CI lib simple 2 VFG example program

	Apr 8,		2013	CIW/SJT

	© Copyright 2013, BitFlow, Inc. All rights reserved.

	Tabstops are 4

	$Author: steve $

	$Date: 2020/10/02 23:30:13 $

	$Id: CIsimpleTwoVFG.c,v 1.8 2020/10/02 23:30:13 steve Exp $

*****************************************************************************/

/*==========================================================================*/
/*
**	For access to command line display.
*/
#include	<stdio.h>
#include	<stdarg.h>
#include	<string.h>
#include	<stdlib.h>
/*
**	For checking for keypress
*/
#include	<sys/time.h>
#include	<sys/types.h>
#include	<unistd.h>
/*
**	For access to BitFlow camera interface library.
*/
#include	"BFciLib.h"
/*==========================================================================*/
static int sExitAns = 0;		/* program exit code */
static tCIp sCIp0 = NULL;		/* device0 open token */
static tCIp sCIp1 = NULL;		/* device1 open token */
static int sNdx0 = 0;			/* device0 index */
static int sNdx1 = 1;			/* device1 index */
static int sMaxFrames = 0;		/* total frames to display */
static int sSkipFrames = 0;		/* skip between display */
static int sDidFrames = 0;		/* total frames handled */
static int sNframes = 4;		/* number of frame ptrs */
static int sLibDebug = 0;		/* show BFciLib debug info */
static char *sFNprefix = NULL;	/* file name prefix for store */
static char sFN[1024] = "";		/* for full file name */
/*--------------------------------------------------------------------------*/
#define	SHOW(x)	{ (void)printf x ; (void)fflush(stdout); }
#define	ERR(x)	{ SHOW(("ERR: ")); SHOW(x); }

static char *sArgv0 = NULL;		/* name of executable */
static void ShowHelp(void)
{
	SHOW(("%s of " __DATE__ " at " __TIME__ "\n", sArgv0));
	SHOW(("   -h           display this message and exit\n"));
	SHOW(("   -D           display library debug info ('LDB' prefix)\n"));
	SHOW(("\n"));
	SHOW(("   -x ndx       choose available device0 ndx (default 0)\n"));
	SHOW(("   -X ndx       choose available device1 ndx (default 1)\n"));
	SHOW(("   -m maxFrames max frames to display (default infinite)\n"));
	SHOW(("   -s skipFr    frames to skip between display (default 0)\n"));
	SHOW(("   -n nFrames   frames to configure for s-g (default 4)\n"));
	SHOW(("   -f fnPrefix  save last frame; fname '<prefix>_w_h_bpp.raw'\n"));
	SHOW(("\n"));
	SHOW(("  CIsimpleTwoVFG: initialize a pair of VFG for even/odd aq\n"));
	SHOW(("      display pixels\n"));
	SHOW(("      display ends with newline\n"));
	SHOW(("\n"));
}

/*==========================================================================*/
#include	<time.h>
#include	<sys/timeb.h>
static tCIDOUBLE GetTime(void)
/*
**	Return fractional seconds
*/
{
	tCIDOUBLE ans = 0.0;

#ifdef _POSIX_TIMERS

	struct timespec tp;

	(void)clock_gettime(CLOCK_MONOTONIC_RAW, &tp);
	ans = (tCIDOUBLE) tp.tv_sec;
	ans += ((tCIDOUBLE) tp.tv_nsec) / 1000000000.0;

#else

	struct timeb tb;

	(void)ftime(&tb);
	ans = tb.millitm;
	ans /= 1000.0;
	ans += tb.time;

#endif

	return (ans);
}

/*--------------------------------------------------------------------------*/
static int DecodeArgs(int argc, char **argv)
/*
**	Parse the input arguments.
*/
{
	char *str;

	argv += 1;
	argc -= 1;					/* skip program name */

	while (argc-- > 0)
	{
		str = *argv++;
		if (str[0] != '-')
		{
			ERR(("Do not know '%s' arg\n", str));
			ShowHelp();
			sExitAns = 1;
			return (sExitAns);
		}
		switch (str[1])
		{
		case 'h':
			ShowHelp();
			return (1);
		case 'D':
			sLibDebug = 1;
			break;
		case 'x':
			(void)sscanf(*argv, "%d", &sNdx0);
			argv += 1;
			argc -= 1;
			break;
		case 'X':
			(void)sscanf(*argv, "%d", &sNdx1);
			argv += 1;
			argc -= 1;
			break;
		case 'm':
			(void)sscanf(*argv, "%d", &sMaxFrames);
			argv += 1;
			argc -= 1;
			break;
		case 's':
			(void)sscanf(*argv, "%d", &sSkipFrames);
			argv += 1;
			argc -= 1;
			break;
		case 'n':
			(void)sscanf(*argv, "%d", &sNframes);
			argv += 1;
			argc -= 1;
			break;
		case 'f':
			(void)strcpy(sFN, *argv);
			sFNprefix = sFN;
			argv += 1;
			argc -= 1;
			break;
		default:
			ERR(("Do not know arg '%s'\n", str));
			ShowHelp();
			sExitAns = 1;
			return (sExitAns);
		}
	}

	return (kCIEnoErr);
}

/*--------------------------------------------------------------------------*/
static int CheckForKeyboardInput(void)
/*
**	Return 0 if no input available from stdin, 1 else
**
**	Note: the console needs a newline in order to post input.
*/
{
	fd_set exceptfds, readfds, writefds;
	struct timeval tv;
	int ans;
	char buff[1024];

	FD_ZERO(&exceptfds);
	FD_ZERO(&readfds);
	FD_ZERO(&writefds);
	FD_SET(fileno(stdin), &readfds);
	(void)memset(&tv, '\0', sizeof(struct timeval));

	ans = select(1, &readfds, &writefds, &exceptfds, &tv);

	if ((ans == 1) && FD_ISSET(fileno(stdin), &readfds))
	{
		/*
		 **   Consume the line.
		 */
		(void)fgets(buff, 1024, stdin);
		return (1);
	}

	return (0);
}

/*--------------------------------------------------------------------------*/
static void LDBdisplay(char *format, ...)
/*
**	Library debug display function.
**
**	This will display progress/information/error strings from BFciLib
*/
{
	va_list val;
	char buff[8192];

	va_start(val, format);
	(void)vsprintf(buff, format, val);
	va_end(val);
	/*
	 ** buff now contains the library display string
	 **
	 ** This function simply prints it to the console.
	 */
	(void)printf("LDB: %s\n", buff);
	(void)fflush(stdout);
}

/*--------------------------------------------------------------------------*/
static void InitAndGetDataUntilKeyPress(void)
/*
**	Illustrate a simple example dual VFG interaction sequence.
*/
{
	FILE *fp;
	tCIRC circ;
	tCIDOUBLE a = -1.0, b, c, d;
	tCIU64 totalBytes = 0, totalLines = 0, sz;
	void *buff = NULL;
	tCIU32 i, fbad = 0;
	tCIU32 nPtrs;
	tCIU8 **uPtrs = NULL;
	tCIU8 *p8;
	unsigned short *p16;
	tCIU32 *p32;
	tCIU32 frameID, value, value2, value3, pixToShow;
	tCIU8 *frameP, *fnP = NULL;
	tCIU32 nFrames, bitsPerPix, hROIoffset, hROIsize, vROIoffset, vROIsize, stride;
	tCIU32 hROIoffset2, hROIsize2, vROIoffset2, vROIsize2, bitsPerPix2, stride2;
	/*
	 ** Check for library debug requested
	 */
	circ = CiSetDebug(NULL, -1, (0 == sLibDebug) ? NULL : LDBdisplay);
	if (kCIEnoErr != circ)
	{
		ERR(("CiSetDebug gave '%s'\n", CiErrStr(circ)));
		sExitAns = 1;
		return;
	}
	/*
	 ** Open the ndx0'th frame grabber with exclusive write permission.
	 */
	circ = CiVFGopen(sNdx0, kCIBO_writeAccess | kCIBO_exclusiveWrAccess, &sCIp0);
	if (kCIEnoErr != circ)
	{
		ERR(("CiVFGopen0 (%d) gave '%s'\n", sNdx0, CiErrStr(circ)));
		sExitAns = 1;
		return;
	}
	/*
	 ** Init the VFG with the config file specified by the DIP switches.
	 */
	circ = CiVFGinitialize(sCIp0, NULL);
	if (kCIEnoErr != circ)
	{
		ERR(("CiVFGinitialize0 gave '%s'\n", CiErrStr(circ)));
		sExitAns = 1;
		goto andOut;
	}
	/*
	 ** Configure the VFG for 4 frame buffers.
	 **
	 ** We do this so the library can calculate the stride value.
	 */
	circ = CiDrvrBuffConfigure(sCIp0, 4, 0, 0, 0, 0);
	if (kCIEnoErr != circ)
	{
		ERR(("CiDrvrBuffConfigure0 gave '%s'\n", CiErrStr(circ)));
		sExitAns = 1;
		goto andOut;
	}
	/*
	 ** Determine buffer configuration.
	 */
	circ = CiBufferInterrogate(sCIp0, &nFrames, &bitsPerPix, &hROIoffset, &hROIsize, &vROIoffset, &vROIsize, &stride);
	if (kCIEnoErr != circ)
	{
		ERR(("CiBufferInterrogate0 gave '%s'\n", CiErrStr(circ)));
		sExitAns = 1;
		goto andOut;
	}
	/*
	 ** Now free the driver allocated buffers -- we now have bpp and stride.
	 */
	circ = CiDrvrBuffConfigure(sCIp0, 0, 0, 0, 0, 0);
	if (kCIEnoErr != circ)
	{
		ERR(("CiDrvrBuffConfigure0 (release) gave '%s'\n", CiErrStr(circ)));
		sExitAns = 1;
		goto andOut;
	}
	/*
	 ** Open the ndx1'th frame grabber with exclusive write permission.
	 */
	circ = CiVFGopen(sNdx1, kCIBO_writeAccess | kCIBO_exclusiveWrAccess, &sCIp1);
	if (kCIEnoErr != circ)
	{
		ERR(("CiVFGopen1 (%d) gave '%s'\n", sNdx1, CiErrStr(circ)));
		sExitAns = 1;
		goto andOut;
	}
	/*
	 ** Init the VFG with the config file specified by the DIP switches.
	 */
	circ = CiVFGinitialize(sCIp1, NULL);
	if (kCIEnoErr != circ)
	{
		ERR(("CiVFGinitialize1 gave '%s'\n", CiErrStr(circ)));
		sExitAns = 1;
		goto andOut;
	}
	/*
	 ** Configure the VFG for 4 frame buffers.
	 **
	 ** We do this so the library can calculate the stride value.
	 */
	circ = CiDrvrBuffConfigure(sCIp1, 4, 0, 0, 0, 0);
	if (kCIEnoErr != circ)
	{
		ERR(("CiDrvrBuffConfigure1 gave '%s'\n", CiErrStr(circ)));
		sExitAns = 1;
		goto andOut;
	}
	/*
	 ** Determine buffer configuration.
	 */
	circ = CiBufferInterrogate(sCIp1, &nFrames, &bitsPerPix2, &hROIoffset2, &hROIsize2, &vROIoffset2, &vROIsize2, &stride2);
	if (kCIEnoErr != circ)
	{
		ERR(("CiBufferInterrogate1 gave '%s'\n", CiErrStr(circ)));
		sExitAns = 1;
		goto andOut;
	}
	/*
	 ** Now free the driver allocated buffers -- we have the info.
	 */
	circ = CiDrvrBuffConfigure(sCIp1, 0, 0, 0, 0, 0);
	if (kCIEnoErr != circ)
	{
		ERR(("CiDrvrBuffConfigure1 (release) gave '%s'\n", CiErrStr(circ)));
		sExitAns = 1;
		goto andOut;
	}
	/*
	 ** Be sure we have even/odd compatibility
	 */
	if ((bitsPerPix != bitsPerPix2) ||
		(hROIoffset != hROIoffset2) ||
		(hROIsize != hROIsize2) ||
		(vROIoffset != vROIoffset2) ||
		(vROIsize != vROIsize2) || (stride != stride2))
	{
		ERR(("%s %d/%d bpp, %d/%d hOff, %d/%d hSz, %d/%d vOff, %d/%d vSz, %d/%d stride\n", "even/odd mismatch:", bitsPerPix, bitsPerPix2, hROIoffset, hROIoffset2, hROIsize, hROIsize2, vROIoffset, vROIoffset2, vROIsize, vROIsize2, stride, stride2));
		sExitAns = 2;
		goto andOut;
	}
	/*
	 ** Calculate the combined buffer size.
	 */
	sz = stride;
	sz *= vROIsize;				/* one VFG */
	sz <<= 1;					/* two VFG */
	sz *= sNframes;				/* nDMA buffer */
	buff = malloc(sz);
	if (NULL == buff)
	{
		ERR(("cannot malloc %lld for %d buff\n", (tCIU64) sz, sNframes));
		sExitAns = 3;
		goto andOut;
	}
	/*
	 ** Configure the even/odd buffers
	 */
	circ = CiUserBuffConfigureEvenOdd(sCIp0, sCIp1, hROIoffset, hROIsize, vROIoffset, vROIsize, sNframes, buff, sz);
	if (kCIEnoErr != circ)
	{
		ERR(("CiUserBuffConfigureEvenOdd gave '%s'\n", CiErrStr(circ)));
		sExitAns = 1;
		goto andOut;
	}
	/*
	 ** Get the buffer pointers for read/write access to buffers.
	 **
	 ** We ask for write access because we are going to clear the buffer
	 ** lines after they are displayed.
	 */
	circ = CiMapFrameBuffers(sCIp0, 1, &nPtrs, &uPtrs);
	if (kCIEnoErr != circ)
	{
		ERR(("CiMapFrameBuffers gave '%s'\n", CiErrStr(circ)));
		sExitAns = 1;
		goto andOut;
	}
	/*
	 ** Decide how many per line we show.
	 */
	switch (bitsPerPix)
	{
	case 64:
	case 8:
		pixToShow = 16;
		break;
	case 10:
	case 12:
		pixToShow = 12;
		break;
	case 14:
	case 16:
	case 24:
		pixToShow = 8;
		break;
	case 30:
	case 32:
	case 36:
	case 48:
		pixToShow = 6;
		break;
	default:
		ERR(("Do not know pixBitDepth of %d\n", bitsPerPix));
		sExitAns = 1;
		goto andOut;
	}
	/*
	 ** Reset acquisition and clear all error conditions.
	 */
	circ = CiAqSWreset(sCIp0);
	if (kCIEnoErr != circ)
	{
		ERR(("CiAqSWreset0 gave '%s'\n", CiErrStr(circ)));
		sExitAns = 1;
		goto andOut;
	}
	circ = CiAqSWreset(sCIp1);
	if (kCIEnoErr != circ)
	{
		ERR(("CiAqSWreset1 gave '%s'\n", CiErrStr(circ)));
		sExitAns = 1;
		goto andOut;
	}
	/*
	 ** Clear the first line of all frame buffers.
	 */
	for (i = 0; i < nPtrs; i++)
	{
		(void)memset(uPtrs[i], '\0', stride);
	}
	/*
	 ** Tell the user how to stop this cleanly.
	 */
	SHOW(("Will now dump first line of frames until newline (skip %d, ndx %d/%d)\n", sSkipFrames, sNdx0, sNdx1));
	/*
	 ** Set up second VFG as acquisition slave.
	 */
	circ = CiFieldPoke(sCIp1, CiFieldNdxFromStr(sCIp1, (char *)"AQBITS_SLAVED"), 1);
	if (kCIEnoErr != circ)
	{
		ERR(("CiFieldPoke of AQBITS_SLAVED  gave '%s'\n", CiErrStr(circ)));
		sExitAns = 1;
		goto andOut;
	}
	/*
	 ** Start continuous acquisition on slave unit first.
	 */
	circ = CiAqStart(sCIp1, 0);
	if (kCIEnoErr != circ)
	{
		ERR(("CiAqStart1 gave '%s'\n", CiErrStr(circ)));
		sExitAns = 1;
		goto andOut;
	}
	circ = CiAqStart(sCIp0, 0);
	if (kCIEnoErr != circ)
	{
		ERR(("CiAqStart0 gave '%s'\n", CiErrStr(circ)));
		sExitAns = 1;
		goto andOut;
	}
	a = GetTime();
	/*
	 ** Display each acquired frame 1st line in a loop.  Stop at newline.
	 */
	while (1)
	{
		/*
		 ** Check to see if a frame is already available before waiting.
		 */
checkAgain:
		circ = CiGetOldestNotDeliveredFrame(sCIp0, &frameID, &frameP);
		switch (circ)
		{
		case kCIEnoErr:
			/*
			 **   We have the frame.
			 */
			break;
		case kCIEnoNewData:
			/*
			 **   We need to wait for another frame.
			 */
			circ = CiWaitNextUndeliveredFrame(sCIp0, -1);
			if (kCIEnoErr != circ)
			{
				switch (circ)
				{
				case kCIEaqAbortedErr:
					SHOW(("CiWaitNextUndeliveredFrame gave '%s'\n", CiErrStr(circ)));
					break;
				default:
					ERR(("CiWaitNextUndeliveredFrame gave '%s'\n", CiErrStr(circ)));
					sExitAns = 1;
				}
				goto andOut;
			}
			goto checkAgain;
		case kCIEaqAbortedErr:
			SHOW(("CiGetOldestNotDeliveredFrame: acqistion aborted\n"));
			goto andOut;
		default:
			ERR(("CiGetOldestNotDeliveredFrame gave '%s'\n", CiErrStr(circ)));
			sExitAns = 1;
			goto andOut;
		}
		/*
		 **   Allow skipping frames so display is not an issue.
		 */
		if ((0 != sSkipFrames) && (0 != (sDidFrames % (sSkipFrames + 1))))
		{
			goto skipHere;
		}

#ifdef	RRRRR
#warning "RRRRR is set"
		/*
		 **   Get the appropriate frameP since uPtrs points to a dummy frame.
		 **
		 **   We build the s-g info so we know the original frame ptr values.
		 */
		frameP = sFramePtrs[frameID % sNframes];
#endif
		/*
		 **   Set the frame pointer for any possible file store
		 */
		fnP = frameP;
		/*
		 **   Display the frameID and the first line of frame data.
		 */
		SHOW(("frameID %9d:", frameID));
		switch (bitsPerPix)
		{
		case 64:
		case 8:
			p8 = frameP;
			for (i = 0; i < pixToShow; i++)
			{
				value = *p8++;
				SHOW((" %02X", value));
			}
			break;
		case 10:
		case 12:
			p16 = (tCIU16 *) frameP;
			for (i = 0; i < pixToShow; i++)
			{
				value = *p16++;
				SHOW((" %03X", value));
			}
			break;
		case 14:
		case 16:
			p16 = (tCIU16 *) frameP;
			for (i = 0; i < pixToShow; i++)
			{
				value = *p16++;
				SHOW((" %04X", value));
			}
			break;
		case 24:
			p8 = (tCIU8 *) frameP;
			for (i = 0; i < pixToShow; i++)
			{
				value = *p8++;
				value2 = *p8++;
				value3 = *p8++;
				SHOW((" %02X.%02X.%02X", value, value2, value3));
			}
			break;
		case 30:
			p32 = (tCIU32 *) frameP;
			for (i = 0; i < pixToShow; i++)
			{
				value = *p32++;
				value2 = (value >> 10) & 0x3FF;
				value3 = (value >> 20) & 0x3FF;
				value &= 0x03FF;
				SHOW((" %03X.%03X.%03X", value, value2, value3));
			}
			break;
		case 32:
			p32 = (tCIU32 *) frameP;
			for (i = 0; i < pixToShow; i++)
			{
				value = *p32++;
				SHOW((" %08X", value));
			}
			break;
		case 36:
			p16 = (tCIU16 *) frameP;
			for (i = 0; i < pixToShow; i++)
			{
				value = *p16++;
				value2 = *p16++;
				value3 = *p16++;
				SHOW((" %03X.%03X.%03X", value, value2, value3));
			}
			break;
		case 48:
			p16 = (tCIU16 *) frameP;
			for (i = 0; i < pixToShow; i++)
			{
				value = *p16++;
				value2 = *p16++;
				value3 = *p16++;
				SHOW((" %04X.%04X.%04X", value, value2, value3));
			}
			break;
		default:
			ERR(("Do not know pixBitDepth of %d\n", bitsPerPix));
			sExitAns = 1;
			goto andOut;
		}
		SHOW(("\n"));

skipHere:
		totalLines += vROIsize;
		totalBytes += stride * vROIsize;
		sDidFrames += 1;
		/*
		 **   Clear the line just displayed so we know it is rewritten by DMA.
		 */
		(void)memset(frameP, '\0', stride);
		/*
		 **   Break out of this loop on newline
		 */
		if (0 != CheckForKeyboardInput())
		{
			break;
		}
		/*
		 **   Break out of loop if countdown hits zero
		 */
		if ((0 != sMaxFrames) && (--sMaxFrames == 0))
		{
			break;
		}
	}

andOut:
	/*
	 ** Save file if asked.
	 */
	if ((NULL != fnP) && (NULL != sFNprefix))
	{
		(void)sprintf(sFNprefix + strlen(sFNprefix), "_%d_%d_%d.raw", hROIsize, vROIsize << 1, bitsPerPix);
		fp = fopen(sFNprefix, "wb");
		if (NULL == fp)
		{
			ERR(("cannot open '%s'\n", sFNprefix));
		}
		else
		{
			for (i = 0; i < vROIsize << 1; i++)
			{
				if (stride != fwrite(fnP, 1, stride, fp))
				{
					ERR(("at %d we failed to write %d to '%s'\n", i, stride, sFNprefix));
					fbad = i + 1;
					break;
				}
				fnP += stride;
			}
			(void)fclose(fp);
			SHOW(("we wrote '%s' (%d bad)\n", sFNprefix, fbad));
		}
	}
	/*
	 ** This is user scatter-gather DMA so we stop acquisition.
	 */
	if ((NULL != sCIp0) && (kCIEnoErr != (circ = CiAqSWreset(sCIp0))))
	{
		ERR(("CiAqSWreset0 (end) gave '%s'\n", CiErrStr(circ)));
	}
	if ((NULL != sCIp1) && (kCIEnoErr != (circ = CiAqSWreset(sCIp1))))
	{
		ERR(("CiAqSWreset1 (end) gave '%s'\n", CiErrStr(circ)));
	}
	/*
	 ** Unmap the frame buffers.
	 */
	if ((NULL != uPtrs) && (kCIEnoErr != (circ = CiUnmapFrameBuffers(sCIp0))))
	{
		ERR(("CiUnmapFrameBuffers gave '%s'\n", CiErrStr(circ)));
	}
	/*
	 ** Release the DMA storage.
	 */
	if ((NULL != sCIp0) && (NULL != sCIp1) && (kCIEnoErr != (circ = CiUserBuffConfigureEvenOdd(sCIp0, sCIp1, 0, 0, 0, 0, 0, NULL, 0))))
	{
		ERR(("CiUserBuffConfigureEvenOdd: release gave '%s'\n", CiErrStr(circ)));
		sExitAns = 1;
	}
	/*
	 ** Release the frame buffers.
	 */
	if (NULL != buff)
	{
		(void)free(buff);
	}
	/*
	 ** Close the access.
	 */
	if ((NULL != sCIp0) && (kCIEnoErr != (circ = CiVFGclose(sCIp0))))
	{
		ERR(("CiVFGclose0 gave '%s'\n", CiErrStr(circ)));
	}
	if ((NULL != sCIp1) && (kCIEnoErr != (circ = CiVFGclose(sCIp1))))
	{
		ERR(("CiVFGclose1 gave '%s'\n", CiErrStr(circ)));
	}
	/*
	 ** Show data rate
	 */
	c = b = GetTime() - a;
	if ((a < 0.0) || (c < 0.001))
	{
		a = 0.0;
		b = 0.0;
		c = 0.0;
		d = 0.0;
	}
	else
	{
		a = ((tCIDOUBLE) totalBytes) / c;
		b = ((tCIDOUBLE) totalLines) / c;
		d = ((tCIDOUBLE) sDidFrames) / c;
	}
	SHOW(("%d/%d: Data rate %.1lf ln/s (%.1lf b/s) (%.1lf FPS) (%.1lf sec) after %d fr\n", sNdx0, sNdx1, b, a, d, c, sDidFrames));

	return;
}

/*==========================================================================*/
int main(int argc, char **argv)
/*
**	Decode command line and acquire/display frames until EOF
*/
{
	sArgv0 = *argv;

	if (kCIEnoErr == DecodeArgs(argc, argv))
	{
		InitAndGetDataUntilKeyPress();
	}

	return (sExitAns);
}

/*==========================================================================*/
/*
	$Log: CIsimpleTwoVFG.c,v $
	Revision 1.8  2020/10/02 23:30:13  steve
	CLOCK_MONOTONIC is not always monotonic, so prefer CLOCK_MONOTONIC_RAW.

	Revision 1.7  2020/10/02 01:17:23  steve
	ftime is deprecated. Use clock_gettime.

	Revision 1.6  2017/12/12 11:05:52  steve
	Fixes for axn+vivid

	Revision 1.5  2016/07/11 20:02:59  steve
	Gn2 xx-2Y/2YE, GPUD, DGMA

	Revision 1.4  2016/03/06 23:16:08  steve
	Support CXP_usualInit and support Axion

	Revision 1.3  2015-01-15 17:24:40  steve
	Cyton available

	Revision 1.2  2014-05-29 13:14:14  steve
	User lib c++ supporting Neon/Alta/Kbn/KbnCXP

	Revision 1.1  2013-04-17 17:48:23  steve
	Add CIsimpleTwoVFG fix 32b PAE

*/