/* **********************************************************************
 * *                                                                    *
 * *     Common Routines for the Radar Data Acquisition (RDA) System    *
 * *                                                                    *
 * **********************************************************************
 * File: include/rdasubs_lib.h
 *
 *                     COPYRIGHT (c) 2001, 2003  BY
 *          SIGMET INCORPORATED, WESTFORD MASSACHUSETTS, U.S.A.
 * 
 * THIS SOFTWARE IS FURNISHED UNDER A LICENSE AND MAY BE USED AND  COPIED
 * ONLY  IN  ACCORDANCE WITH  THE  TERMS  OF  SUCH  LICENSE  AND WITH THE
 * INCLUSION OF THE ABOVE COPYRIGHT NOTICE.  THIS SOFTWARE  OR  ANY OTHER
 * COPIES  THEREOF MAY NOT BE PROVIDED OR OTHERWISE MADE AVAILABLE TO ANY
 * OTHER PERSON.  NO TITLE TO AND OWNERSHIP OF  THE  SOFTWARE  IS  HEREBY
 * TRANSFERED.
 */
#ifndef SIGMET_RDASUBS_LIB_H
#define SIGMET_RDASUBS_LIB_H 1

#ifdef __cplusplus
extern "C" {
#endif


/* *******************************
 * *                             *
 * * Message Catalog Definitions *
 * *                             *
 * *******************************
 */
#define     BAD_GLOBAL         1
#define RDA_BAD_GLOBAL       (RDA_FACILITY + BAD_GLOBAL)
#define     EXT_GLOBAL         2
#define RDA_EXT_GLOBAL       (RDA_FACILITY + EXT_GLOBAL)
#define     MISS_GLOBAL        3
#define RDA_MISS_GLOBAL      (RDA_FACILITY + MISS_GLOBAL)
#define     PTHREADERR         4
#define RDA_PTHREADERR       (RDA_FACILITY + PTHREADERR)
#define     RPCIRIS_AGAIN      5
#define RDA_RPCIRIS_AGAIN    (RDA_FACILITY + RPCIRIS_AGAIN)
#define     RPCIRIS_CREATE     6
#define RDA_RPCIRIS_CREATE   (RDA_FACILITY + RPCIRIS_CREATE)
#define     RPCIRIS_REG        7
#define RDA_RPCIRIS_REG      (RDA_FACILITY + RPCIRIS_REG)
#define     RPCIRIS_EXIT       8
#define RDA_RPCIRIS_EXIT     (RDA_FACILITY + RPCIRIS_EXIT)
#define     RPCIRIS_REPLY      9
#define RDA_RPCIRIS_REPLY    (RDA_FACILITY + RPCIRIS_REPLY)
#define     RPCIRIS_NOPROC    10
#define RDA_RPCIRIS_NOPROC   (RDA_FACILITY + RPCIRIS_NOPROC)
#define     GENSPC_ARGS       11
#define RDA_GENSPC_ARGS      (RDA_FACILITY + GENSPC_ARGS)
#define     BADTSUNIT         12
#define RDA_BADTSUNIT        (RDA_FACILITY + BADTSUNIT)
#define     BADTSCLIENT       13
#define RDA_BADTSCLIENT      (RDA_FACILITY + BADTSCLIENT)
#define     MISSTSDATA        14
#define RDA_MISSTSDATA       (RDA_FACILITY + MISSTSDATA)
#define     TSMERGESYNC       15
#define RDA_TSMERGESYNC      (RDA_FACILITY + TSMERGESYNC)
#define     NOTWRITER         16
#define RDA_NOTWRITER        (RDA_FACILITY + NOTWRITER)
#define     DUPWRITER         17
#define RDA_DUPWRITER        (RDA_FACILITY + DUPWRITER)
#define     COREDUMP          18
#define RDA_COREDUMP         (RDA_FACILITY + COREDUMP)

#ifndef MESSAGE_ONLY

/* *****************************************
 * *                                       *
 * *  RVP8 TimeSeries (RVP8TS) Public API  *
 * *                                       *
 * *****************************************
 */
#ifdef SIGMET_RVP8_H

/* This collection of routines provides an interface layer into the
 * live (I,Q) timeseries that are generated by the RVP8.  From a
 * reader's point of view the API is entirely stateless and passive,
 * i.e., it allows any number of callers to eavesdrop on the (I,Q)
 * data as they arrive, but there are no control actions passed back
 * in the other direction.
 *
 * The reason that an event driven model is not provided is that the
 * API is fundamentally a single-writer / multiple-reader interface.
 * There is no private state maintained for each reader client that
 * hooks up to it.  This was a design goal from the start; readers
 * should be able to come and go willynilly without affecting anything
 * else.
 *
 * As such, the notion of 'notify me when new data are available' is
 * not well defined, because 'new' would have to be a per-client
 * notion, i.e., 'new' since the last data that each particular client
 * happened to look at.
 * 
 * Also, the API is really most valuable in providing random access to
 * the recent buffered (I,Q) data.  Because of the PCI/DMA buffering
 * this is a pseudo realtime interface, and the data are always
 * 200-400ms delayed from their actual time of arrival.  As such, it
 * is not terribly important to be able to track the leading edge of
 * newly arriving data with any particular precision.
 * 
 * There are about two seconds of data buffered in the timeseries API;
 * so merely checking at 10-20Hz presents a negligible CPU load, does
 * not risk loosing any data, and matches the PCI/DMA burst transfers.
 */
struct rvp8ts_info ;		/* Opaque to callers */
typedef struct rvp8ts_info Rvp8TS ;

/* Non-volatile configuration file for the Timeseries API
 */
struct rvp8tsConfig {
  char sVersion[8] ;		/* IRIS version when saved */
  SINT4 iDesWrMainUnit ;	/* RVP8/Main initial desired writer */
  SINT4 iDesWrApp0Unit ;	/* APP0 and APP1 initial desired writer */
  SINT4 iDesWrApp1Unit ;
  SINT4 nIQDVecs ;		/* Number of buffered (I,Q) vectors */
} ;

/* --------------------------------------------------------------
 * ------------------ Attach/Release Routines  ------------------
 * --------------------------------------------------------------
 */
/* Attach to a live timeseries data section; the returned pointer
 * should be used for all subsequent calls within this API.  This
 * routine is used by all reader clients, and can also be used by a
 * solitary writer client.  The API is created by the first writer
 * that tries to attach to it, and it must already exist before any
 * reader can attach.
 */
Rvp8TS * rvp8tsAttach_( MESSAGE *pStatus_a, SINT4 iUnit_a, SINT4 iClientType_a ) ;

#define RVP8TS_UNIT_MAIN     0	/* Final timeseries from RVP8 */
#define RVP8TS_UNIT_RXPRI    1	/* Intermediate APIs reserved for */
#define RVP8TS_UNIT_RXSEC    2	/*   individual RVP8/Rx cards */
#define RVP8TS_UNIT_APP0     3	/* Available for other applications */
#define RVP8TS_UNIT_APP1     4
#define RVP8TS_MAX_UNITS     5  /* This many independent API's can exist */

#define RVP8TS_CLIENT_VOID     -1 /* Special flag for 'No Client Type' */
#define RVP8TS_CLIENT_READER    0 /* Client is one-of-many readers */
#define RVP8TS_CLIENT_RVP8MAIN  1 /* Writer: RVP8/Main realtime Rx card */
#define RVP8TS_CLIENT_ARCHIVE   2 /* Writer: Archive/Playback data */
#define RVP8TS_CLIENT_TSIMPORT  3 /* Writer: Network data reception */
#define RVP8TS_CLIENT_APP0      4 /* Writer: Available for other applications */
#define RVP8TS_CLIENT_APP1      5
#define RVP8TS_CLIENT_SWITCHER  6 /* Optional special 'switch request' client */
#define RVP8TS_MAX_CLIENTS      7 /* Illegal values here and beyond... */

#define RVP8TS_DUPLICATE_CLIENT_MASK \
  BIT( RVP8TS_CLIENT_APP0 ) | BIT( RVP8TS_CLIENT_READER   ) | \
  BIT( RVP8TS_CLIENT_APP1 ) | BIT( RVP8TS_CLIENT_SWITCHER )

#if defined(SIGMET_RVP8_H) && \
  ( (RVP8TS_MAX_UNITS   > RVP8TSUNITDIM  ) || \
    (RVP8TS_MAX_CLIENTS > RVP8TSCLIENTDIM) ) 
#error Shared memory attached client list is not sized properly
#endif

/* Release the attachment that this process/thread has to a live RVP8
 * timeseries segment.
 */
MESSAGE rvp8tsRelease( Rvp8TS *pTS_a ) ;

/* Given a client type, return a brief string describing that client.
 */
const char *rvp8tsClientName( SINT4 iClientType_a ) ;

/* ---------------------------------------------------------------
 * ------------- Support for Pulse Sequence Numbers  -------------
 * ---------------------------------------------------------------
 */
/* The RVP8 assigns a sequential 32-bit key to each pulse of (I,Q)
 * data that is acquired.  These pulse sequence numbers are then used
 * throughout the RVP8TS API to refer to pulses and navigate among
 * them.  RVP8TSCURRENTSEQNUM() returns the sequence number of a very
 * recently acquired valid pulse, which generally will also be the
 * MOST recently acquired one.  There will always be such a pulse,
 * though it may not be one that you are actually interested in.
 */
UINT4 rvp8tsCurrentSeqNum( Rvp8TS *pTS_a ) ;

/* The acquisition mode is a byte that increments whenever the
 * operating conditions and/or environment have changed for the data
 * being written to the API.  Knowing the current acquisition mode
 * allows us to decide whether a prior pulse of data was acquired
 * according to the present operating conditions.
 */
UINT1 rvp8tsCurrentAqMode( Rvp8TS *pTS_a ) ;

/* Given an acquisition mode, return a flag indicating whether that
 * set of pulses has come to a definite end, i.e., there will be no
 * more pulses written with that iAqMode value.
 */
UINT1 rvp8tsAqModeEnded( Rvp8TS *pTS_a, UINT1 iAqMode_a ) ;

/* Test whether a given pulse sequence number exists as a valid (okay)
 * pulse in the timeseries history.  This routine can be called after
 * having processed the data from RVP8TSGETPULSES() in order to check
 * that the very first pulse sequence number (plus its associated
 * data) is still valid.  If it is, then you know that all of the live
 * pulse data were sampled properly for the entire set of pulses,
 * i.e., none of those data were overwritten in the time it took to
 * process them all.
 *
 * The convention is that the RVP8TS API only knows about sequence
 * numbers that 1) were "recently" supplied by RVP8TSCURRENTSEQNUM(),
 * or 2) were computed incrementally by adding a forward pulse count
 * to such a number.  These forward pulse counts might have come from
 * calls to RVP8TSGETPULSES() or RVP8TSNUMPULSESFWD().  In the latter
 * case, the manually updated sequence number may not be immediately
 * valid if the data for that pulse have not yet arrived.  This is a
 * normal condition.
 *
 * Of course a completely random sequence number is also virtually
 * assured fail the test for validity, and thus resemble one of these
 * properly updated values whose data merely are not available just
 * yet.  To distinguish these three cases, please use the following
 * pair of tests on a trial sequence number:
 *
 *    if( rvp8tsSeqNumOkay( TS, iTrialSeqNum ) )
 *      {
 *        The sequence number is valid; proceed with processing
 *      }
 *    else if( rvp8tsSeqNumOkay( TS, iTrialSeqNum-1 ) )
 *      {
 *        Wait a little while; the sequence number will soon be valid
 *      }
 *    else
 *      {
 *        The sequence number is bad; we're lost, get a new one
 *      }
 */
UINT1 rvp8tsSeqNumOkay( Rvp8TS *pTS_a, UINT4 iSeqNum_a ) ;

/* Given a pulse sequence number, RVP8TSLATENESS() computes how "late"
 * or "far behind" it is in the timeseries buffer.  A value of 0.0
 * means very recent (not late at all); and 1.0 means very old, or
 * entirely invalid. The inverse routine RVP8TSLOOKUPLATENESS() finds
 * a sequence number that approximates a given lateness.
 *
 * The lateness computations are based on both header wraparound and
 * data wraparound, with the larger (later) of the two being used.
 * Lateness values are useful because they provide callers with a
 * measure of how well they are keeping up with the realtime data
 * flow.
 */
FLT4        rvp8tsLateness( Rvp8TS *pTS_a, UINT4 iSeqNum_a  ) ;
UINT4 rvp8tsLookupLateness( Rvp8TS *pTS_a, FLT4 fLateness_a ) ;

/* Return the actual age in milliseconds of the data corresponding to
 * a given pulse sequence number.  A very large number is returned if
 * the sequence number is invalid.
 */
UINT4 rvp8tsRealTimeAge( Rvp8TS *pTS_a, UINT4 iSeqNum_a ) ;

/* Starting from a given pulse, return the number of valid contiguous
 * pulses in either the forward or reverse direction.  The identified
 * group of pulses are guaranteed to have been acquired in a mutually
 * consistent manner (same ranges, same FIR coefficients, etc).  This
 * tells the maximum number of available pulses that can sensibly be
 * interpreted as a coherent group.
 *
 * NOTE: The returned count includes the starting pulse itself.  A
 * count of zero, therefore, means that the starting pulse itself was
 * invalid.  A count of one means that the starting pulse was valid,
 * but had no neighbor in the indicated direction, etc.
 */
SINT4 rvp8tsNumPulsesFwd( Rvp8TS *pTS_a, UINT4 iSeqNum_a ) ;
SINT4 rvp8tsNumPulsesRev( Rvp8TS *pTS_a, UINT4 iSeqNum_a ) ;

/* ------------------------------------------------------------------
 * ----------- Pulse Header and Data Extraction Routines  -----------
 * ------------------------------------------------------------------
 */
/* Get a pointer to the pulse header corresponding to a given sequence
 * number, or NULL if that number has expired.
 */
const struct rvp8PulseHdr *rvp8tsGetPulseHdr
( Rvp8TS *pTS_a, UINT4 iSeqNum_a ) ;

/* Given a pulse header, return a pointer to the auxiliary information
 * structure that is associated with it.  This structure holds data
 * that remain constant within each block of pulses that are acquired
 * in a consistent manner, i.e., within each "ProcSection" interval.
 */
const struct rvp8PulseInfo *rvp8tsGetPulseInfo
( Rvp8TS *pTS_a, const struct rvp8PulseHdr *pHdr_a ) ;

/* Related routine to return the information structure for the most
 * recent pulse in the timeseries API.
 */
const struct rvp8PulseInfo *rvp8tsCurrentPulseInfo( Rvp8TS *pTS_a ) ;

/* Return the configured size of the (I,Q) circular data storage area.
 * Value returned is the number of (I,Q) vectors that are buffered.
 */
SINT4 rvp8tsGetNumIQDVecs( Rvp8TS *pTS_a ) ;

/* Get a contiguous run of pulse headers that begins with a given
 * pulse sequence number.  Return value is the actual number of pulses
 * that were collected together.  This number may be less than
 * IMAXPULSES_A if fewer contiguous pulses are available, or if the
 * headers are wrapping, or if there is a discontinuity in how the
 * pulses were acquired.
 *
 * Alternate entry point RVP8TSGETPULSESQUICK() can be used to get an
 * interval of pulse headers when you already know that the interval
 * is sensible.  It returns either the requested pulse count, or zero
 * if the first pulse has dropped out of existence.  In that case the
 * pulse header array is still filled in, but pointing to zeroed
 * headers.
 */
SINT4 rvp8tsGetPulses
( Rvp8TS *pTS_a, const struct rvp8PulseHdr *pHdrs_a[],
  UINT4 iSeqNum_a, SINT4 iMaxPulses_a ) ;

SINT4 rvp8tsGetPulsesQuick
( Rvp8TS *pTS_a, const struct rvp8PulseHdr *pHdrs_a[],
  UINT4 iSeqNum_a, SINT4 iPulses_a ) ;

/* Scan an array of pulse headers to determine the minimum and maximum
 * number of (I,Q) vectors over the entire set of pulses.
 */
void rvp8tsMinMaxVecs
( const struct rvp8PulseHdr *pHdrs_a[], SINT4 nPulses_a,
  SINT4 *pMinVecs_a, SINT4 *pMaxVecs_a, FLT4 *pAvgVecs_a ) ;

/* Return a pointer to the floating (I,Q) data for a given pulse and a
 * given receive channel.  The floating data are returned as a pointer
 * to existing memory within the timeseries API, which is then usable
 * by both reader and writer clients.  The return pointer is qualified
 * as a simple (FLT4 *) with the following caveats:
 *
 *    Timeseries readers should add a 'const' qualifier to provide
 *    compile time checking that the (I,Q) data are never written.
 *    Note that a runtime error will still result even if this is not
 *    done, as the shared timeseries segment is not mapped writable
 *    for readers.
 *
 *    Although the (I,Q) data physically reside in volatile shared
 *    memory, they are assumed to be non-volatile from the point of
 *    view of API readers and writers during actual data processing.
 *    This is why no 'volatile' return qualifier is used here.
 *
 * The floating "I" and "Q" values are normalized so that a full scale
 * sinusoidal input signal at the RVP8/IFD IF-Input would produce a
 * vector magnitude of 1.0.
 */
FLT4 *rvp8tsIQDataFLT4
( Rvp8TS *pTS_a, const struct rvp8PulseHdr *pHdr_a, SINT4 iRx_a ) ;

/* Return the maximum magnitude of an array of timeseries data.  This
 * can be useful for detecting saturated data points, for example.
 */
FLT4 rvp8tsMaxMagSqIQ( const FLT4 *fTS_a, SINT4 nPoints_a ) ;

#define SATIQPOWTHRESHSUM  0.10	/* Power thresh for checking individual (I,Q)'s */
#define SATIQPOWTHRESHONE  0.95	/* Max (I,Q) that would not have saturated A/D's */
#define SATIQMAXDOMINANCE 16.0	/* Max T0/R0 that's recoverable during saturation */

/* ------------------------------
 * Routines to help efficiently extract timeseries (single bin,
 * multiple pulses) from the (I,Q) data area.  The PREP routine
 * analyzes a sequence of pulse headers and summarizes how the data
 * for a given receiver should be accessed.  Most of the work is
 * determining where the possible wraparound seam lies in the
 * otherwise contiguous pulse data.
 */
struct rvp8PrepTS {		/* PreProcessed header/data info */
  SINT4 iRx ;			/*   Which receiver this analysis is for */
  SINT4 iStride ;		/*   Data stride between pulses */
  SINT4 iMaxPulses ;		/*   This is all that we know about */
  SINT4 iInitialRun ;		/*   # of pulses up to possible data wrap */
  const FLT4 *pIQ[2] ;		/*   ==> Initial and wrapped pulse starts */
} ;

void rvp8tsPrepTimeSeries
( Rvp8TS *pTS_a, struct rvp8PrepTS *prepTS_a,
  const struct rvp8PulseHdr *pHdrs_a[], SINT4 nPulses_a, SINT4 iRx_a ) ;

/* The corresponding GETPREP routine returns the timeseries for a
 * number of contiguous bins from the previously selected receiver.
 * Use a matrix transpose operation to extract several bins at a time
 * in a way that does not create a lot of cache misses.
 */
void rvp8tsGetPreppedTS
( FLT4 *fTS_a, SINT4 iStrideTS_a, const struct rvp8PrepTS *prepTS_a,
  SINT4 nPulses_a, SINT4 iBin_a, SINT4 iBinCount_a ) ;

/* ------------------------------------------------------------------
 * ---------------   Writer/Creator Client Interface  ---------------
 * ------------------------------------------------------------------
 */
/* Attempt to become an active writer of a given timeseries API.  The
 * return value is the actual writer state that was attained, which
 * may be different than the requested state if other clients are
 * conflicting with our request.
 */
UINT1 rvp8tsSetWriterState( Rvp8TS *pTS_a, UINT1 lActive_a ) ;
UINT1 rvp8tsGetWriterState( Rvp8TS *pTS_a ) ;

/* Get the type of client that is currently the active writer for a
 * given Timeseries API.  There is a possibility that this function
 * may return a non-void client type when, in fact, no such client
 * actually exists.  This can happen when a writer client is killed
 * ungracefully.  The rvp8tsSetWriterState() routine will still work
 * properly in such cases, but you must be careful not to be misled
 * into thinking that it's not worth trying to call it because some
 * other writer still appears to be active.
 */
SINT4 rvp8tsGetCurrentWriter( Rvp8TS *pTS_a ) ;

/* Check whether a client of the given type is presently attached to
 * the API.  The same caveats mentioned for rvp8tsGetCurrentWriter()
 * also apply to this routine, i.e., it is possible to think that a
 * badly killed client is still attached.
 */
UINT1 rvp8tsCheckForClient( Rvp8TS *pTS_a, SINT4 iClientType_a ) ;

/* Get/Set the desired type of writer client for a given API.  This
 * desired writer is really only an advisory to other writer clients,
 * telling them which data source is the desired one at the moment.
 */
void  rvp8tsSetDesiredWriter( Rvp8TS *pTS_a, SINT4 iClientType_a ) ;
SINT4 rvp8tsGetDesiredWriter( Rvp8TS *pTS_a ) ;

/* Allocate the required space from the timeseries circular buffers to
 * hold the actual (I,Q) data.  If the amount of space remaining for
 * the next pulse of (I,Q) data can not hold the longest possible
 * segment, then wrap the data write pointer back to the beginning of
 * the buffer.  This insures that all of the pulses that we generate
 * for a given pulse are contiguous in memory.  Then fill in the data
 * offset of the start of the pulse for each receiver, and bump the
 * global (I,Q) write pointer as we go.  The stride between each pulse
 * is constant, making it easier later on to extract timeseries from
 * individual bins.
 *
 * The global state of pTS_a is updated and the following header
 * fields are filled in:
 *
 *    iDataOffs[] : Starting indicies of (I,Q) data for each Rx
 *     iVIQPerBin : Number of (I,Q) vectors per bin
 *       iMaxVecs : Maximum (I,Q) data length, used to set pulse stride
 *        iWrapIQ : WrapAround flag for (I,Q) circular buffer
 */
void rvp8tsAllocateDataAreas
( Rvp8TS *pTS_a, struct rvp8PulseHdr *pNewHdr_a,
  SINT4 iVIQPerBin_a, SINT4 iMaxVecs_a ) ;

/* Insert the next successive pulse header into a given timeseries
 * segment.  The new header will be marked as VALID, so the assumption
 * is that all of the (I,Q) data that are referenced by the header
 * have already been written.
 *
 * The global state of pTS_a is updated and the following header
 * fields are filled in:
 *
 *     iSeqNum : Sequence number for this new pulse
 *     iAqMode : Acquisition mode reference flag
 *      lValid : Header contents valid flag
 */
const struct rvp8PulseHdr * rvp8tsInsertPulseHdr
( Rvp8TS *pTS_a, const struct rvp8PulseHdr *pNewHdr_a ) ;

/* Begin a new acquisition mode interval, i.e., a contiguous run of
 * pulses that are marked as having all been acquired in the same
 * manner.  The auxiliary information structure will be associated
 * with all of the pulses that are subsequently acquired.
 *
 * The iAqMode field of the saved pulse information structure gets
 * filled in by this routine.  Whatever value it had on entry is
 * discarded.
 */
SINT4 rvp8tsBeginNewAqMode
( Rvp8TS *pTS_a, const struct rvp8PulseInfo *pInfo_a ) ;

/* Optional routine to announce the end of the current acquisition
 * mode.  This call can be issued by the timeseries writer when it
 * knows that no more pulses will be written in the current manner.
 * The advantage of doing this is that it gives readers a clue for
 * early exit from their processing routines.
 */
void rvp8tsEndCurrentAqMode( Rvp8TS *pTS_a ) ;

/* Merge the data from two Ctrl/Data timeseries streams attached to
 * two RVP8/Rx cards into a third control-only stream that combines
 * the data from both receivers.
 */
MESSAGE rvp8tsMergeStreams
( Rvp8TS *TS_a, Rvp8TS *RxTS_a[2], UINT4 iSeqNums_a[2], SINT4 iSysTimeOff_a ) ;

#endif /* #ifdef SIGMET_RVP8_H */

/* ************************************
 * *                                  *
 * *  Miscellaneous RDA Entry Points  *
 * *                                  *
 * ************************************
 */

/* Macro to exponentially average a new value into a running average.
 * The intermediate calculations are done as FLT8.
 */
#define FEXPAVG( NEW, AVG, EXP ) \
  AVG = ( (      (FLT8)(EXP)) * ((FLT8)(AVG)) ) + \
        ( (1.0 - (FLT8)(EXP)) * ((FLT8)(NEW)) )

/* ---------------------------------------------------------------
 * ------------------ Simple Device/Memory I/O  ------------------
 * ---------------------------------------------------------------
 */
/* Open an individual device file for the selected mode of operation.
 * Do nothing if the file name is empty.
 */
MESSAGE openSimpleDev
( volatile const char *sDevName_a, const char *sMode_a, FILE **pFile_a ) ;

/* Create (or map to) a shared memory section.  It is an error if we
 * ask to create a section that already exists, or if we try to map to
 * a section that does not exist.  Write access is by request.
 */
MESSAGE rdaShmemMap
( const char *sName_a, SINT4 iReqSize_a, UINT1 lCreate_a, UINT1 lWriteable_a,
  void **mapAddr_a, SINT4 *mapSize_a, SINT4 *mapChan_a ) ;

/* Setup our internal signal handler for the likely signals that we
 * might get from the outside world.  The RDA version of signal setup
 * is needed until the standard IRIS handlers become reentrant.
 */
void rda_unix_handler_setup
( void (*unix_handler_a)( int iSigNum_a ) ) ;
void rda_unix_handler_setup_one
( void (*unix_handler_a)( int iSigNum_a ), int iSigNum_a ) ;

void encourageCoreDump( void ) ;

MESSAGE iDivertToLogFile( const char *sName_a, SINT4 iOldFD_a ) ;

/* ----------------------------------------------------------------
 * ---------------- Low-Level Conversion Routines  ----------------
 * ----------------------------------------------------------------
 */

/* Return PRF ratio for the given unfolding mode.
 */
FLT4 fUmodeRatio( SINT4 umode_a ) ;

/* Produce a dB level that clamps at a reasonable lower limit.
 */
FLT4 fSaneDB( FLT4 fLevel_a ) ;
#define MINSANEDB  -150.0
#define MINSANEPOW  1E-15

/* -----------------------------------------------------------------
 * ---------------- Remote Procedure Call Routines  ----------------
 * -----------------------------------------------------------------
 */
/* Main thread to register an RPC handler for requests from the
 * IRISNET utility.  This routine should be created as a thread, and
 * it will never return.
 */
struct rpcIris {		/* Arg to rpcIris_main() */
  UINT4 rpcServPrognum ;	/*   Server program number */
  UINT4 (*statFunc)( void ) ;	/*   Function returning live status */
} ;
void *rpcIris_main( void *rpcIris_a ) ;

/* --------------------------------------------------------------
 * ---------------- RVP8 Shared Access Routines  ----------------
 * --------------------------------------------------------------
 */
#ifdef SIGMET_RVP8_H

/* Create/Map/Unmap the RVP8 shared global section
 */
MESSAGE rvp8GlobalMap( UINT1 lCreate_a, UINT1 lWriteable_a ) ;
MESSAGE rvp8GlobalUnmap( void ) ;
volatile struct rvp8Global *rvp8GetGlobalPointer( void ) ;

/* Create/Map/Unmap the RVP8 shared (I,Q) data section
 */
MESSAGE rvp8IQCtrlDataMap
( SINT4 iUnit_a, const SINT4 *nIQDVecs_a, UINT1 lCreate_a, UINT1 lWriteable_a ) ;

MESSAGE rvp8IQCtrlDataUnmap( SINT4 iUnit_a ) ;

volatile struct rvp8IQCtrl *rvp8GetIQCtrlPointer( SINT4 iUnit_a ) ;
volatile struct rvp8IQData *rvp8GetIQDataPointer( SINT4 iUnit_a ) ;

/* Create/Map/Unmap the RVP8 shared PROC data section
 */
MESSAGE rvp8MMProcMap( UINT1 lCreate_a, UINT1 lWriteable_a ) ;
MESSAGE rvp8MMProcUnmap( void ) ;
volatile struct rvp8MMProc *rvp8GetMMProcPointer( void ) ;

#endif /* #ifdef SIGMET_RVP8_H */

/* --------------------------------------------------------------
 * -------------- RVP8 Algorithm Support Routines  --------------
 * --------------------------------------------------------------
 */
#ifdef SIGMET_RVP8_H
#ifdef SIGMET_INTELIPP_LIB_H

#define NBINMOMCORS  2		/* Number of complex correlation terms */

struct rvp8DftConf {		/* Specifications for computing DFTs */
  SINT4 iSpecSize ;		  /* Actual spectrum size to use */
  FLT4  fReNormExp ;              /* Renormalized spectrum weighting exponent */
  IppsDFTSpec_C_32fc *pDFTSpec ;  /* Precomputations for FFT/DFT */
  Ipp8u *pDFTWork ;		  /* Dedicated working buffer */
  Ipp32fc windows		  /* Precomputed windows for each of the */
    [NWINTYPES][RVP8MAXPULSES] ;  /*   possible filter choices. */
  FLT4 fCorDots			  /* Dot product terms for computing each */
    [ 2*NBINMOMCORS ]		  /*   lag of autocorelations directly from */
    [ RVP8MAXPULSES ] ;		  /*   the power spectrum. */
  UINT4 iMagic ;		  /* Magic number to indicate validity */
#define DFTCONFMAGIC 0xD93571AE
} ;

void rvp8DftConfInit  ( struct rvp8DftConf *dftConf_a, SINT4 iSpecSize_a ) ;
void rvp8DftConfReInit( struct rvp8DftConf *dftConf_a, SINT4 iSpecSize_a ) ;

void rvp8DftConfFree( struct rvp8DftConf *dftConf_a ) ;

typedef void ( genPowerSpectrum_f )
       ( FLT4 *fSpec_a, const FLT4 *fTS_a, SINT4 iWinType_a,
	 const struct rvp8DftConf *dftConf_a, SINT4 nPulses_a ) ;

genPowerSpectrum_f rvp8GenPowerSpectrum ;

void rvp8RenormSpectrum
( FLT4 *fSpec_a, const FLT4 *fTS_a, const struct rvp8DftConf *dftConf_a ) ;

#endif /* #ifdef SIGMET_INTELIPP_LIB_H */

FLT4 fMeanRawBinSpacingKM( volatile const struct rvp8Global *GM_a ) ;

void  custom_rnv_init ( volatile FLT4 fcustom_rnvtab_a[ CUSTOMRNVSIZE ] ) ;
FLT4 fcustom_rnv( volatile const FLT4 fcustom_rnvtab_a[ CUSTOMRNVSIZE ],
		  FLT4 frange_a ) ;

#endif /* #ifdef SIGMET_RVP8_H */

SINT4 iCoPolRxChan
( SINT4 iRxMode_a, UINT1 lCoPolOnPriRx_a, SINT4 iTxPol_a ) ;

#ifdef SIGMET_RVP8_H

const struct rvp8Noise * rvp8RefNoises
( volatile const struct rvp8Global *GM_a, Rvp8TS *TS_a ) ;

void initNoiseOnePW
( volatile struct rvp8Noise *pNoises_a, FLT4 fsdbmlevel_a,
  FLT4 fnsedbdown_h_a, FLT4 fnsedbdown_v_a, FLT4 fNoiseStdvDB_a ) ;

FLT4 fRvp8RefZCal
( volatile const struct rvp8Global *GM_a, Rvp8TS *TS_a ) ;

#endif /* #ifdef SIGMET_RVP8_H */

/* --------------------------------------------------------------
 * ------------------ Multiple Thread Support  ------------------
 * --------------------------------------------------------------
 */
#ifdef INCLUDE_SCHED_PROTOTYPES

SINT4 setSchedParam
( struct sched_param *pSchedParam_a, int iPolicy_a, SINT4 iPriority_a ) ;

#endif /* #ifdef INCLUDE_SCHED_PROTOTYPES */

#ifdef INCLUDE_PTHREAD_PROTOTYPES

#define PSELF_NONE ((void *)0)	/* Values for thread-specific pointers */
#define PSELF_ROOT ((void *)1)	/*   to private data. */

UINT1 lPthreadCondRelTimedWait
( pthread_cond_t *pCond_a, pthread_mutex_t *pMutex_a, UINT4 iMicroSec_a ) ;

const char * schedPolicyName( int iPolicy_a ) ;

int gprof_pthread_create
( pthread_t * thread, pthread_attr_t * attr,
  void * (*start_routine)(void *), void * arg ) ;

#endif /* #ifdef INCLUDE_PTHREAD_PROTOTYPES */

/* --------------------------------------------------------------
 * ------------------ DSP Info Library Covers  ------------------
 * --------------------------------------------------------------
 */
FLT4 fGasAttenFromCode16_rda( UINT2 icode16_a  ) ;
UINT2 iCode16FromFGasAtten_rda( FLT4 fGasAtten_a ) ;

#ifdef SIGMET_SETUP_H
const char *DspStringFromPMode_rda
( char sString_a[16], UINT2 pmode_a,
  const char sUserPModeNames_a[NUMPMODENAMES][16] ) ;
#endif /* #ifdef SIGMET_SETUP_H */

MESSAGE dsp_tcf_from_string_rda( const char expr_a[], UINT2 *tcf_a ) ;
char * dsp_string_from_tcf_rda( UINT2 tcf_a ) ;

#endif /* #ifndef MESSAGE_ONLY */

#ifdef __cplusplus
}
#endif
#endif /* #ifndef SIGMET_RDASUBS_LIB_H */
