/*******************************************************************************
 *
 * RTips Technologies
 * Bangalore, India
 * www.rtipsonline.com
 *
 * Filename: mti_irig_demo.c
 *
 * Copyright (c) 2025 RTips Technologies
 *
 *******************************************************************************
 * Module Description:
 *
 *      This application demonstrates the use of IRIG-B time synchronisation
 * 	between IRIG 106 CH10 monitors running on different computers. The common
 *	time provided by IRIG-B packets are used as reference times along with the 
 *	relative time counter (RTC) of the individual computers to generate an 
 *	absolute time that is same in all connected monitors to a resolution and 
 *	accuracy of 1ms or better. 
 *
 *	On hardware that support IRIG-B transmitters (e.g. BU-67210i/F/M), the 
 *	sample enquires if the transmitter should be switched ON. Such boards 
 *	may be used as an IRIG-B Source.
 *
 *	For the time synchronisation to work, proper connections must be made
 *	to feed the IRIG-B time code into all the 1553 cards. Further, it must  
 *	be ensured that there is only one IRIG-B transmitter.
 *
 *	_______________		  _________________________
 *	|IRIG-B Source|---------->|IRIG 106 CH10 Monitor-1|
 *	---------------	     | 	  -------------------------
 * 			     |	  _________________________
 *			     ---->|IRIG 106 CH10 Monitor-2|
 *			     | 	  -------------------------
 *			     |	  _________________________
 *			     ---->|IRIG 106 CH10 Monitor-n|
 *			     	  -------------------------
 *	For BU-67210i, following are the relevant pinouts:
 *	a. IRIG-B transmitter 	: pin-30 on the breakout DB37 connector
 *	b. IRIG-B input		: pin-34 on the breakout DB37 connector
 * 	c. IRIG-B GND		: pin-31 on the breakout DB37 connector
 ******************************************************************************/

/* Include Files */
#ifdef WIN32
#include <windows.h>
#include <conio.h>
#include <stdio.h>
#include <ctype.h>
#include "stdemace.h"
#endif

#ifdef LINUX
#include <unistd.h>             /* For 'sleep()'. */
#include <emacepl/stdemace.h>
/*#define DDC_LINUX_THREAD*/
#include <stdio.h>
#include <ctype.h>
/*#include "../linuxutil.h"*/
#endif

#ifdef INTEGRITY
#include "../Integrity_Samples.h"
#endif

#ifdef VX_WORKS
#include "../LibPrj/include/stdemace.h"
#include "../utils/vxwrksuti.h"
#endif

#define MTI_NUM_CHANNELS 4

static S16BIT wResult           = 0x0000;
static S16BIT DevNum            = 0x0000;
static S16BIT timeout           = 0;
static U16BIT quit              = 0;
static MTI_CH10_DATA_PKT* pPkt  = NULL;
static MTI_CH10_TIME_PKT* pTimePkt = NULL;
static FILE *pFile = NULL;

static char szMessage[1000];
static int timeMsgTotalNum = 0;

static U32BIT totalPcktNum      = 0;    /* total number of packets                      */
static U32BIT totalMsgNum       = 0;    /* total number of messages                     */
static int pcktNum              = -1;   /* number of packets read in 10 sec interval    */
static U16BIT dataChnlId        = 0;
static U16BIT irigChnlId        = 1;

static U32BIT dwOptions         = 0;

static void ThreadMsgGet();
static void ThreadMsgRate();
static U16BIT* DecodeCh10Msg(U16BIT* ptr16, U16BIT* ptr16RelTime);

#ifdef WIN32
__int64 i64DeviceTime = 0;
__int64 i64PreviousDeviceTime = 0;
#endif /* WIN32 */

#ifdef LINUX
S64BIT i64DeviceTime = 0;
S64BIT i64PreviousDeviceTime = 0;
#endif /* LINUX */

#ifdef VX_WORKS
S64BIT i64DeviceTime = 0;
S64BIT i64PreviousDeviceTime = 0;
#endif /* VX_WORKS */

#ifdef INTEGRITY
S64BIT i64DeviceTime = 0;
S64BIT i64PreviousDeviceTime = 0;
#endif /* INTEGRITY */

typedef struct
{
    long unsigned int ulHours;
    long unsigned int ulMinutes;
    long unsigned int ulSeconds;
    long unsigned int ulYears;
    long unsigned int ulMilliSeconds;
    long unsigned int ulDays;
    long unsigned int ulMonths;
    unsigned char timeSource;   /* 1 = External, 0 = Internal, All Other Values = Source Invalid */
}ABSOLUTE_TIME;

ABSOLUTE_TIME absDeviceTime;

#define PRINT_DATA_WORDS 1

/* Convert a packed BCD byte to decimal.
   Assumes high nibble and low nibble each represent digits 0..9.
   If invalid (i.e. a nibble >9) behavior is undefined or can be checked. */
U8BIT bcd_to_decimal8(U8BIT bcd)
{
    U8BIT hi = (bcd >> 4) & 0x0F;
    U8BIT lo = bcd & 0x0F;
    return hi * 10 + lo;
}

/*******************************************************************************
 * Name:    PressAKey
 *
 * Description:
 *
 *      Allows application to pause to allow screen contents to be read.
 *
 * In   none
 * Out  none
 ******************************************************************************/
static void PressAKey()
{
    /* flush input buffer */
    while (kbhit())
    {
        getchar();
    }

    printf("\nPress <ENTER> to continue...");

    /* flush the output buffer */
    fflush(stdout);

    while (!kbhit())
    {
        /* wait for keypress... */
        Sleep(10);
    }

    /* flush input buffer */
    while (kbhit())
    {
        getchar();
    }
}

/*******************************************************************************
 * Name:    PrintHeader
 *
 * Description:
 *
 *      Prints the sample program header.
 *
 * In   none
 * Out  none
 ******************************************************************************/
static void PrintHeader()
{
    U16BIT wLibVer;

    wLibVer = aceGetLibVersion();

    printf("*********************************************\n");
    printf("BU-69092 1553 Runtime Library               *\n");
    printf("Release Rev %d.%d.%d                           *\n",
            (wLibVer>>8),           /* Major*/
            (wLibVer&0xF0)>>4,      /* Minor*/
            (wLibVer&0xF));         /* Devel*/

    if ((wLibVer&0xF)!=0)
        printf("=-=-=-=-=-=-=-INTERIM VERSION-=-=-=-=-=-=-=-*\n");

    printf("Copyright (c) 2025 RTips Technologies  *\n");
    printf("*********************************************\n");
    printf("mti_irig_demo.c : IRIG-B time synchronisation demo *\n");
    printf("*********************************************\n\n");
    
    printf("Connect a IRIG-B source to the 1553 board for time sync to work\n");
}

/*******************************************************************************
 * Name:    PrintOutError
 *
 * Description:
 *
 *      This function prints out errors returned from library functions.
 *
 * In   result - the error number
 * Out  none
 ******************************************************************************/
static void PrintOutError(S16BIT nResult)
{
    char buf[80];

    aceErrorStr(nResult,buf,80);
    printf("RTL Function Failure-> %s.\n",buf);
}

/*******************************************************************************
 * Name:    ReadTimePkt
 *
 * Description:
 *
 *      Reads a time packet if available and prints information from it
 ******************************************************************************/
static void ReadTimePkt()
{
    char* pszSignalDetected;
    U16BIT* pu16TimeCntr;
    static S16BIT wResult = 0x0000;

    /* get Ch10TimePkt */
    wResult = aceMTIGetCh10TimePkt(DevNum, &pTimePkt, 0);

    /* make proper actions */
    switch (wResult)
    {
    case ACE_ERR_DATA_UNAVAILABLE:
    {
        break;
    }

    case ACE_ERR_SUCCESS:
    {
        /* only IRIG-B format is supported at this time */
        pu16TimeCntr = GET_MTI_CH10_TIME_PKT_TIME_CNTR_POINTER(pTimePkt);

#ifdef WIN32
        i64DeviceTime =
            ((__int64)pu16TimeCntr[0]) |
            ((__int64)pu16TimeCntr[1] << 16) |
            ((__int64)pu16TimeCntr[2] << 32);
#endif /* WIN32 */

#ifdef LINUX
        i64DeviceTime =
            ((S64BIT)pu16TimeCntr[0]) |
            ((S64BIT)pu16TimeCntr[1] << 16) |
            ((S64BIT)pu16TimeCntr[2] << 32);
#endif /* LINUX */

        pszSignalDetected = (pTimePkt->u32ChnlSpecificData & MTI_CH10_TIME_PKT_CHANNEL_DATA_SRC_EXT) ? "External (IRIG)" : "Internal";
        absDeviceTime.timeSource = (pTimePkt->u32ChnlSpecificData & MTI_CH10_TIME_PKT_CHANNEL_DATA_SRC_EXT);

#ifdef WIN32
        absDeviceTime.ulHours   = (ULONG)((ULONG)(pTimePkt->ullGlobalTime >> 40) & 0x0000007F);
        absDeviceTime.ulMinutes = (ULONG)((ULONG)(pTimePkt->ullGlobalTime >> 32) & 0x0000007F);
        absDeviceTime.ulSeconds = (ULONG)((ULONG)(pTimePkt->ullGlobalTime >> 56) & 0x0000007F);
        absDeviceTime.ulMilliSeconds = ((ULONG)((ULONG)(pTimePkt->ullGlobalTime >> 48) & 0x0000007F));
        absDeviceTime.ulYears   = (ULONG)((ULONG)(pTimePkt->ullGlobalTime) & 0x0000FFFF);
        absDeviceTime.ulDays    = (ULONG)((ULONG)(pTimePkt->ullGlobalTime >> 16) & 0x000000FF);
        absDeviceTime.ulMonths  = (ULONG)((ULONG)(pTimePkt->ullGlobalTime >> 24) & 0x0000001F);

        sprintf(szMessage,
            "\n*** Global Time=%016I64X, %02X/%02X/%04X, H:M:S=%02X:%02X:%02X:%02X, Relative Time Cntr=%I64d, (delta=%I64d), Source: %s\n",
            pTimePkt->ullGlobalTime, absDeviceTime.ulMonths, absDeviceTime.ulDays, absDeviceTime.ulYears, 
            absDeviceTime.ulHours, absDeviceTime.ulMinutes, absDeviceTime.ulSeconds, absDeviceTime.ulMilliSeconds,
            i64DeviceTime,
            (i64DeviceTime - i64PreviousDeviceTime),
            pszSignalDetected);
#endif /* WIN32 */

#ifdef LINUX
        absDeviceTime.ulHours = (long unsigned int)((long unsigned int)(pTimePkt->ullGlobalTime >> 24) & 0x0000007F);
        absDeviceTime.ulMinutes = (long unsigned int)((long unsigned int)(pTimePkt->ullGlobalTime >> 16) & 0x0000007F);
        absDeviceTime.ulSeconds = (long unsigned int)((long unsigned int)(pTimePkt->ullGlobalTime >> 8) & 0x0000007F);
        absDeviceTime.ulMilliSeconds = (long unsigned int)((long unsigned int)(pTimePkt->ullGlobalTime >> 0) & 0x0000007F);
        absDeviceTime.ulYears = (long unsigned int)((long unsigned int)(pTimePkt->ullGlobalTime >> 48) & 0x0000FFFF);
        absDeviceTime.ulDays = (long unsigned int)((long unsigned int)(pTimePkt->ullGlobalTime >> 32) & 0x000000FF);
        absDeviceTime.ulMonths = (long unsigned int)((long unsigned int)(pTimePkt->ullGlobalTime >> 40) & 0x0000001F);

        sprintf(szMessage,
            "\n*** Global Time=%016llX, %02lX/%02lX/%04lX, H:M:S=%02lX:%02lX:%02lX:%02lX, Relative Time Cntr=%lld, (delta=%lld), Source: %s\n",
            pTimePkt->ullGlobalTime, absDeviceTime.ulMonths, absDeviceTime.ulDays, absDeviceTime.ulYears, absDeviceTime.ulHours, absDeviceTime.ulMinutes, absDeviceTime.ulSeconds, absDeviceTime.ulMilliSeconds,
            i64DeviceTime,
            (i64DeviceTime - i64PreviousDeviceTime),
            pszSignalDetected);
#endif /* LINUX */

        printf("%s", szMessage);
        i64PreviousDeviceTime = i64DeviceTime;

        timeMsgTotalNum++;
        break;
    }

    case ACE_ERR_BUFFER_OVERFLOW:
    {
        printf("Time Pkt Status: ACE_ERR_BUFFER_OVERFLOW\n");
        break;
    }

    case ACE_ERR_BUFFER_UNAVAILABLE:
    {
        printf("Time Pkt Status: ACE_ERR_BUFFER_UNAVAILABLE\n");
        break;
    }

    default:
    {
        /* ignore */
        printf("Unknown error\n");
        break;
    }
    }

    /* Free packet, we do not care if there is an error here */
    aceMTIFreeCh10TimePkt(DevNum, &pTimePkt);
}


/*******************************************************************************
 * Name:    ThreadMsgGet
 *
 * Description:
 *
 *      A separate thread to get Ch10 message
 *
 * In   none
 * Out  none
 ******************************************************************************/
static void ThreadMsgGet()
{
	U16BIT *pData;
	S16BIT msgNumber = 1;
    U16BIT* p16Ptr;
    U16BIT* p16DataPktRelTimePtr;
    U16BIT* pEndofMsg;
    U32BIT u32NumMsgsInPacket;

	/* Wait for a little over one second for at least one IRIG-B packet to be received */
	Sleep(1250);

	/* Try and read a time packet if one is available */
	ReadTimePkt();

    while(!quit)
    {
        /* Try and read a time packet */
        ReadTimePkt();

        /* get a Ch10 Data Package */
        wResult=aceMTIGetCh10DataPkt(DevNum,&pPkt,timeout);

        /* make proper actions */
        switch (wResult)
        {
            case ACE_ERR_SUCCESS:
            {
                pcktNum++;
                totalPcktNum++;
                totalMsgNum += pPkt->u32ChnlSpecificData & 0x00FFFFFF;
				pData = &(pPkt->u16MsgData[0]);

                /* Verify IRIG and Data channel IDs. */
                if (pPkt->u16ChannelId != dataChnlId)
                {
                    printf("Retrieved Data Channel ID, %d, does not match setting %d!\n",pPkt->u16ChannelId, dataChnlId);
                }

                p16DataPktRelTimePtr = GET_MTI_CH10_PKT_TIME_CNTR_POINTER(pPkt);

                u32NumMsgsInPacket = 0;
                p16Ptr = (U16BIT*)GET_MTI_CH10_PKT_MSG_DATA_POINTER(pPkt);
                pEndofMsg = p16Ptr + pPkt->u32DataLength / 2;

                while (p16Ptr < pEndofMsg)
                {
                    printf("\n");
                    p16Ptr = DecodeCh10Msg(p16Ptr, p16DataPktRelTimePtr);

                    u32NumMsgsInPacket++;

                    if (u32NumMsgsInPacket >= (pPkt->u32ChnlSpecificData & 0x00FFFFFF))
                    {
                        break;
                    }
                }

                break;
            }
            case ACE_ERR_BUFFER_OVERFLOW:
                printf("Status: ACE_ERR_BUFFER_OVERFLOW\n");
                break;

            case ACE_ERR_BUFFER_UNAVAILABLE:
                printf("Status: ACE_ERR_BUFFER_UNAVAILABLE\n");
                break;

            default:
                /* ignore */
                break;
        }

        /*  Free packet
            we do not care if there is an error here
        */
        aceMTIFreeCh10DataPkt(DevNum, &pPkt);
        Sleep(5);
    }

}

/*******************************************************************************
 * Name:    DecodeCh10Msg
 *
 * Description:
 *
 *      This function decodes one 1553 message contained in the Ch10 Packet 
 *      and prints this info along with time stamps.
 *
 * In   ptr16           Ch10 packet binary data pointer
 * In   ptr16RelTime    Ch10 packet header Relative Time Counter pointer
 * Out  pointer to the next data to decode
 ******************************************************************************/
static U16BIT* DecodeCh10Msg
(
    U16BIT* ptr16,
    U16BIT* ptr16RelTime
)
{
    U16BIT j;
    U16BIT len = 0;
    U16BIT TT0, TT1, TT2, TT3;
    char szBuffer[100];
    U16BIT wRT, wTR1, wSA, wWC;
    U16BIT wBlkStatus;
    static U32BIT messageCount = 0;
    int deltaRtcCount = 0;  /* Difference between the time packets RTC and the CH10 packets RTC */
    float deltaMilliSeconds = 0; /* Above difference but in seconds instead of clock count */
    float correctedMilliSeconds = 0;
    int irigMilliSeconds;
    
#ifdef WIN32
    __int64 i64IntraPktTime;    /* 48-bit Relative Time Counter of this 1553 message */
    __int64 i64DataPktRelTime;  /* 48-bit Relative Time Counter of the CH10 packet containing this 1553 message */
    
    i64IntraPktTime =
        ((__int64)ptr16[0]) |
        ((__int64)ptr16[1] << 16) |
        ((__int64)ptr16[2] << 32);

    i64DataPktRelTime =
        ((__int64)ptr16RelTime[0]) |
        ((__int64)ptr16RelTime[1] << 16) |
        ((__int64)ptr16RelTime[2] << 32);
#endif /* WIN32 */

#ifdef LINUX
    S64BIT i64IntraPktTime;     /* 48-bit Relative Time Counter of this 1553 message */
    S64BIT i64DataPktRelTime;   /* 48-bit Relative Time Counter of the CH10 packet containing this 1553 message */
    
    i64IntraPktTime =
        ((S64BIT)ptr16[0]) |
        ((S64BIT)ptr16[1] << 16) |
        ((S64BIT)ptr16[2] << 32);

    i64DataPktRelTime =
        ((S64BIT)ptr16RelTime[0]) |
        ((S64BIT)ptr16RelTime[1] << 16) |
        ((S64BIT)ptr16RelTime[2] << 32);
#endif /* LINUX */

#ifdef WIN32
    printf("[Time Pkt RTC=%012I64X, Data Pkt RTC=%012I64X, Intra Pkt RTC=%012I64X]\n", i64DeviceTime, i64DataPktRelTime, i64IntraPktTime);
#endif

#ifdef LINUX
    printf("[Time Pkt RTC=%012llX, Data Pkt RTC=%012llX, Intra Pkt TT=%012llX]\n", i64DeviceTime, i64DataPktRelTime, i64IntraPktTime);
#endif

    /* Compute the absolute time stamp of this 1553 message by combining the time packet information with the 
        time tag information of this CH10 packet and this 1553 message */
    deltaRtcCount = i64IntraPktTime - i64DeviceTime;  /* Get the time delta between the occurrence of the last CH10 time packet
                                                            and this CH10 data packet */

    deltaMilliSeconds = (deltaRtcCount / 10000000.0) * 1000; /* Convert that to milli seconds assumes time tag resolution to be 100ns */

    /* CH10 format provides milliseconds as BCD - Hundreds of ms on upper nibble and tens of milliseconds in lower nibble. Convert 
       this to decimal from BCD. Multiply by 10 is needed as CH10 provides hundreds & tens ms only */
    irigMilliSeconds = bcd_to_decimal8(absDeviceTime.ulMilliSeconds) * 10; 
    
    /* adjust this to account for the deltaMilliseconds */
    correctedMilliSeconds = irigMilliSeconds + deltaMilliSeconds;
    
    printf("MSG# %07ld: Abs Time %02X/%02X/%04X, H:M:S:ms.us=%02X:%02X:%02X:%.03f", messageCount++,
        absDeviceTime.ulMonths, absDeviceTime.ulDays, absDeviceTime.ulYears,
        absDeviceTime.ulHours, absDeviceTime.ulMinutes, absDeviceTime.ulSeconds, 
        correctedMilliSeconds);

    TT0 = *ptr16;
    ptr16++;

    TT1 = *ptr16;
    ptr16++;

    TT2 = *ptr16;
    ptr16++;

    TT3 = *ptr16;
    ptr16++;

    printf("\n");

    /* print block status word */
    wBlkStatus = *ptr16;
    printf("BSW:0x%04x\t", *ptr16);
    ptr16++;

    /* print GAP */
    printf("GAP:0x%04x\t", *ptr16);
    ptr16++;

    /* print message length */
    len = *ptr16;
    printf("LEN:0x%02x\t", *ptr16);
    ptr16++;

    if ((wBlkStatus & 0x0600) != 0)
    {
        /* error detected, print data with parsing */
        printf("ERROR: BSW & 0x0600 != 0!\nCMD/DATA/STAT: ");

        for (j = 0; j < (len / 2); j++)
        {
#if PRINT_DATA_WORDS
            printf("0x%04x ", *ptr16);
            if (((j + 1) % 8) == 0)
            {
                printf("\n");
            }
#endif
            ptr16++;
        }
        ptr16--;
        printf("\n");
    }
    else
    {
        /* Parse message and print formatted display */
        aceCmdWordParse(*ptr16, &wRT, &wTR1, &wSA, &wWC);

        if (!wTR1)
        {
            /* BC-to-RT message */
            sprintf(szBuffer, "%02d-%c-%02d-%02d", wRT, wTR1 ? 'T' : 'R', wSA, wWC);
            printf("CMD1 %04X --> %s\n", *ptr16, szBuffer);

            ptr16++;

            /* Check if there is a 2nd command */
            if ((wBlkStatus & ACE_MT_BSW_RTRT) || (wBlkStatus & ACE_MT_BSW_RTRTERR_2CMD))
            {
                /* 2nd command*/
                aceCmdWordParse(*ptr16, &wRT, &wTR1, &wSA, &wWC);
                sprintf(szBuffer, "%02d-%c-%02d-%02d", wRT, wTR1 ? 'T' : 'R', wSA, wWC);
                printf("CMD2 %04X --> %s\n", *ptr16, szBuffer);

                ptr16++;

                printf("STAT: 0x%04x\n", *ptr16);

                ptr16++;
                len -= 4;
            }

#if PRINT_DATA_WORDS
            printf("DATA:\n");
#endif

            for (j = 0; j < (len / 2) - 2; j++)
            {
#if PRINT_DATA_WORDS
                printf("0x%04x ", *ptr16);
                if (((j + 1) % 8) == 0)
                {
                    printf("\n");
                }
#endif
                ptr16++;
            }
#if PRINT_DATA_WORDS
            printf("\n");
#endif
            printf("STAT: 0x%04x\n", *ptr16);
        }
        else
        {
            /* RT-to-BC*/
            sprintf(szBuffer, "%02d-%c-%02d-%02d", wRT, wTR1 ? 'T' : 'R', wSA, wWC);
            printf("CMD1 %04X --> %s\n", *ptr16, szBuffer);

            ptr16++;

            printf("STAT: 0x%04x\n", *ptr16);

            ptr16++;

#if PRINT_DATA_WORDS
            printf("DATA:\n");
#endif

            for (j = 0; j < (len / 2) - 2; j++)
            {
#if PRINT_DATA_WORDS
                printf("0x%04x ", *ptr16);
                if (((j + 1) % 8) == 0)
                {
                    printf("\n");
                }
#endif
                ptr16++;
            }
#if PRINT_DATA_WORDS
            printf("\n");
#endif
            ptr16--;
        }
    }
    ptr16++;

    return ptr16;
}

/*******************************************************************************
 * Name:    ThreadMsgRate
 *
 *  Description:
 *
 *      A separate thread to report message throughput
 *
 * In   none
 * Out  none
 ******************************************************************************/
static void ThreadMsgRate()
{
    int i;

    printf("\n");
    while (!quit)
    {
        /* wait for 10 seconds */
        for (i=0;i<100;i++)
        {
            Sleep(100);
            if (quit)
            {
                break;
            }
        }

        if( pcktNum != -1 )
        {
            if(pcktNum > 0)
            {
                printf("Dev-%d: %d pckts/sec or %d pckts in 10 seconds.", DevNum,pcktNum/10,pcktNum);
                printf(" Total %d pckts, %d msgs. \n", totalPcktNum, totalMsgNum);
                pcktNum=0;
            }
            else
            {
                printf("Dev-%d: No pckts read in the last 10 seconds.\n",DevNum);
            }
        }
    }/* while loop */
}

/*******************************************************************************
 * Name:    main
 *
 * Description:
 *
 *      Program entry point.
 *
 * In   none
 * Out  none
 ******************************************************************************/
#ifdef VX_WORKS
int mtidemo(void)
#else
int main(void)
#endif
{
    /* Variable Definition */
    HWVERSIONINFO structHWVersionInfo;
    S16BIT        wBufNum           = 16;
    S32BIT        dwPktPoolSize     = 0x20000;

    ACE_IRIG_TX strIRIGTxSet, strIRIGTxGet;
    U32BIT u32IRIGTxSupported = 0;
	int choice = -1;

    
#ifdef LINUX
    pthread_t     dwIdThreadMsgGet;
    pthread_t     dwIdThreadMsgRate;
    pthread_t*    hThreadMsgGet     = NULL;
    pthread_t*    hThreadMsgRate    = NULL;
#else
    DWORD         dwIdThreadMsgGet;
    DWORD         dwIdThreadMsgRate;

    HANDLE        hThreadMsgGet     = NULL;
    HANDLE        hThreadMsgRate    = NULL;
    HANDLE        hConsole          = NULL;

    /* Setup Windows Console Screen */
    hConsole = GetStdHandle(STD_OUTPUT_HANDLE);
    SetConsoleTitle("DDC AceXtreme RTL [MTiDemo.c - Console App MTi Demo]");
#endif

    /* initialize global vars */
    wResult           = 0x0000;
    DevNum            = 0x0000;
    timeout           = -1;  /* Ch10 Data retrieval wait for ever option */
    quit              = 0;
    pPkt  = NULL;
    totalPcktNum      = 0;    /* total number of packets */
    totalMsgNum       = 0;    /* total number of messages */
    pcktNum           = -1;   /* number of packets read in 10 sec interval */

	/* Open mti log file */
	pFile = fopen("mtistack.txt","w");


    /* Print out sample header */
    PrintHeader();

    /* Get Logical Device # */
    printf("\nSelect MTi Logical Device Number (0-31):\n");
    printf("> ");

    scanf("%hd", &DevNum);

    aceInitialize(DevNum,ACE_ACCESS_CARD,ACE_MODE_TEST,0,0,0);

    /* check to see if this device is MTI capable */
    wResult = aceGetHwVersionInfo(DevNum, &structHWVersionInfo);

    aceFree(DevNum);

    if ((structHWVersionInfo.dwCapabilities & HWVER_CAPABILITY_MTI) == 0)
    {
        printf("\nERROR: This device is not MTI capable\n");
        PressAKey();
        return(1);
    }

    /* Get Ch10 Data channel ID */
    printf("\nInput Ch10 Data channel ID:\n");
    printf("> ");

    scanf("%hd", &dataChnlId);

    /* Get Ch10 IRIG channel ID */
    printf("\nInput Ch10 IRIG channel ID:\n");
    printf("> ");

    scanf("%hd", &irigChnlId);

    /* OK to start */
    printf("MTi Demo launched on channel %d with no data wait time delay\n",DevNum);
    pcktNum=0;

    /* Initialize Device */
    wResult = aceInitialize(DevNum,ACE_ACCESS_CARD,ACE_MODE_MTI/*+ACE_NO_TT_RESET*/,0,0,0);
    if(wResult)
    {
        printf("aceInitialize Failed!");
        PrintOutError(wResult);
        PressAKey();
        return(1);
    }

    printf("\nStart IRIG-B transmitter (0 = No, 1 = Yes): ");
	scanf("%d", &choice);
	if(choice == 1)
	{
		/* Turn on the the external digital IRIG transmitter if the card has one. */
	    memset(&strIRIGTxGet, 0, sizeof(ACE_IRIG_TX));
	    wResult = aceGetIRIGTx(DevNum, &strIRIGTxGet);
	    if (wResult)
	    {
		    printf("aceGetIRIGTx Failed! wResult %d \n", wResult);
		    PrintOutError(wResult);
	    }
	    else
	    {
		    u32IRIGTxSupported = strIRIGTxGet.u16IRIGBTxSupported;

		    if (u32IRIGTxSupported == 1)
		    {
		        /* turn on IRIG transmitter */
		        strIRIGTxSet.u16Enable = TRUE;
		        strIRIGTxSet.u16Days = 156;
		        strIRIGTxSet.u16Hours = 4;
		        strIRIGTxSet.u16Minutes = 40;
		        strIRIGTxSet.u16Seconds = 0;
		        strIRIGTxSet.u16Year = 9;
		        strIRIGTxSet.u32Control = 0;

		        wResult = aceSetIRIGTx(DevNum, strIRIGTxSet);
		        if (wResult)
		        {
		            printf("aceSetIRIGTx Failed!\n");
		            PrintOutError(wResult);
		        }
                else
                {
                    printf("IRIG Transmitter started successfully.\n");
                }
		    }
		    else
		    {
		        printf("IRIG Transmitter not supported.\n");
		    }
	    }
	}
	
    /* Set Time Tag resolution to 100ns (default = 2uS) */
    wResult = aceSetTimeTagRes(DevNum, ACE_TT_100NS);
    if (wResult)
    {
        printf("aceInitialize Failed!");
        PrintOutError(wResult);
        PressAKey();
        return(1);
    }

    /* allocate memory for Data MTI packages */
    pPkt = (MTI_CH10_DATA_PKT*) malloc(dwPktPoolSize);
    if(pPkt == NULL)
    {
        printf("malloc pPkt Failed!");
        PrintOutError(wResult);
        goto EndSample;
    }

    /* Allocate memory for IRIG MTI packages */
    pTimePkt = (MTI_CH10_TIME_PKT*) malloc(dwPktPoolSize);
    if(pPkt == NULL)
    {
        printf("malloc IRIG pTimePkt Failed!");
        PrintOutError(wResult);
        goto EndSample;
    }

    /* CONFIGURE MT-I MODE HERE
     * Note TO VxWorks users, change FALSE to TRUE in aceMTIConfigure to enable
     * ZeroCopy buffers feature.  With ZeroCopy enabled, user buffer allocation is
     * not required.  Therefore, memory allocation/deallocation
     * for pPkt should be removed - use pPkt as a pointer only */
    wResult = aceMTIConfigure(DevNum,   /* logical device number                                                            */
                    0x20000,            /* Dev byte size set to 128 KB, some boards may not allocate more than this, e.g. Q-prime */
                    wBufNum,            /* number of available buffers to hold ch10 packets                                 */
                    40960,              /* size in bytes of each ch10 packet buffer - bufsize                               */
                    FALSE,              /* FALSE since user supplied buffer is used in Chapter 10 packet management         */
                    (40960 / 2),        /* num of words in packet - applies if MTI_NUM_WORDS enabled. set < (bufsize - 256) */
                    500,                /* number of messages in packet - applies if MTI_NUM_MSGS enabled                   */
                    1000,               /* time interval per packet - applies if MTI_TIME_INT enabled                       */
                    (MTI_TIME_MSG_TRIG_INT | MTI_NUM_WORDS | MTI_NUM_MSGS),
                    dataChnlId,         /* Ch10 Channel ID                                                                  */
                    0,0,0,              /* Reserved for future devlopment                                                   */
                    dwOptions           /* Options                                                                          */
                   );

    if(wResult != ACE_ERR_SUCCESS)
    {
        printf("aceMTIConfigure Failed!");
        PrintOutError(wResult);
        goto EndSample;
    }

    /* Set Irig Channel ID */
    wResult = aceMTISetCh10TimePktId(DevNum, irigChnlId);

    /* Start MTi */
    wResult = aceMTIStart(DevNum);
    if (wResult != ACE_ERR_SUCCESS)
    {
        printf("aceMTIStart Failed!");
        PrintOutError(wResult);
        goto EndSample;
    }

    /* Enable time packet */
    wResult = aceMTICh10TimePktEnable(DevNum, TRUE);
    
    /* create a thread to get Ch10 msgs */
    hThreadMsgGet  = CreateThread(NULL,                 /* default security attributes  */
                            0,                          /* use default stack size       */
                            (LPTHREAD_START_ROUTINE) ThreadMsgGet, /* thread function   */
                            NULL,                       /* no thread function argument  */
                            0,                          /* use default creation flags   */
                            &dwIdThreadMsgGet);

    if(hThreadMsgGet==NULL)
    {
        printf("CreateThread for hThreadMsgGet Failed!");
        PrintOutError(wResult);
        goto EndSample;
    }

    /* create a thread to print information periodically */
    hThreadMsgRate = CreateThread(NULL,                 /* default security attributes  */
                            0,                          /* use default stack size       */
                            (LPTHREAD_START_ROUTINE) ThreadMsgRate, /* thread function  */
                            NULL,                       /* no thread function argument  */
                            0,                          /* use default creation flags   */
                            &dwIdThreadMsgRate);

    if(hThreadMsgRate==NULL)
    {
        printf("CreateThread for hThreadMsgRate Failed!");
        PrintOutError(wResult);
        goto EndSample;
    }

    /* Waiting for Packets */
    while(1)
    {
        /* wait for 100 ms */
        Sleep(100);

        if(kbhit())
        {
            quit=1;
            break;
        }
    }
    Sleep(1000);
    getch();

EndSample:

    /* turn off IRIG Transmitter */
    if (u32IRIGTxSupported)
    {
        strIRIGTxSet.u16Enable = 0;
        aceSetIRIGTx(DevNum, strIRIGTxSet);
    }

    /* turn off interrupts */
    aceMTICh10TimePktEnable(DevNum, FALSE);
    
    /* Stop MTi */
    wResult=aceMTIStop(DevNum);
    if(wResult != ACE_ERR_SUCCESS)
    {
        printf("aceMTIStop Failed!");
        PrintOutError(wResult);
    }

    /* close ThreadMsgGet  */
    if(hThreadMsgGet)
    {
        if (CloseHandle(hThreadMsgGet))
        {
            hThreadMsgGet=NULL;
        }
    }

    /* close ThreadMsgRate */
    if(hThreadMsgRate)
    {
        if (CloseHandle(hThreadMsgRate))
        {
            hThreadMsgRate=NULL;
        }
    }

    /* free memory */
    if (pPkt)
    {
        free(pPkt);
        pPkt = NULL;
    }
    if (pTimePkt)
    {
        free(pTimePkt);
        pTimePkt = NULL;
    }

    /* free the card */
    wResult = aceFree(DevNum);
    if(wResult)
    {
        printf("aceFree failed!");
        PrintOutError(wResult);
        PressAKey();
    }

	fclose(pFile);

    printf("MTi Demo closed.\n");
    printf("\n----------------------------------------\n"
           "MTi Device Number:  %d\n"
           "Total Ch10 Packets: %d\n"
           "Total Messages:     %d\n"
           "----------------------------------------\n",DevNum, totalPcktNum, totalMsgNum);

    PressAKey();

    return 0;
}
