/*****************************************************************************
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
*/