/*******************************************************************************
********************************************************************************
**
**  Copyright(c) 2022, Alliance for Automotive Innovation
**  Used only under license from the Alliance for Automotive Innovation. All Rights Reserved.
**
**  Project:  J1699-5
**  FileName: Test11_PerformanceCounters.c
**  Author:   EnGenius
**  Date:     2/25/2022
**  Email:    <support@autosinnovate.org>
**
**  Purpose:  SAE J1699-5 Vehicle OBD II Compliance Test Cases Source Code.
**            This source code is intended to run the tests described in
**            the SAE J1699-5 document in an automated manner, when compiled
**            and used with an SAE J2534-compatible pass-thru device.
**
**            File j1699.c contains information on building and running this test.
**
**  Description:
**
**  Modifications:  03/13/2023  Initial Version
**
********************************************************************************
*******************************************************************************/

#include <stdio.h>    // C Library input and output declarations
#include <stdlib.h>   // C Library general function declarations
#include <string.h>   // C Library character array declarations
#include <time.h>     // C Library time and date declarations
#include <conio.h>    // MS-DOS console input and output declarations
#include <windows.h>  // Windows API declarations
#include "j2534.h"    // j1699 project j2534 declarations
#include "j1699.h"    // j1699 project general declarations
#include "ScreenOutput.h"  // j1699 project console output declarations


// Function Prototypes
BOOL   VerifyIMReadiness      ( void);
BOOL   IsIM_ReadinessComplete ( PID *pPid );
STATUS EvaluateINF14          ( void );

STATUS VerifyAndEvaluateIPTData  ( BOOL bDisplayErrorMsg );
BOOL   ReadDataFromLogFile    ( const char *szTestSectionStart,
                                const char *szTestSectionEnd );
BOOL   ReadIPD                ( const char *szTestSectionEnd );
BOOL   CheckIPD               ( BYTE        EcuIdx );
STATUS EvaluateIPD            ( BYTE        EcuIdx,
                                DWORD      *pIPTStatusFlags,
                                BOOL        bDisplayErrorMsg );
BOOL   ReadDTCIUMPRData       ( const char* szTestSectionEnd );
STATUS EvaluateDTCIUMPRData   ( BYTE        EcuIdx,
                                DWORD      *pIPTStatusFlags,
                                BOOL        bDisplayErrorMsg );

STATUS RunDynamicTest11       ( BOOL          bDisplayCARBTimers,
                                unsigned long EngineStartTimestampMsecs );
void   PrintECUData           ( void );


extern STATUS VerifyINF14Data  ( BYTE  EcuIdx );

extern STATUS GetIPD           ( BYTE  EcuIdx );
extern STATUS VerifyIPD        ( void );

extern STATUS VerifyDTCExtData ( BYTE  ExtDataID );
extern STATUS GetDTCExtData    ( BYTE  ExtDataID,
                                 WORD  Index );

extern BOOL SaveDTCIUMPRData   ( BYTE  EcuIdx,
                                 WORD *pIndex );
extern void SaveIPD            ( BYTE  EcuIdx );

extern void   LogLastTransaction    ( void );     // copies last transaction from ring buffer to log file
extern void   DumpTransactionBuffer ( void );  // copies transaction ring buffer to log file
extern void   SaveTransactionStart  ( void );   // marks the start of a new transaction in the ring buffer


// Variable Definitions
#define IMREADINESS_SIZE        17  // Number of IM Readiness Entries displayed

typedef enum
{
	COMPLETE=0,
	INCOMPLETE=1,
	NOTSUPPORTED=2,
	DISABLED=3,
	INVALID=4
}IM_Status;
//IM_Status **Test11IMStatus;
IM_Status   Test11IMStatus[MAX_ECUS][IMREADINESS_SIZE];
IM_Status   eCurrentIMStatus[IMREADINESS_SIZE];

const char *szIM_Status[] =
{
	"Complete",
	"Incomplete",
	"Not Supported",
	"Disabled",
	"Invalid"
};

PID *pTest11_PIDF501 = NULL;

//WORD     **pIPD_Test11_5 = NULL;
//WORD     **pIPD_Test11_11 = NULL;
//WORD     **pIPD_Test11_CurrentDisplayData = NULL;

//DTCIUMPR **pDTCIUMPR_Test11_5 = NULL;
//DTCIUMPR **pDTCIUMPR_Test11_11 = NULL;
//DTCIUMPR **pDTCIUMPR_Test11_CurrentDisplayData = NULL;


typedef struct
{
	BOOL bCatBank1;
	BOOL bCatBank2;
	BOOL bO2Bank1;
	BOOL bO2Bank2;
	BOOL bSO2Bank1;
	BOOL bSO2Bank2;
	BOOL bNCAT;
	BOOL bNADS;
	BOOL bAFRIBank1;
	BOOL bAFRIBank2;
	BOOL bPFBank1;
	BOOL bPFBank2;
} BANK_SUPPORT;
static BANK_SUPPORT BankSupport[MAX_ECUS];

const char *szTest_Status[] =
{
	"Normal",
	"FAILURE"
};

//******************************************************************************
// Dynamic Test 11.x screen
//******************************************************************************
#define COL1            0
#define COL2            9
#define COL3            17
#define COL4            25
#define COL5            33

#define COL6            40
#define COL7            49
#define COL8            57
#define COL9            65
#define COL10           73

#define COL_ECUPG       (COL1+8)
#define COL_ECU         (COL1+12)
#define COL_DATPG       (COL1+9)
#define SPACE           9

#define ECU_PG_ROW       0
#define ECU_NUM_ROW      0
#define ECU_ID_ROW       1
#define ECU_STATUS_ROW   2
#define DAT_PG_ROW       3
#define STATUS_ROW       4
#define CARB_CYCLE_ROW  22
#define TEST_STATUS_ROW 23
#define PRESS_ESC_ROW   24

#define STRING_WIDTH    13
#define NUMERIC_WIDTH   5
#define HEX_WIDTH       4
#define ECU_WIDTH       8

#define IUMPR_1_COL_LEN 13
#define IUMPR_2_COL_LEN 16


StaticTextElement  _string_elements_11[] =
{
	// ECU List
	{"ECU Pg:",     COL1,                ECU_PG_ROW},      // 0
	{"ECU List:",   COL1,                ECU_ID_ROW},      // 1
	{"ECU Status:", COL1,                ECU_STATUS_ROW},  // 2
	{"Data Pg:",    COL1,                DAT_PG_ROW},      // 3
	{"1",           COL_ECU + 0 * SPACE, ECU_NUM_ROW},     // 4
	{"2",           COL_ECU + 1 * SPACE, ECU_NUM_ROW},     // 5
	{"3",           COL_ECU + 2 * SPACE, ECU_NUM_ROW},     // 6
	{"4",           COL_ECU + 3 * SPACE, ECU_NUM_ROW},     // 7
	{"5",           COL_ECU + 4 * SPACE, ECU_NUM_ROW},     // 8
	{"6",           COL_ECU + 5 * SPACE, ECU_NUM_ROW},     // 9
	{"7",           COL_ECU + 6 * SPACE, ECU_NUM_ROW},     // 10
	{"8",           COL_ECU + 7 * SPACE, ECU_NUM_ROW},     // 11

	// Test Status
	{"Test Status:",                                                                    COL1, TEST_STATUS_ROW}, // 12
	{"Press ESC to exit, F to FAIL, a # to change ECU, PGUP/PGDN to change ECU page,",  COL1, PRESS_ESC_ROW},   // 13
	{"or L/R Arrow to change Data page",                                                COL1, PRESS_ESC_ROW+1}, // 14
//	{"",                                                                                0,    PRESS_ESC_ROW+1}  // 15
};
const int _num_string_elements_11 = sizeof ( _string_elements_11 )/sizeof ( _string_elements_11[0] );


StaticTextElement  _string_elements_imr[] =
{
	// IM Readiness
	{"I/M STATUS",           COL1, STATUS_ROW+0},  // 0
	{"MISFIRE",              COL1, STATUS_ROW+1},  // 1
	{"FUEL",                 COL1, STATUS_ROW+2},  // 2
	{"CCM",                  COL1, STATUS_ROW+3},  // 3
	{"COOL",                 COL1, STATUS_ROW+4},  // 4
	{"G/D SPECIFIC",         COL1, STATUS_ROW+5},  // 5, see _string_elements11_Gas and _string_elements11_Deisel respectively for STATUS_ROW+5
	{"G/D SPECIFIC",         COL1, STATUS_ROW+6},  // 6, see _string_elements11_Gas and _string_elements11_Deisel respectively for STATUS_ROW+6
	{"G/D SPECIFIC",         COL1, STATUS_ROW+7},  // 7, see _string_elements11_Gas and _string_elements11_Deisel respectively for STATUS_ROW+7
	{"G/D SPECIFIC",         COL1, STATUS_ROW+8},  // 8, see _string_elements11_Gas and _string_elements11_Deisel respectively for STATUS_ROW+8
	{"PF",                   COL1, STATUS_ROW+9},  // 9
	{"EGS",                  COL1, STATUS_ROW+10}, // 10
	{"CV",                   COL1, STATUS_ROW+11}, // 11
	{"EGR",                  COL1, STATUS_ROW+12}, // 12
	{"VVT",                  COL1, STATUS_ROW+13}, // 13
	{"DOR",                  COL1, STATUS_ROW+14}, // 14
	{"CSER",                 COL1, STATUS_ROW+15}, // 15
	{"G/D SPECIFIC",         COL1, STATUS_ROW+16}, // 16, see _string_elements11_Gas and _string_elements11_Deisel respectively for STATUS_ROW+16
	{"OTH",                  COL1, STATUS_ROW+17}, // 17
};
const int _num_string_elements_imr = sizeof ( _string_elements_imr )/sizeof ( _string_elements_imr[0] );

#define IMSTATUS_ELEMENT_INDEX            0
#define IMSTATUS_GD_ELEMENT1_START_INDEX  5
#define IMSTATUS_GD_ELEMENT1_END_INDEX    8
#define IMSTATUS_GD_ELEMENT2_INDEX        16


StaticTextElement  _string_elements_iumpr1[] =
{
	// counters and column headings
	{"RATE BASED COUNTERS",  COL6, STATUS_ROW+0},  // 0
	{"OBDCOND",              COL6, STATUS_ROW+1},  // 1
	{"IGNCNTR",              COL6, STATUS_ROW+2},  // 2

	{"Initial",              COL7, STATUS_ROW+3},  // 3
	{"Current",              COL9, STATUS_ROW+3},  // 4

	{"N",                    COL7, STATUS_ROW+4},  // 5
	{"D",                    COL8, STATUS_ROW+4},  // 6

	{"N",                    COL9, STATUS_ROW+4},  // 7
	{"D",                    COL10, STATUS_ROW+4}, // 8

	// DTCIUMPR, N/D Initial/Current Value columns
	// COL6, STATUS_ROW+5 thru +17  // DTC
};
const int _num_string_elements_iumpr1 = sizeof ( _string_elements_iumpr1 )/sizeof ( _string_elements_iumpr1[0] );

#define IPDSTATUS_ELEMENT_INDEX            0


StaticTextElement  _string_elements_iumpr2[] =
{
	// DTCIUMPR, N/D Initial/Current Heading columns
	{"Initial",     COL2, STATUS_ROW+0},            // 0
	{"Current",     COL4, STATUS_ROW+0},            // 1

	{"N",           COL2, STATUS_ROW+1},            // 2
	{"D",           COL3, STATUS_ROW+1},            // 3

	{"N",           COL4, STATUS_ROW+1},            // 4
	{"D",           COL5, STATUS_ROW+1},            // 5

	// DTCIUMPR, N/D Initial/Current Value columns
	// COL1, STATUS_ROW+2 thru +17  // DTC

	// DTCIUMPR, N/D Initial/Current Heading columns
	{"Initial",     COL7, STATUS_ROW+0},            // 6
	{"Current",     COL9, STATUS_ROW+0},            // 7

	{"N",           COL7, STATUS_ROW+1},            // 8
	{"D",           COL8, STATUS_ROW+1},            // 9

	{"N",           COL9, STATUS_ROW+1},            // 10
	{"D",           COL10, STATUS_ROW+1}          , // 11

	// DTCIUMPR, N/D Initial/Current Value columns
	// COL6, STATUS_ROW+2 thru +17  // DTC
};
const int _num_string_elements_iumpr2 = sizeof ( _string_elements_iumpr2 )/sizeof ( _string_elements_iumpr2[0] );


StaticTextElement  _string_elements11_Gas[] =
{
	// column 1
	{"CAT",       COL1, STATUS_ROW+5},
	{"HCAT",      COL1, STATUS_ROW+6},
	{"EVAP",      COL1, STATUS_ROW+7},
	{"AIR",       COL1, STATUS_ROW+8},

	{"reserved",  COL1, STATUS_ROW+16},
};
const int _num_string_elements11_Gas = sizeof ( _string_elements11_Gas )/sizeof ( _string_elements11_Gas[0] );

StaticTextElement  _string_elements11_Diesel[] =
{
	// column 1
	{"HCCAT",     COL1, STATUS_ROW+5},
	{"NCAT",      COL1, STATUS_ROW+6},
	{"reserved",  COL1, STATUS_ROW+7},
	{"BP",        COL1, STATUS_ROW+8},

	{"NAC",       COL1, STATUS_ROW+16},
};
const int _num_string_elements11_Diesel = sizeof ( _string_elements11_Diesel )/sizeof ( _string_elements11_Diesel[0] );

StaticTextElement  _string_elements_timers[] =
{
	{"Cont. Idle (Sec.):",  COL1, CARB_CYCLE_ROW},
	{"At Speed (Sec.):",      30, CARB_CYCLE_ROW},
	{"Run Time (Sec.):",      56, CARB_CYCLE_ROW}
};
const int _num_string_elements_timers = sizeof ( _string_elements_timers )/sizeof ( _string_elements_timers[0] );


DynamicValueElement  _dynamic_elements_11[] =
{
	// ECU Page
	{COL_ECUPG,           ECU_PG_ROW,     1},           // 0   Curent ECU Page
	{COL_DATPG,           DAT_PG_ROW,     1},           // 1   Curent Data Page

	// ECU ID columns
	{COL_ECU + 0 * SPACE, ECU_ID_ROW,     ECU_WIDTH},   // 2   ECU 1
	{COL_ECU + 1 * SPACE, ECU_ID_ROW,     ECU_WIDTH},   // 3   ECU 2
	{COL_ECU + 2 * SPACE, ECU_ID_ROW,     ECU_WIDTH},   // 4   ECU 3
	{COL_ECU + 3 * SPACE, ECU_ID_ROW,     ECU_WIDTH},   // 5   ECU 4
	{COL_ECU + 4 * SPACE, ECU_ID_ROW,     ECU_WIDTH},   // 6   ECU 5
	{COL_ECU + 5 * SPACE, ECU_ID_ROW,     ECU_WIDTH},   // 7   ECU 6
	{COL_ECU + 6 * SPACE, ECU_ID_ROW,     ECU_WIDTH},   // 8   ECU 7
	{COL_ECU + 7 * SPACE, ECU_ID_ROW,     ECU_WIDTH},   // 9   ECU 8

	// ECU status columns
	{COL_ECU + 0 * SPACE, ECU_STATUS_ROW, HEX_WIDTH},   // 10  ECU Status 1
	{COL_ECU + 1 * SPACE, ECU_STATUS_ROW, HEX_WIDTH},   // 11  ECU Status 2
	{COL_ECU + 2 * SPACE, ECU_STATUS_ROW, HEX_WIDTH},   // 12  ECU Status 3
	{COL_ECU + 3 * SPACE, ECU_STATUS_ROW, HEX_WIDTH},   // 13  ECU Status 4
	{COL_ECU + 4 * SPACE, ECU_STATUS_ROW, HEX_WIDTH},   // 14  ECU Status 5
	{COL_ECU + 5 * SPACE, ECU_STATUS_ROW, HEX_WIDTH},   // 15  ECU Status 6
	{COL_ECU + 6 * SPACE, ECU_STATUS_ROW, HEX_WIDTH},   // 16  ECU Status 7
	{COL_ECU + 7 * SPACE, ECU_STATUS_ROW, HEX_WIDTH},   // 17  ECU Status 8

	// Test Status
	{14,                  TEST_STATUS_ROW, STRING_WIDTH},  // 18 Test Status
	{COL4,                TEST_STATUS_ROW, STRING_WIDTH}   // 19 DTC Status
};
const int _num_dynamic_elements_11 = sizeof ( _dynamic_elements_11 )/sizeof ( _dynamic_elements_11[0] );

// Indexes common to dynamic elements Page1 and Page2+
#define ECU_PAGE_INDEX              0
#define DATA_PAGE_INDEX             1
#define ECU_ID_INDEX                2
#define ECU_STATUS_INDEX            10

#define TESTSTATUS_INDEX      18
// Normal / FAILURE                 18
// blank / DTC Detected             19


// dynamic IM Readiness elements (Data Page 1 only)
DynamicValueElement  _dynamic_elements_imr[] =
{
	// IM Readiness status columns                Gas      / Diesel
	{COL2, STATUS_ROW+1,  STRING_WIDTH},   // 0   MISFIRE  / MISFIRE
	{COL2, STATUS_ROW+2,  STRING_WIDTH},   // 1   FUEL     / FUEL
	{COL2, STATUS_ROW+3,  STRING_WIDTH},   // 2   CCM      / CCM
	{COL2, STATUS_ROW+4,  STRING_WIDTH},   // 3   COOL     / COOL
	{COL2, STATUS_ROW+5,  STRING_WIDTH},   // 4   CAT      / HCCAT
	{COL2, STATUS_ROW+6,  STRING_WIDTH},   // 5   HCAT     / NCAT
	{COL2, STATUS_ROW+7,  STRING_WIDTH},   // 6   EVAP     / reserved
	{COL2, STATUS_ROW+8,  STRING_WIDTH},   // 7   AIR      / BP
	{COL2, STATUS_ROW+9,  STRING_WIDTH},   // 8   PF       / PF
	{COL2, STATUS_ROW+10, STRING_WIDTH},   // 9   EGS      / EGS
	{COL2, STATUS_ROW+11, STRING_WIDTH},   // 10  CV       / CV
	{COL2, STATUS_ROW+12, STRING_WIDTH},   // 11  EGR      / EGR
	{COL2, STATUS_ROW+13, STRING_WIDTH},   // 12  VVT      / VVT
	{COL2, STATUS_ROW+14, STRING_WIDTH},   // 13  DOR      / DOR
	{COL2, STATUS_ROW+15, STRING_WIDTH},   // 14  CSER     / CSER
	{COL2, STATUS_ROW+16, STRING_WIDTH},   // 15  reserved / NAC
	{COL2, STATUS_ROW+17, STRING_WIDTH},   // 16  OTH      / OTH
};
const int _num_dynamic_elements_imr = sizeof ( _dynamic_elements_imr )/sizeof ( _dynamic_elements_imr[0] );

// Indexes for dynamic IM Readiness elements (Data Page 1 only)
#define IM_READINESS_INDEX          0
// MISFIRE     / MISFIRE            0
// FUEL        / FUEL               1
// CCM         / CCM                2
// COOL        / COOL               3
// CAT         / HCCAT              4
// HCAT        / NCAT               5
// EVAP        / reserved           6
// AIR         / BP                 7
// PF          / PF                 8
// EGS         / EGS                9
// CV          / CV                 10
// EGR         / EGR                11
// VVT         / VVT                12
// DOR         / DOR                13
// CSER        / CSER               14
// reserved    / NAC                15
// OTH         / OTH                16


// dynamic IUMPR elements (Data Page 1 only)
DynamicValueElement  _dynamic_elements_iumpr1[] =
{
	// IPD current value columns
	{COL8, STATUS_ROW+1, NUMERIC_WIDTH},    // 0   OBD Monitoring Conditions
	{COL8, STATUS_ROW+2, NUMERIC_WIDTH},    // 1   Ignition Counter

	// DTCIUMPR DTC column
	{COL6, STATUS_ROW+5,  NUMERIC_WIDTH},   // 2   #1 DTC
	{COL6, STATUS_ROW+6,  NUMERIC_WIDTH},   // 3   #2 DTC
	{COL6, STATUS_ROW+7,  NUMERIC_WIDTH},   // 4   #3 DTC
	{COL6, STATUS_ROW+8,  NUMERIC_WIDTH},   // 5   #4 DTC
	{COL6, STATUS_ROW+9,  NUMERIC_WIDTH},   // 6   #5 DTC
	{COL6, STATUS_ROW+10, NUMERIC_WIDTH},   // 7   #6 DTC
	{COL6, STATUS_ROW+11, NUMERIC_WIDTH},   // 8   #7 DTC
	{COL6, STATUS_ROW+12, NUMERIC_WIDTH},   // 9   #8 DTC
	{COL6, STATUS_ROW+13, NUMERIC_WIDTH},   // 10  #9 DTC
	{COL6, STATUS_ROW+14, NUMERIC_WIDTH},   // 11  #10 DTC
	{COL6, STATUS_ROW+15, NUMERIC_WIDTH},   // 12  #11 DTC
	{COL6, STATUS_ROW+16, NUMERIC_WIDTH},   // 13  #12 DTC
	{COL6, STATUS_ROW+17, NUMERIC_WIDTH},   // 14  #13 DTC

	// DTCIUMPR initial value columns
	{COL7, STATUS_ROW+5,  NUMERIC_WIDTH},   // 15  #1 Numerator
	{COL8, STATUS_ROW+5,  NUMERIC_WIDTH},   // 16  #1 Denominator

	{COL7, STATUS_ROW+6,  NUMERIC_WIDTH},   // 17  #2 Numerator
	{COL8, STATUS_ROW+6,  NUMERIC_WIDTH},   // 18  #2 Denominator

	{COL7, STATUS_ROW+7,  NUMERIC_WIDTH},   // 19  #3 Numerator
	{COL8, STATUS_ROW+7,  NUMERIC_WIDTH},   // 20  #3 Denominator

	{COL7, STATUS_ROW+8,  NUMERIC_WIDTH},   // 21  #4 Numerator
	{COL8, STATUS_ROW+8,  NUMERIC_WIDTH},   // 22  #4 Denominator

	{COL7, STATUS_ROW+9,  NUMERIC_WIDTH},   // 23  #5 Numerator
	{COL8, STATUS_ROW+9,  NUMERIC_WIDTH},   // 24  #5 Denominator

	{COL7, STATUS_ROW+10, NUMERIC_WIDTH},   // 25  #6 Numerator
	{COL8, STATUS_ROW+10, NUMERIC_WIDTH},   // 26  #6 Denominator

	{COL7, STATUS_ROW+11, NUMERIC_WIDTH},   // 27  #7 Numerator
	{COL8, STATUS_ROW+11, NUMERIC_WIDTH},   // 28  #7 Denominator

	{COL7, STATUS_ROW+12, NUMERIC_WIDTH},   // 29  #8 Numerator
	{COL8, STATUS_ROW+12, NUMERIC_WIDTH},   // 30  #8 Denominator

	{COL7, STATUS_ROW+13, NUMERIC_WIDTH},   // 31  #9 Numerator
	{COL8, STATUS_ROW+13, NUMERIC_WIDTH},   // 32  #9 Denominator

	{COL7, STATUS_ROW+14, NUMERIC_WIDTH},   // 33  #10 Numerator
	{COL8, STATUS_ROW+14, NUMERIC_WIDTH},   // 34  #10 Denominator

	{COL7, STATUS_ROW+15, NUMERIC_WIDTH},   // 35  #19 Numerator
	{COL8, STATUS_ROW+15, NUMERIC_WIDTH},   // 36  #19 Denominator

	{COL7, STATUS_ROW+16, NUMERIC_WIDTH},   // 37  #12 Numerator
	{COL8, STATUS_ROW+16, NUMERIC_WIDTH},   // 38  #12 Denominator

	{COL7, STATUS_ROW+17, NUMERIC_WIDTH},   // 39  #13 Numerator
	{COL8, STATUS_ROW+17, NUMERIC_WIDTH},   // 40  #13 Denominator

	// DTCIUMPR current value columns
	{COL9,  STATUS_ROW+5,  NUMERIC_WIDTH},  // 41  #1 Numerator
	{COL10, STATUS_ROW+5,  NUMERIC_WIDTH},  // 42  #1 Denominator

	{COL9,  STATUS_ROW+6,  NUMERIC_WIDTH},  // 43  #2 Numerator
	{COL10, STATUS_ROW+6,  NUMERIC_WIDTH},  // 44  #2 Denominator

	{COL9,  STATUS_ROW+7,  NUMERIC_WIDTH},  // 45  #3 Numerator
	{COL10, STATUS_ROW+7,  NUMERIC_WIDTH},  // 46  #3 Denominator

	{COL9,  STATUS_ROW+8,  NUMERIC_WIDTH},  // 47  #4 Numerator
	{COL10, STATUS_ROW+8,  NUMERIC_WIDTH},  // 48  #4 Denominator

	{COL9,  STATUS_ROW+9,  NUMERIC_WIDTH},  // 49  #5 Numerator
	{COL10, STATUS_ROW+9,  NUMERIC_WIDTH},  // 50  #5 Denominator

	{COL9,  STATUS_ROW+10, NUMERIC_WIDTH},  // 51  #6 Numerator
	{COL10, STATUS_ROW+10, NUMERIC_WIDTH},  // 52  #6 Denominator

	{COL9,  STATUS_ROW+11, NUMERIC_WIDTH},  // 53  #7 Numerator
	{COL10, STATUS_ROW+11, NUMERIC_WIDTH},  // 54  #7 Denominator

	{COL9,  STATUS_ROW+12, NUMERIC_WIDTH},  // 55  #8 Numerator
	{COL10, STATUS_ROW+12, NUMERIC_WIDTH},  // 56  #8 Denominator

	{COL9,  STATUS_ROW+13, NUMERIC_WIDTH},  // 57  #8 Numerator
	{COL10, STATUS_ROW+13, NUMERIC_WIDTH},  // 58  #9 Denominator

	{COL9,  STATUS_ROW+14, NUMERIC_WIDTH},  // 59  #10 Numerator
	{COL10, STATUS_ROW+14, NUMERIC_WIDTH},  // 60  #10 Denominator

	{COL9,  STATUS_ROW+15, NUMERIC_WIDTH},  // 61  #11 Numerator
	{COL10, STATUS_ROW+15, NUMERIC_WIDTH},  // 62  #11 Denominator

	{COL9,  STATUS_ROW+16, NUMERIC_WIDTH},  // 63  #12 Numerator
	{COL10, STATUS_ROW+16, NUMERIC_WIDTH},  // 64  #12 Denominator

	{COL9,  STATUS_ROW+17, NUMERIC_WIDTH},  // 65 #13 Numerator
	{COL10, STATUS_ROW+17, NUMERIC_WIDTH},  // 66 #13 Denominator
};
const int _num_dynamic_elements_iumpr1 = sizeof ( _dynamic_elements_iumpr1 )/sizeof ( _dynamic_elements_iumpr1[0] );

// Indexes for dynamic IUMPR elements (Data Page 1 only)
#define OBD_MONITOR_COND_INDEX      0
#define IGNITION_COUNTER_INDEX      1

#define IUMPR_DTC_INDEX             2
// #1 DTC                           3
// . . .
// #13 DTC                          14

#define IUMPR_INIT_INDEX            15
// #1 Numerator                     15
// #1 Denominator                   16
// . . .
// #13 Numerator                    39
// #13 Denominator                  40

#define IUMPR_CUR_INDEX             41
// #1 Numerator                     41
// #1 Denominator                   42
// . . .
// #13 Numerator                    65
// #13 Denominator                  66


// dynamic IUMPR elements (Data Page 2+)
DynamicValueElement  _dynamic_elements_iumpr2[] =
{
	// 1st DTCIUMPR DTC column
	{COL1, STATUS_ROW+2,  NUMERIC_WIDTH},  // 0   #14 DTC
	{COL1, STATUS_ROW+3,  NUMERIC_WIDTH},  // 1   #15 DTC
	{COL1, STATUS_ROW+4,  NUMERIC_WIDTH},  // 2   #16 DTC
	{COL1, STATUS_ROW+5,  NUMERIC_WIDTH},  // 3   #17 DTC
	{COL1, STATUS_ROW+6,  NUMERIC_WIDTH},  // 4   #18 DTC
	{COL1, STATUS_ROW+7,  NUMERIC_WIDTH},  // 5   #19 DTC
	{COL1, STATUS_ROW+8,  NUMERIC_WIDTH},  // 6   #20 DTC
	{COL1, STATUS_ROW+9,  NUMERIC_WIDTH},  // 7   #21 DTC
	{COL1, STATUS_ROW+10, NUMERIC_WIDTH},  // 8   #22 DTC
	{COL1, STATUS_ROW+11, NUMERIC_WIDTH},  // 9   #23 DTC
	{COL1, STATUS_ROW+12, NUMERIC_WIDTH},  // 10  #24 DTC
	{COL1, STATUS_ROW+13, NUMERIC_WIDTH},  // 11  #25 DTC
	{COL1, STATUS_ROW+14, NUMERIC_WIDTH},  // 12  #26 DTC
	{COL1, STATUS_ROW+15, NUMERIC_WIDTH},  // 13  #27 DTC
	{COL1, STATUS_ROW+16, NUMERIC_WIDTH},  // 14  #28 DTC
	{COL1, STATUS_ROW+17, NUMERIC_WIDTH},  // 15  #29 DTC

	// 1st DTCIUMPR initial value columns
	{COL2, STATUS_ROW+2,  NUMERIC_WIDTH},  // 16  #14 Numerator
	{COL3, STATUS_ROW+2,  NUMERIC_WIDTH},  // 17  #14 Denominator

	{COL2, STATUS_ROW+3,  NUMERIC_WIDTH},  // 18  #15 Numerator
	{COL3, STATUS_ROW+3,  NUMERIC_WIDTH},  // 19  #15 Denominator

	{COL2, STATUS_ROW+4,  NUMERIC_WIDTH},  // 20  #16 Numerator
	{COL3, STATUS_ROW+4,  NUMERIC_WIDTH},  // 21  #16 Denominator

	{COL2, STATUS_ROW+5,  NUMERIC_WIDTH},  // 22  #17 Numerator
	{COL3, STATUS_ROW+5,  NUMERIC_WIDTH},  // 23  #17 Denominator

	{COL2, STATUS_ROW+6,  NUMERIC_WIDTH},  // 24  #18 Numerator
	{COL3, STATUS_ROW+6,  NUMERIC_WIDTH},  // 25  #18 Denominator

	{COL2, STATUS_ROW+7,  NUMERIC_WIDTH},  // 26  #19 Numerator
	{COL3, STATUS_ROW+7,  NUMERIC_WIDTH},  // 27  #19 Denominator

	{COL2, STATUS_ROW+8,  NUMERIC_WIDTH},  // 28  #20 Numerator
	{COL3, STATUS_ROW+8,  NUMERIC_WIDTH},  // 29  #20 Denominator

	{COL2, STATUS_ROW+9,  NUMERIC_WIDTH},  // 30  #21 Numerator
	{COL3, STATUS_ROW+9,  NUMERIC_WIDTH},  // 31  #21 Denominator

	{COL2, STATUS_ROW+10, NUMERIC_WIDTH},  // 32  #22 Numerator
	{COL3, STATUS_ROW+10, NUMERIC_WIDTH},  // 33  #22 Denominator

	{COL2, STATUS_ROW+11, NUMERIC_WIDTH},  // 34  #23 Numerator
	{COL3, STATUS_ROW+11, NUMERIC_WIDTH},  // 35  #23 Denominator

	{COL2, STATUS_ROW+12, NUMERIC_WIDTH},  // 36  #24 Numerator
	{COL3, STATUS_ROW+12, NUMERIC_WIDTH},  // 37  #24 Denominator

	{COL2, STATUS_ROW+13, NUMERIC_WIDTH},  // 38  #25 Numerator
	{COL3, STATUS_ROW+13, NUMERIC_WIDTH},  // 39  #25 Denominator

	{COL2, STATUS_ROW+14, NUMERIC_WIDTH},  // 40  #26 Numerator
	{COL3, STATUS_ROW+14, NUMERIC_WIDTH},  // 41  #26 Denominator

	{COL2, STATUS_ROW+15, NUMERIC_WIDTH},  // 42  #27 Numerator
	{COL3, STATUS_ROW+15, NUMERIC_WIDTH},  // 43  #27 Denominator

	{COL2, STATUS_ROW+16, NUMERIC_WIDTH},  // 44  #28 Numerator
	{COL3, STATUS_ROW+16, NUMERIC_WIDTH},  // 45  #28 Denominator

	{COL2, STATUS_ROW+17, NUMERIC_WIDTH},  // 46  #29 Numerator
	{COL3, STATUS_ROW+17, NUMERIC_WIDTH},  // 47  #29 Denominator

	// 1st DTCIUMPR current value columns
	{COL4, STATUS_ROW+2,  NUMERIC_WIDTH},  // 48  #14 Numerator
	{COL5, STATUS_ROW+2,  NUMERIC_WIDTH},  // 49  #14 Denominator

	{COL4, STATUS_ROW+3,  NUMERIC_WIDTH},  // 50  #15 Numerator
	{COL5, STATUS_ROW+3,  NUMERIC_WIDTH},  // 51  #15 Denominator

	{COL4, STATUS_ROW+4,  NUMERIC_WIDTH},  // 52  #16 Numerator
	{COL5, STATUS_ROW+4,  NUMERIC_WIDTH},  // 53  #16 Denominator

	{COL4, STATUS_ROW+5,  NUMERIC_WIDTH},  // 54  #17 Numerator
	{COL5, STATUS_ROW+5,  NUMERIC_WIDTH},  // 55  #17 Denominator

	{COL4, STATUS_ROW+6,  NUMERIC_WIDTH},  // 56  #18 Numerator
	{COL5, STATUS_ROW+6,  NUMERIC_WIDTH},  // 57  #18 Denominator

	{COL4, STATUS_ROW+7,  NUMERIC_WIDTH},  // 58  #19 Numerator
	{COL5, STATUS_ROW+7,  NUMERIC_WIDTH},  // 59  #19 Denominator

	{COL4, STATUS_ROW+8,  NUMERIC_WIDTH},  // 60  #20 Numerator
	{COL5, STATUS_ROW+8,  NUMERIC_WIDTH},  // 61  #20 Denominator

	{COL4, STATUS_ROW+9,  NUMERIC_WIDTH},  // 62  #21 Numerator
	{COL5, STATUS_ROW+9,  NUMERIC_WIDTH},  // 63  #21 Denominator

	{COL4, STATUS_ROW+10, NUMERIC_WIDTH},  // 64  #22 Numerator
	{COL5, STATUS_ROW+10, NUMERIC_WIDTH},  // 65  #22 Denominator

	{COL4, STATUS_ROW+11, NUMERIC_WIDTH},  // 66  23 Numerator
	{COL5, STATUS_ROW+11, NUMERIC_WIDTH},  // 67  #23 Denominator

	{COL4, STATUS_ROW+12, NUMERIC_WIDTH},  // 68  #24 Numerator
	{COL5, STATUS_ROW+12, NUMERIC_WIDTH},  // 69  #24 Denominator

	{COL4, STATUS_ROW+13, NUMERIC_WIDTH},  // 70  #25 Numerator
	{COL5, STATUS_ROW+13, NUMERIC_WIDTH},  // 71  #25 Denominator

	{COL4, STATUS_ROW+14, NUMERIC_WIDTH},  // 72  #26 Numerator
	{COL5, STATUS_ROW+14, NUMERIC_WIDTH},  // 73  #26 Denominator

	{COL4, STATUS_ROW+15, NUMERIC_WIDTH},  // 74  #27 Numerator
	{COL5, STATUS_ROW+15, NUMERIC_WIDTH},  // 75  #27 Denominator

	{COL4, STATUS_ROW+16, NUMERIC_WIDTH},  // 76  #28 Numerator
	{COL5, STATUS_ROW+16, NUMERIC_WIDTH},  // 77  #28 Denominator

	{COL4, STATUS_ROW+17, NUMERIC_WIDTH},  // 78  #29 Numerator
	{COL5, STATUS_ROW+17, NUMERIC_WIDTH},  // 79  #29 Denominator


	// 2nd DTCIUMPR DTC column
	{COL6, STATUS_ROW+2,  NUMERIC_WIDTH},  // 80  #30 DTC
	{COL6, STATUS_ROW+3,  NUMERIC_WIDTH},  // 81  #31 DTC
	{COL6, STATUS_ROW+4,  NUMERIC_WIDTH},  // 82  #32 DTC
	{COL6, STATUS_ROW+5,  NUMERIC_WIDTH},  // 83  #33 DTC
	{COL6, STATUS_ROW+6,  NUMERIC_WIDTH},  // 84  #34 DTC
	{COL6, STATUS_ROW+7,  NUMERIC_WIDTH},  // 85  #35 DTC
	{COL6, STATUS_ROW+8,  NUMERIC_WIDTH},  // 86  #36 DTC
	{COL6, STATUS_ROW+9,  NUMERIC_WIDTH},  // 87  #37 DTC
	{COL6, STATUS_ROW+10, NUMERIC_WIDTH},  // 88  #38 DTC
	{COL6, STATUS_ROW+11, NUMERIC_WIDTH},  // 89  #49 DTC
	{COL6, STATUS_ROW+12, NUMERIC_WIDTH},  // 90  #40 DTC
	{COL6, STATUS_ROW+13, NUMERIC_WIDTH},  // 91  #41 DTC
	{COL6, STATUS_ROW+14, NUMERIC_WIDTH},  // 92  #42 DTC
	{COL6, STATUS_ROW+15, NUMERIC_WIDTH},  // 93  #43 DTC
	{COL6, STATUS_ROW+16, NUMERIC_WIDTH},  // 94  #44 DTC
	{COL6, STATUS_ROW+17, NUMERIC_WIDTH},  // 95  #45 DTC

	// 2nd DTCIUMPR initial value columns
	{COL7, STATUS_ROW+2,  NUMERIC_WIDTH},  // 96  #30 Numerator
	{COL8, STATUS_ROW+2,  NUMERIC_WIDTH},  // 97  #30 Denominator

	{COL7, STATUS_ROW+3,  NUMERIC_WIDTH},  // 98  #31 Numerator
	{COL8, STATUS_ROW+3,  NUMERIC_WIDTH},  // 99  #31 Denominator

	{COL7, STATUS_ROW+4,  NUMERIC_WIDTH},  // 100 #32 Numerator
	{COL8, STATUS_ROW+4,  NUMERIC_WIDTH},  // 101 #32 Denominator

	{COL7, STATUS_ROW+5,  NUMERIC_WIDTH},  // 102 #33 Numerator
	{COL8, STATUS_ROW+5,  NUMERIC_WIDTH},  // 103 #33 Denominator

	{COL7, STATUS_ROW+6,  NUMERIC_WIDTH},  // 104 #34 Numerator
	{COL8, STATUS_ROW+6,  NUMERIC_WIDTH},  // 105 #34 Denominator

	{COL7, STATUS_ROW+7,  NUMERIC_WIDTH},  // 108 #35 Numerator
	{COL8, STATUS_ROW+7,  NUMERIC_WIDTH},  // 109 #35 Denominator

	{COL7, STATUS_ROW+8,  NUMERIC_WIDTH},  // 106 #36 Numerator
	{COL8, STATUS_ROW+8,  NUMERIC_WIDTH},  // 107 #36 Denominator

	{COL7, STATUS_ROW+9,  NUMERIC_WIDTH},  // 110 #37 Numerator
	{COL8, STATUS_ROW+9,  NUMERIC_WIDTH},  // 111 #37 Denominator

	{COL7, STATUS_ROW+10, NUMERIC_WIDTH},  // 112 #38 Numerator
	{COL8, STATUS_ROW+10, NUMERIC_WIDTH},  // 113 #38 Denominator

	{COL7, STATUS_ROW+11, NUMERIC_WIDTH},  // 114 #39 Numerator
	{COL8, STATUS_ROW+11, NUMERIC_WIDTH},  // 115 #39 Denominator

	{COL7, STATUS_ROW+12, NUMERIC_WIDTH},  // 116 #40 Numerator
	{COL8, STATUS_ROW+12, NUMERIC_WIDTH},  // 117 #40 Denominator

	{COL7, STATUS_ROW+13, NUMERIC_WIDTH},  // 118 #41 Numerator
	{COL8, STATUS_ROW+13, NUMERIC_WIDTH},  // 119 #41 Denominator

	{COL7, STATUS_ROW+14, NUMERIC_WIDTH},  // 120 #42 Numerator
	{COL8, STATUS_ROW+14, NUMERIC_WIDTH},  // 121 #42 Denominator

	{COL7, STATUS_ROW+15, NUMERIC_WIDTH},  // 122 #43 Numerator
	{COL8, STATUS_ROW+15, NUMERIC_WIDTH},  // 123 #43 Denominator

	{COL7, STATUS_ROW+16, NUMERIC_WIDTH},  // 124 #44 Numerator
	{COL8, STATUS_ROW+16, NUMERIC_WIDTH},  // 125 #44 Denominator

	{COL7, STATUS_ROW+17, NUMERIC_WIDTH},  // 126 #45 Numerator
	{COL8, STATUS_ROW+17, NUMERIC_WIDTH},  // 127 #45 Denominator

	// 2nd DTCIUMPR current value columns
	{COL9,  STATUS_ROW+2,  NUMERIC_WIDTH},  // 128 #30 Numerator
	{COL10, STATUS_ROW+2,  NUMERIC_WIDTH},  // 129 #30 Denominator

	{COL9,  STATUS_ROW+3,  NUMERIC_WIDTH},  // 130 #31 Numerator
	{COL10, STATUS_ROW+3,  NUMERIC_WIDTH},  // 131 #31 Denominator

	{COL9,  STATUS_ROW+4,  NUMERIC_WIDTH},  // 132 #32 Numerator
	{COL10, STATUS_ROW+4,  NUMERIC_WIDTH},  // 133 #32 Denominator

	{COL9,  STATUS_ROW+5,  NUMERIC_WIDTH},  // 134 #33 Numerator
	{COL10, STATUS_ROW+5,  NUMERIC_WIDTH},  // 135 #33 Denominator

	{COL9,  STATUS_ROW+6,  NUMERIC_WIDTH},  // 136 #34 Numerator
	{COL10, STATUS_ROW+6,  NUMERIC_WIDTH},  // 137 #34 Denominator

	{COL9,  STATUS_ROW+7,  NUMERIC_WIDTH},  // 138 #35 Numerator
	{COL10, STATUS_ROW+7,  NUMERIC_WIDTH},  // 139 #35 Denominator

	{COL9,  STATUS_ROW+8,  NUMERIC_WIDTH},  // 140 #36 Numerator
	{COL10, STATUS_ROW+8,  NUMERIC_WIDTH},  // 141 #36 Denominator

	{COL9,  STATUS_ROW+9,  NUMERIC_WIDTH},  // 142 #37 Numerator
	{COL10, STATUS_ROW+9,  NUMERIC_WIDTH},  // 143 #37 Denominator

	{COL9,  STATUS_ROW+10, NUMERIC_WIDTH},  // 144 #38 Numerator
	{COL10, STATUS_ROW+10, NUMERIC_WIDTH},  // 145 #38 Denominator

	{COL9,  STATUS_ROW+11, NUMERIC_WIDTH},  // 146 #39 Numerator
	{COL10, STATUS_ROW+11, NUMERIC_WIDTH},  // 147 #39 Denominator

	{COL9,  STATUS_ROW+12, NUMERIC_WIDTH},  // 148 #40 Numerator
	{COL10, STATUS_ROW+12, NUMERIC_WIDTH},  // 149 #40 Denominator

	{COL9,  STATUS_ROW+13, NUMERIC_WIDTH},  // 150 #41 Numerator
	{COL10, STATUS_ROW+13, NUMERIC_WIDTH},  // 151 #41 Denominator

	{COL9,  STATUS_ROW+14, NUMERIC_WIDTH},  // 152 #42 Numerator
	{COL10, STATUS_ROW+14, NUMERIC_WIDTH},  // 153 #12 Denominator

	{COL9,  STATUS_ROW+15, NUMERIC_WIDTH},  // 154 #43 Numerator
	{COL10, STATUS_ROW+15, NUMERIC_WIDTH},  // 155 #43 Denominator

	{COL9,  STATUS_ROW+16, NUMERIC_WIDTH},  // 156 #44 Numerator
	{COL10, STATUS_ROW+16, NUMERIC_WIDTH},  // 157 #44 Denominator

	{COL9,  STATUS_ROW+17, NUMERIC_WIDTH},  // 158 #45 Numerator
	{COL10, STATUS_ROW+17, NUMERIC_WIDTH},  // 159 #45 Denominator
};
const int _num_dynamic_elements_iumpr2 = sizeof ( _dynamic_elements_iumpr2 )/sizeof ( _dynamic_elements_iumpr2[0] );

// Indexes for dynamic IUMPR elements (Data Page 1 only)
#define IUMPR_COL1_DTC_INDEX        0
// #14 DTC                          0
// . . .
// #29 DTC                          15

#define IUMPR_COL1_INIT_INDEX       16
// #14 Numerator                    16
// #14 Denominator                  17
// . . .
// #29 Numerator                    46
// #29 Denominator                  47

#define IUMPR_COL1_CUR_INDEX        48
// #14 Numerator                    48
// #14 Denominator                  49
// . . .
// #29 Numerator                    78
// #29 Denominator                  79

#define IUMPR_COL2_DTC_INDEX        80
// #30 DTC                          80
// . . .
// #45 DTC                          95

#define IUMPR_COL2_INIT_INDEX       96
// #30 Numerator                    96
// #30 Denominator                  97
// . . .
// #45 Numerator                    126
// #45 Denominator                  127

#define IUMPR_COL2_CUR_INDEX        128
// #30 Numerator                    128
// #30 Denominator                  129
// . . .
// #45 Numerator                    158
// #45 Denominator                  159


// dynamic timer elements (Data Page 1 and 2+)
DynamicValueElement  _dynamic_elements_timers[] =
{
	{20, CARB_CYCLE_ROW,   NUMERIC_WIDTH},  // 0 CARB Idle Timer
	{47, CARB_CYCLE_ROW,   NUMERIC_WIDTH},  // 1 CARB At Speed Timer
	{73, CARB_CYCLE_ROW,   NUMERIC_WIDTH},  // 2 CARB Run Time
};
const int _num_dynamic_elements_timers = sizeof ( _dynamic_elements_timers )/sizeof ( _dynamic_elements_timers[0] );

// Indexes for dynamic timer elements (Data Page 1 only)
#define CARB_TIMER_INDEX            0
#define IDLE_TIMER_INDEX            0
#define SPEED_25_MPH_TIMER_INDEX    1
#define TOTAL_DRIVE_TIMER_INDEX     2


// Data Page limits
#define MAX_DATA_PAGE_1             IUMPR_1_COL_LEN      // Maximum number of DTC IUMPR on page 1
#define MAX_DATA_PER_PAGE           2 * IUMPR_2_COL_LEN  // Maximum number of DTC IUMPR on pages other than 1
#define MAX_DATA_PAGE               8                    // MAX_DATA / MAX_DATA_PER_PAGE rounded up


// IPT (IPD and IUMPR) Status Flags
#define IPT_STATUS_FLAG_BYTES 16
// Flags[0]
#define OBDCOND_NOT_DONE          0xFFFFFFFE
#define IGNCNTR_NOT_DONE          0xFFFFFFFD

#define DTCIUMPR1_COMP_NOT_DONE   0xFFFFFFFB
#define DTCIUMPR1_COND_NOT_DONE   0xFFFFFFF7
#define DTCIUMPR2_COMP_NOT_DONE   0xFFFFFFEF
#define DTCIUMPR2_COND_NOT_DONE   0xFFFFFFDF
#define DTCIUMPR3_COMP_NOT_DONE   0xFFFFFFBF
#define DTCIUMPR3_COND_NOT_DONE   0xFFFFFF7F
#define DTCIUMPR4_COMP_NOT_DONE   0xFFFFFEFF
#define DTCIUMPR4_COND_NOT_DONE   0xFFFFFDFF
#define DTCIUMPR5_COMP_NOT_DONE   0xFFFFFBFF
#define DTCIUMPR5_COND_NOT_DONE   0xFFFFF7FF
#define DTCIUMPR6_COMP_NOT_DONE   0xFFFFEFFF
#define DTCIUMPR6_COND_NOT_DONE   0xFFFFDFFF
#define DTCIUMPR7_COMP_NOT_DONE   0xFFFFBFFF
#define DTCIUMPR7_COND_NOT_DONE   0xFFFF7FFF
#define DTCIUMPR8_COMP_NOT_DONE   0xFFFEFFFF
#define DTCIUMPR8_COND_NOT_DONE   0xFFFDFFFF
#define DTCIUMPR9_COMP_NOT_DONE   0xFFFBFFFF
#define DTCIUMPR9_COND_NOT_DONE   0xFFF7FFFF
#define DTCIUMPR10_COMP_NOT_DONE  0xFFEFFFFF
#define DTCIUMPR10_COND_NOT_DONE  0xFFDFFFFF
#define DTCIUMPR11_COMP_NOT_DONE  0xFFBFFFFF
#define DTCIUMPR11_COND_NOT_DONE  0xFF7FFFFF
#define DTCIUMPR12_COMP_NOT_DONE  0xFEFFFFFF
#define DTCIUMPR12_COND_NOT_DONE  0xFDFFFFFF
#define DTCIUMPR13_COMP_NOT_DONE  0xFBFFFFFF
#define DTCIUMPR13_COND_NOT_DONE  0xF7FFFFFF
#define DTCIUMPR14_COMP_NOT_DONE  0xEFFFFFFF
#define DTCIUMPR14_COND_NOT_DONE  0xDFFFFFFF
#define DTCIUMPR15_COMP_NOT_DONE  0xBFFFFFFF
#define DTCIUMPR15_COND_NOT_DONE  0x7FFFFFFF

// Flags[1]
#define DTCIUMPR16_COMP_NOT_DONE  0xFFFFFFFE
#define DTCIUMPR16_COND_NOT_DONE  0xFFFFFFFD
#define DTCIUMPR17_COMP_NOT_DONE  0xFFFFFFFB
#define DTCIUMPR17_COND_NOT_DONE  0xFFFFFFF7
#define DTCIUMPR18_COMP_NOT_DONE  0xFFFFFFEF
#define DTCIUMPR18_COND_NOT_DONE  0xFFFFFFDF
#define DTCIUMPR19_COMP_NOT_DONE  0xFFFFFFBF
#define DTCIUMPR19_COND_NOT_DONE  0xFFFFFF7F
#define DTCIUMPR20_COMP_NOT_DONE  0xFFFFFEFF
#define DTCIUMPR20_COND_NOT_DONE  0xFFFFFDFF
#define DTCIUMPR21_COMP_NOT_DONE  0xFFFFFBFF
#define DTCIUMPR21_COND_NOT_DONE  0xFFFFF7FF
#define DTCIUMPR22_COMP_NOT_DONE  0xFFFFEFFF
#define DTCIUMPR22_COND_NOT_DONE  0xFFFFDFFF
#define DTCIUMPR23_COMP_NOT_DONE  0xFFFFBFFF
#define DTCIUMPR23_COND_NOT_DONE  0xFFFF7FFF
#define DTCIUMPR24_COMP_NOT_DONE  0xFFFEFFFF
#define DTCIUMPR24_COND_NOT_DONE  0xFFFDFFFF
#define DTCIUMPR25_COMP_NOT_DONE  0xFFFBFFFF
#define DTCIUMPR25_COND_NOT_DONE  0xFFF7FFFF
#define DTCIUMPR26_COMP_NOT_DONE  0xFFEFFFFF
#define DTCIUMPR26_COND_NOT_DONE  0xFFDFFFFF
#define DTCIUMPR27_COMP_NOT_DONE  0xFFBFFFFF
#define DTCIUMPR27_COND_NOT_DONE  0xFF7FFFFF
#define DTCIUMPR28_COMP_NOT_DONE  0xFEFFFFFF
#define DTCIUMPR28_COND_NOT_DONE  0xFDFFFFFF
#define DTCIUMPR29_COMP_NOT_DONE  0xFBFFFFFF
#define DTCIUMPR29_COND_NOT_DONE  0xF7FFFFFF
#define DTCIUMPR30_COMP_NOT_DONE  0xEFFFFFFF
#define DTCIUMPR30_COND_NOT_DONE  0xDFFFFFFF
#define DTCIUMPR31_COMP_NOT_DONE  0xBFFFFFFF
#define DTCIUMPR31_COND_NOT_DONE  0x7FFFFFFF

// Flags[2]
#define DTCIUMPR32_COMP_NOT_DONE  0xFFFFFFFE
#define DTCIUMPR32_COND_NOT_DONE  0xFFFFFFFD
#define DTCIUMPR33_COMP_NOT_DONE  0xFFFFFFFB
#define DTCIUMPR33_COND_NOT_DONE  0xFFFFFFF7
#define DTCIUMPR34_COMP_NOT_DONE  0xFFFFFFEF
#define DTCIUMPR34_COND_NOT_DONE  0xFFFFFFDF
#define DTCIUMPR35_COMP_NOT_DONE  0xFFFFFFBF
#define DTCIUMPR35_COND_NOT_DONE  0xFFFFFF7F
#define DTCIUMPR36_COMP_NOT_DONE  0xFFFFFEFF
#define DTCIUMPR36_COND_NOT_DONE  0xFFFFFDFF
#define DTCIUMPR37_COMP_NOT_DONE  0xFFFFFBFF
#define DTCIUMPR37_COND_NOT_DONE  0xFFFFF7FF
#define DTCIUMPR38_COMP_NOT_DONE  0xFFFFEFFF
#define DTCIUMPR38_COND_NOT_DONE  0xFFFFDFFF
#define DTCIUMPR39_COMP_NOT_DONE  0xFFFFBFFF
#define DTCIUMPR39_COND_NOT_DONE  0xFFFF7FFF
#define DTCIUMPR40_COMP_NOT_DONE  0xFFFEFFFF
#define DTCIUMPR40_COND_NOT_DONE  0xFFFDFFFF
#define DTCIUMPR41_COMP_NOT_DONE  0xFFFBFFFF
#define DTCIUMPR41_COND_NOT_DONE  0xFFF7FFFF
#define DTCIUMPR42_COMP_NOT_DONE  0xFFEFFFFF
#define DTCIUMPR42_COND_NOT_DONE  0xFFDFFFFF
#define DTCIUMPR43_COMP_NOT_DONE  0xFFBFFFFF
#define DTCIUMPR43_COND_NOT_DONE  0xFF7FFFFF
#define DTCIUMPR44_COMP_NOT_DONE  0xFEFFFFFF
#define DTCIUMPR44_COND_NOT_DONE  0xFDFFFFFF
#define DTCIUMPR45_COMP_NOT_DONE  0xFBFFFFFF
#define DTCIUMPR45_COND_NOT_DONE  0xF7FFFFFF
#define DTCIUMPR46_COMP_NOT_DONE  0xEFFFFFFF
#define DTCIUMPR46_COND_NOT_DONE  0xDFFFFFFF
#define DTCIUMPR47_COMP_NOT_DONE  0xBFFFFFFF
#define DTCIUMPR47_COND_NOT_DONE  0x7FFFFFFF


// used with the on-screen test status
#define SUB_TEST_NORMAL     0x00
#define SUB_TEST_FAIL       0x01
#define SUB_TEST_DTC        0x02
#define SUB_TEST_INITIAL    0x80


#define SetFieldDec(index,val)     (update_screen_dec  ( _dynamic_elements_11,     _num_dynamic_elements_11,     index, val ))
#define SetFieldHex(index,val)     (update_screen_hex  ( _dynamic_elements_11,     _num_dynamic_elements_11,     index, val ))
#define SetFieldText(index,val)    (update_screen_text ( _dynamic_elements_11,     _num_dynamic_elements_11,     index, val ))

#define SetFieldDec_IM(index,val)  (update_screen_dec  ( _dynamic_elements_imr,    _num_dynamic_elements_imr,    index, val ))
#define SetFieldHex_IM(index,val)  (update_screen_hex  ( _dynamic_elements_imr,    _num_dynamic_elements_imr,    index, val ))
#define SetFieldText_IM(index,val) (update_screen_text ( _dynamic_elements_imr,    _num_dynamic_elements_imr,    index, val ))

#define SetFieldDec_1(index,val)   (update_screen_dec  ( _dynamic_elements_iumpr1, _num_dynamic_elements_iumpr1, index, val ))
#define SetFieldHex_1(index,val)   (update_screen_hex  ( _dynamic_elements_iumpr1, _num_dynamic_elements_iumpr1, index, val ))
#define SetFieldText_1(index,val)  (update_screen_text ( _dynamic_elements_iumpr1, _num_dynamic_elements_iumpr1, index, val ))

#define SetFieldDec_2(index,val)   (update_screen_dec  ( _dynamic_elements_iumpr2, _num_dynamic_elements_iumpr2, index, val ))
#define SetFieldHex_2(index,val)   (update_screen_hex  ( _dynamic_elements_iumpr2, _num_dynamic_elements_iumpr2, index, val ))
#define SetFieldText_2(index,val)  (update_screen_text ( _dynamic_elements_iumpr2, _num_dynamic_elements_iumpr2, index, val ))

#define SetFieldDec_T(index,val)   (update_screen_dec  ( _dynamic_elements_timers, _num_dynamic_elements_timers, index, val ))
#define SetFieldHex_T(index,val)   (update_screen_hex  ( _dynamic_elements_timers, _num_dynamic_elements_timers, index, val ))
#define SetFieldText_T(index,val)  (update_screen_text ( _dynamic_elements_timers, _num_dynamic_elements_timers, index, val ))


#define NORMAL_TEXT        -1
#define HIGHLIGHTED_TEXT    8
#define RED_TEXT            1


extern FILE         *gLogFileHandle;

extern const char szEcuId[];
extern const char szIpdSize[];
extern const char szIpdName[][8];
extern const char szIumprSize[];
extern const char szIumprName[];
extern const char szIumprData1[];
extern const char szIumprData2[];
extern const char szSmadSize[];
extern const char szSmadName[];
extern const char szSmadData1[];
extern const char szSmadData2[];
extern const char szSmadData3[];

extern BYTE DTCTypeCharacterSize;

/*******************************************************************************
**
**  Function:  Test11_PerformanceCounters
**
**  Purpose:   Run Test 11 to verify performance counters
**
*******************************************************************************/
STATUS Test11_PerformanceCounters ( BOOL *pbTestReEntered )
{
	STATUS         eRetVal;

	unsigned long  EngineStartTimestampMsecs = 0;
	BOOL           bRunTest11_2;
	BOOL           bSubTestFailed = FALSE;


	// initialize arrays
	memset ( BankSupport,
	         FALSE,
	         sizeof ( BankSupport ) );


//*******************************************************************************
//  Test 11.1 - Verify performance counters
//*******************************************************************************
	gTestSubsection = 1;
	gbTestSubsectionFailed = FALSE;
	bSubTestFailed = FALSE;

	Log ( SUBSECTION_BEGIN, SCREENOUTPUTON, LOGOUTPUTON, NO_PROMPT,
	      "(Verify performance counters)" );

	if ( gbEngineRunning == FALSE )
	{
		Log ( PROMPT, SCREENOUTPUTON, LOGOUTPUTON, ENTER_PROMPT,
		      "Turn ignition to crank position and start engine." );
		gbEngineRunning = TRUE;
		EngineStartTimestampMsecs = GetTickCount ( );
	}

	// Verify still connected
	if ( VerifyECUCommunication ( ) == FAIL )
	{
		bSubTestFailed = TRUE;

		// lost connection, probably due to crank - so re-establish connection
		DisconnectOBDProtocol ( );

		if ( ConnectOBDProtocol ( ) == FAIL )
		{
			Log ( FAILURE, SCREENOUTPUTON, LOGOUTPUTON, NO_PROMPT,
			      "Connect Protocol unsuccessful\n" );
			Log ( SUBSECTION_FAILED_RESULT, SCREENOUTPUTON, LOGOUTPUTON, NO_PROMPT, "" );
			return FAIL;
		}
	}


	bRunTest11_2 = FALSE;

	if ( VerifyIMReadiness () == FALSE )
	{
		bRunTest11_2 = TRUE;
	}

	// Read DTC ExtData $91 (IUMPR)
	if ( VerifyDTCExtData ( 0x91 ) != PASS )
	{
		Log ( FAILURE, SCREENOUTPUTON, LOGOUTPUTON, NO_PROMPT,
		     "IUMPR Data error\n" );
		bSubTestFailed = TRUE;
	}

	// Read DTC ExtDaya $93 (SMAD)
	if ( VerifyDTCExtData ( 0x93 ) != PASS )
	{
		Log ( FAILURE, SCREENOUTPUTON, LOGOUTPUTON, NO_PROMPT,
		      "SMAD Data error\n" );
		bSubTestFailed = TRUE;
	}


	// if this test has not been reenter, the data is already in the appropriate array,
	// otherwise read the data from the file
	if ( *pbTestReEntered == TRUE )
	{
		// Retrieve IPT and IUMPR from the first run of test 10.10
		if ( ReadDataFromLogFile ( "**** Test 10.10 (", "**** Test 10.10" ) == FALSE )
		{
			bSubTestFailed = TRUE;
			gbTestSubsectionFailed = FALSE;
			Log ( FAILURE, SCREENOUTPUTON, LOGOUTPUTON, NO_PROMPT,
			      "Could not find Test 10.10 IPD and DTCIUMPR data in log file.\n" );
		}
	}


	eRetVal = VerifyAndEvaluateIPTData ( FALSE );
	// if conditions require running of test 11.2
	if ( (eRetVal & FAIL) == FAIL )
	{
		bRunTest11_2 = TRUE;
	}
	// if conditions indicate that test 11.2 may not finish normally
	if ( (eRetVal & ERRORS) == ERRORS )
	{
		bSubTestFailed = TRUE;
	}


	if ( VerifyVehicleState ( gbEngineRunning, gbHybrid ) != PASS )
	{
		bSubTestFailed = TRUE;
	}

	if ( bSubTestFailed == TRUE || gbTestSubsectionFailed == TRUE )
	{
		if ( Log ( SUBSECTION_FAILED_RESULT, SCREENOUTPUTON, LOGOUTPUTON, YES_NO_ALL_PROMPT,
		           "Errors detected that may cause the next test,\n"
		           "Test 11.2 (Complete Drive Cycle and Clear IM Readiness Bits),\n"
		           "to fail even when the test exits normally." ) == 'N' )
		{
			return FAIL;
		}
	}
	else
	{
		Log ( SUBSECTION_PASSED_RESULT, SCREENOUTPUTON, LOGOUTPUTON, NO_PROMPT, "" );
	}


//*******************************************************************************
//  Test 11.2 - Manufacture Specific Drive Cycle
//*******************************************************************************
	gTestSubsection = 2;
	gbTestSubsectionFailed = FALSE;
	bSubTestFailed = FALSE;

	Log ( SUBSECTION_BEGIN, SCREENOUTPUTON, LOGOUTPUTON, NO_PROMPT,
	      "(Manufacture Specific Drive Cycle)" );

	if ( bRunTest11_2 == TRUE )
	{
		// tester-present message should already be active

		Log ( PROMPT, SCREENOUTPUTON, LOGOUTPUTON, NO_PROMPT,
		      "\n"
		      "Drive the vehicle in the manufacturer-specified manner to complete all the\n"
		      "OBD monitors required to set all the supported I/M Readiness bits to a \"Ready\"\n"
		      "condition. The vehicle may have to \"soak\" with the ignition off (engine off).\n"
		      "Allow vehicle to soak according to the manufacturer-specified conditions in\n"
		      "order to run any engine-off diagnostics and/or prepare the vehicle for any \n"
		      "engine-running diagnostics on the next drive cycle that requires an engine-off\n"
		      "soak period.\n\n" );

		if ( Log ( PROMPT, SCREENOUTPUTON, LOGOUTPUTON, YES_NO_PROMPT,
		           "Would you like to use the J1699 software as a monitor to view\n"
		           "the status of the I/M Readiness bits?\n"
		           "(Enter 'N' to exit the software if you are going to turn the vehicle off\n"
		           "or don't need a monitor. You can return to this point, at any time,\n"
		           "by re-starting the J1699 software and selecting 'Dynamic Tests'.)" ) != 'Y' )
		{
			Log ( SUBSECTION_INCOMPLETE_RESULT, SCREENOUTPUTON, LOGOUTPUTON, NO_PROMPT, "" );
			ABORT_RETURN;
		}


		// stop tester-present message
		StopPeriodicMsg ( TRUE );
		Sleep ( gRequestDelayTimeMsecs );


		eRetVal = RunDynamicTest11 ( *pbTestReEntered, EngineStartTimestampMsecs );


		// re-start tester-present message
		StartPeriodicMsg ( );


		if ( VerifyVehicleState ( gbEngineRunning, gbHybrid ) != PASS )
		{
			bSubTestFailed = TRUE;
		}

		if ( eRetVal == ABORT || gbTestSectionAborted == TRUE )
		{
			if ( gbTestSubsectionFailed == TRUE || bSubTestFailed == TRUE )
			{
				Log ( SUBSECTION_FAILED_RESULT, SCREENOUTPUTON, LOGOUTPUTON, ENTER_PROMPT, "" );
			}
			Log ( SUBSECTION_INCOMPLETE_RESULT, SCREENOUTPUTON, LOGOUTPUTON, NO_PROMPT, "" );
			ABORT_RETURN;
		}
		else if ( eRetVal == FAIL || gbTestSubsectionFailed == TRUE || bSubTestFailed == TRUE )
		{
			if ( Log ( SUBSECTION_FAILED_RESULT, SCREENOUTPUTON, LOGOUTPUTON, YES_NO_ALL_PROMPT, "" ) == 'N' )
			{
				return FAIL;
			}
		}
		else
		{
			Log ( SUBSECTION_PASSED_RESULT, SCREENOUTPUTON, LOGOUTPUTON, NO_PROMPT, "" );
		}
	}
	else
	{
		Log ( SUBSECTION_PASSED_RESULT, SCREENOUTPUTON, LOGOUTPUTON, NO_PROMPT, "" );
	}


//*******************************************************************************
//  Test 11.3 -
//*******************************************************************************
	gTestSubsection = 3;
	gbTestSubsectionFailed = FALSE;
	bSubTestFailed = FALSE;

	Log ( SUBSECTION_BEGIN, SCREENOUTPUTON, LOGOUTPUTON, NO_PROMPT,
	      "(Turn engine off to allow drive cycle data to be collected without it changing during data collection process.))" );

	Log ( PROMPT, SCREENOUTPUTON, LOGOUTPUTON, ENTER_PROMPT,
	      "Pull over to a safe spot and turn the ignition off for at least 30 sec.\n"
	      "Keep tool connected to the J1962 connector.  " );

	Log ( PROMPT, SCREENOUTPUTON, LOGOUTPUTON, ENTER_PROMPT,
	      "Turn key on without cranking or starting engine." );

	Log ( SUBSECTION_PASSED_RESULT, SCREENOUTPUTON, LOGOUTPUTON, NO_PROMPT, "" );

	// Engine should now be not running
	gbEngineRunning = FALSE;


//*******************************************************************************
//  Test 11.4 - Re-establish communication, ignition on, engine off
//*******************************************************************************
	gTestSubsection = 4;
	gbTestSubsectionFailed = FALSE;
	bSubTestFailed = FALSE;

	Log ( SUBSECTION_BEGIN, SCREENOUTPUTON, LOGOUTPUTON, NO_PROMPT,
	      "(Re-establish communication, ignition on, engine off)" );

	if ( DetermineOBDProtocol ( ) != PASS )
	{
		Log ( FAILURE, SCREENOUTPUTON, LOGOUTPUTON, NO_PROMPT,
		      "Protocol determination unsuccessful.\n" );
		bSubTestFailed = TRUE;
	}

	if ( gbTestSubsectionFailed == TRUE || bSubTestFailed == TRUE )
	{
		if ( Log ( SUBSECTION_FAILED_RESULT, SCREENOUTPUTON, LOGOUTPUTON, YES_NO_ALL_PROMPT, "" ) == 'N' )
		{
			return FAIL;
		}
	}
	else
	{
		Log ( SUBSECTION_PASSED_RESULT, SCREENOUTPUTON, LOGOUTPUTON, NO_PROMPT, "" );
	}


//*******************************************************************************
//  Test 11.5 - Verify Diagnostic Data, Engine Off
//*******************************************************************************
	gTestSubsection = 5;
	gbTestSubsectionFailed = FALSE;
	bSubTestFailed = FALSE;

	Log ( SUBSECTION_BEGIN, SCREENOUTPUTON, LOGOUTPUTON, NO_PROMPT,
	      "(Verify Diagnostic Data (SID $22 PIDs), Engine Off)" );

	if ( VerifyIM_Ready ( ) != PASS )
	{
		bSubTestFailed = TRUE;
	}

	if ( VerifyVehicleState ( gbEngineRunning, gbHybrid ) != PASS )
	{
		bSubTestFailed = TRUE;
	}

	if ( gbTestSubsectionFailed == TRUE || bSubTestFailed == TRUE )
	{
		if ( Log ( SUBSECTION_FAILED_RESULT, SCREENOUTPUTON, LOGOUTPUTON, YES_NO_ALL_PROMPT, "" ) == 'N' )
		{
			return FAIL;
		}
	}
	else
	{
		Log ( SUBSECTION_PASSED_RESULT, SCREENOUTPUTON, LOGOUTPUTON, NO_PROMPT, "" );
	}


//*******************************************************************************
//  Test 11.6 - Verify Monitor Test Results, Engine Off
//*******************************************************************************
	gTestSubsection = 6;
	gbTestSubsectionFailed = FALSE;
	bSubTestFailed = FALSE;

	Log ( SUBSECTION_BEGIN, SCREENOUTPUTON, LOGOUTPUTON, NO_PROMPT,
	      "(Verify On-Board Monitoring Test Results (SID $19 DTCEDRN $92))" );

	// Read DTC ExtData $92 (Test Result), based on list read in Test 10.3
	if ( VerifyDTCExtData ( 0x92 ) != PASS )
	{
		Log ( FAILURE, SCREENOUTPUTON, LOGOUTPUTON, NO_PROMPT,
		      "Test Result Data error\n" );
		bSubTestFailed = TRUE;
	}

//	if ( VerifyVehicleState ( gbEngineRunning, gbHybrid ) != PASS )
//	{
//		bSubTestFailed = TRUE;
//	}

	if ( gbTestSubsectionFailed == TRUE || bSubTestFailed == TRUE )
	{
		if ( Log ( SUBSECTION_FAILED_RESULT, SCREENOUTPUTON, LOGOUTPUTON, YES_NO_ALL_PROMPT, "" ) == 'N' )
		{
			return FAIL;
		}
	}
	else
	{
		Log ( SUBSECTION_PASSED_RESULT, SCREENOUTPUTON, LOGOUTPUTON, NO_PROMPT, "" );
	}


//*******************************************************************************
//  Test 11.7 - Verify Vehicle Information, Engine Off
//*******************************************************************************
	gTestSubsection = 7;
	gbTestSubsectionFailed = FALSE;
	bSubTestFailed = FALSE;

	Log ( SUBSECTION_BEGIN, SCREENOUTPUTON, LOGOUTPUTON, NO_PROMPT,
	      "(Verify Vehicle Information (Sevice $22 INFs), Engine Off)" );

	// Read DTC ExtData $91 (IUMPR)
	if ( VerifyDTCExtData ( 0x91 ) != PASS )
	{
		Log ( FAILURE, SCREENOUTPUTON, LOGOUTPUTON, NO_PROMPT,
		      "IUMPR Data error\n" );
		bSubTestFailed = TRUE;
	}

	// Read DTC ExtDaya $93 (SMAD)
	if ( VerifyDTCExtData ( 0x93 ) != PASS )
	{
		Log ( FAILURE, SCREENOUTPUTON, LOGOUTPUTON, NO_PROMPT,
		      "SMAD Data error\n" );
		bSubTestFailed = TRUE;
	}

	if ( VerifyAndEvaluateIPTData ( TRUE ) != PASS )
	{
		bSubTestFailed = TRUE;
		gbTestSubsectionFailed = FALSE;
	}

	if ( EvaluateINF14 ( ) != PASS )
	{
		bSubTestFailed = TRUE;
	}

	if ( bSubTestFailed == TRUE || gbTestSubsectionFailed == TRUE )
	{
		if ( Log ( SUBSECTION_FAILED_RESULT, SCREENOUTPUTON, LOGOUTPUTON, YES_NO_ALL_PROMPT, "" ) == 'N' )
		{
			return FAIL;
		}
	}
	else
	{
		Log ( SUBSECTION_PASSED_RESULT, SCREENOUTPUTON, LOGOUTPUTON, NO_PROMPT, "" );
	}


//*******************************************************************************
//  Test 11.8 - Verify Diagnostic Data, Engine Off
//*******************************************************************************
	gTestSubsection = 8;
	gbTestSubsectionFailed = FALSE;
	bSubTestFailed = FALSE;

	Log ( SUBSECTION_BEGIN, SCREENOUTPUTON, LOGOUTPUTON, NO_PROMPT,
	      "(Verify IM Readiness (SID $22 PID $F501), Engine Off)" );

	if ( VerifyIM_Ready ( ) != PASS )
	{
		bSubTestFailed = TRUE;
	}

	if ( VerifyVehicleState ( gbEngineRunning, gbHybrid ) != PASS )
	{
		bSubTestFailed = TRUE;
	}

	if ( gbTestSubsectionFailed == TRUE || bSubTestFailed == TRUE )
	{
		if ( Log ( SUBSECTION_FAILED_RESULT, SCREENOUTPUTON, LOGOUTPUTON, YES_NO_ALL_PROMPT, "" ) == 'N' )
		{
			return FAIL;
		}
	}
	else
	{
		Log ( SUBSECTION_PASSED_RESULT, SCREENOUTPUTON, LOGOUTPUTON, NO_PROMPT, "" );
	}


//*******************************************************************************
//  Test 11.9 - Verify DTCs, Engine Off
//*******************************************************************************
	gTestSubsection = 9;
	gbTestSubsectionFailed = FALSE;
	bSubTestFailed = FALSE;

	Log ( SUBSECTION_BEGIN, SCREENOUTPUTON, LOGOUTPUTON, NO_PROMPT,
	      "(Verify DTCs (SID $19 LEV $42 StatusMask $08), Engine Off)" );

	if ( VerifyConfirmedDTCData ( DTC_STATE_NO_FAULT ) != PASS )
	{
		bSubTestFailed = TRUE;
	}

	if ( VerifyVehicleState ( gbEngineRunning, gbHybrid ) != PASS )
	{
		bSubTestFailed = TRUE;
	}

	if ( gbTestSubsectionFailed == TRUE || bSubTestFailed == TRUE )
	{
		if ( Log ( SUBSECTION_FAILED_RESULT, SCREENOUTPUTON, LOGOUTPUTON, YES_NO_ALL_PROMPT, "" ) == 'N' )
		{
			return FAIL;
		}
	}
	else
	{
		Log ( SUBSECTION_PASSED_RESULT, SCREENOUTPUTON, LOGOUTPUTON, NO_PROMPT, "" );
	}


//*******************************************************************************
//  Test 11.10 - Verify Pending DTCs, Engine Off
//*******************************************************************************
	gTestSubsection = 10;
	gbTestSubsectionFailed = FALSE;
	bSubTestFailed = FALSE;

	Log ( SUBSECTION_BEGIN, SCREENOUTPUTON, LOGOUTPUTON, NO_PROMPT,
	      "(Verify Pending DTCs (SID $19 LEV $42 StatusMask $04), Engine Off)" );

	if ( VerifyPendingDTCData ( DTC_STATE_NO_FAULT ) != PASS )
	{
		bSubTestFailed = TRUE;
	}

	if ( VerifyVehicleState ( gbEngineRunning, gbHybrid ) != PASS )
	{
		bSubTestFailed = TRUE;
	}

	if ( gbTestSubsectionFailed == TRUE || bSubTestFailed == TRUE )
	{
		if ( Log ( SUBSECTION_FAILED_RESULT, SCREENOUTPUTON, LOGOUTPUTON, YES_NO_ALL_PROMPT, "" ) == 'N' )
		{
			return FAIL;
		}
	}
	else
	{
		Log ( SUBSECTION_PASSED_RESULT, SCREENOUTPUTON, LOGOUTPUTON, NO_PROMPT, "" );
	}


//*******************************************************************************
//  Test 11.11 - Clear DTCs, Engine Off
//*******************************************************************************
	gTestSubsection = 11;
	gbTestSubsectionFailed = FALSE;
	bSubTestFailed = FALSE;

	Log ( SUBSECTION_BEGIN, SCREENOUTPUTON, LOGOUTPUTON, NO_PROMPT,
	      "(Clear DTCs (SID $14), Engine Off)" );

	if ( ClearCodes ( ) != PASS )
	{
		bSubTestFailed = TRUE;
	}

	if ( VerifyVehicleState ( gbEngineRunning, gbHybrid ) != PASS )
	{
		bSubTestFailed = TRUE;
	}

	if ( gbTestSubsectionFailed == TRUE || bSubTestFailed == TRUE )
	{
		if ( Log ( SUBSECTION_FAILED_RESULT, SCREENOUTPUTON, LOGOUTPUTON, YES_NO_ALL_PROMPT, "" ) == 'N' )
		{
			return FAIL;
		}
	}
	else
	{
		Log ( SUBSECTION_PASSED_RESULT, SCREENOUTPUTON, LOGOUTPUTON, NO_PROMPT, "" );
	}


//*******************************************************************************
//  Test 11.12 - Verify Vehicle Information, Engine Off
//*******************************************************************************
	gTestSubsection = 12;
	gbTestSubsectionFailed = FALSE;
	bSubTestFailed = FALSE;

	Log ( SUBSECTION_BEGIN, SCREENOUTPUTON, LOGOUTPUTON, NO_PROMPT,
	      "(Verify Vehicle Information (DTC IUMPR, SMAD, IM Readiness), Engine Off)" );

	// Read DTC ExtData $91 (IUMPR)
	if ( VerifyDTCExtData ( 0x91 ) != PASS )
	{
		Log ( FAILURE, SCREENOUTPUTON, LOGOUTPUTON, NO_PROMPT,
		     "IUMPR Data error\n" );
		bSubTestFailed = TRUE;
	}

	// Read DTC ExtDaya $93 (SMAD)
	if ( VerifyDTCExtData ( 0x93 ) != PASS )
	{
		Log ( FAILURE, SCREENOUTPUTON, LOGOUTPUTON, NO_PROMPT,
		      "SMAD Data error\n" );
		bSubTestFailed = TRUE;
	}

	if ( VerifyAndEvaluateIPTData ( TRUE ) != PASS )
	{
		bSubTestFailed = TRUE;
		gbTestSubsectionFailed = FALSE;
	}

	if ( VerifyVehicleState ( gbEngineRunning, gbHybrid ) != PASS )
	{
		bSubTestFailed = TRUE;
	}

	if ( gbTestSubsectionFailed == TRUE || bSubTestFailed == TRUE )
	{
		if ( Log ( SUBSECTION_FAILED_RESULT, SCREENOUTPUTON, LOGOUTPUTON, YES_NO_ALL_PROMPT, "" ) == 'N' )
		{
			return FAIL;
		}
	}
	else
	{
		Log ( SUBSECTION_PASSED_RESULT, SCREENOUTPUTON, LOGOUTPUTON, NO_PROMPT, "" );
	}

	return PASS;
}


/*******************************************************************************
**
**  Function:  VerifyIMReadiness
**
**  Purpose:
**
**  NOTE:
**
*******************************************************************************/
BOOL VerifyIMReadiness ( void )
{
	REQ_MSG        stReqMsg;
	BYTE           EcuIdx;

	PID           *pPid;


	// check if need to run test 11.2
	stReqMsg.SID      = 0x22;
	stReqMsg.NumIds   = 1;
	stReqMsg.u.DID[0] = 0xF501;
	if ( RequestSID ( &stReqMsg, REQ_MSG_NORMAL ) == FAIL )
	{
		Log ( FAILURE, SCREENOUTPUTON, LOGOUTPUTON, NO_PROMPT,
		      "PID $F501 request unsuccessful\n" );
		Log ( SUBSECTION_FAILED_RESULT, SCREENOUTPUTON, LOGOUTPUTON, NO_PROMPT, "" );
		return FALSE;
	}

	for ( EcuIdx = 0;
	      EcuIdx < gNumOfECUs;
	      EcuIdx++ )
	{
		if ( IsIDSupported ( EcuIdx, PF5REQUEST,0xF501 ) == TRUE )
		{
			pPid = (PID *)&gstResponse[EcuIdx].PID[0];

			// save for EvaluateIPData ( )
			pTest11_PIDF501[EcuIdx].PIDLSB  = pPid->PIDLSB;
			pTest11_PIDF501[EcuIdx].Data[0] = pPid->Data[0];
			pTest11_PIDF501[EcuIdx].Data[1] = pPid->Data[1];
			pTest11_PIDF501[EcuIdx].Data[2] = pPid->Data[2];
			pTest11_PIDF501[EcuIdx].Data[3] = pPid->Data[3];
			pTest11_PIDF501[EcuIdx].Data[4] = pPid->Data[4];
			pTest11_PIDF501[EcuIdx].Data[5] = pPid->Data[5];

			if ( IsIM_ReadinessComplete ( pPid ) == FALSE )
			{
				return FALSE;
			}
		}
	}

	return TRUE;
}


/*******************************************************************************
**
**  Function:  IsIM_ReadinessComplete
**
**  Purpose:
**
**  NOTE:      supported bits :: 1 -> supported,    0 -> not supported
**
**               status  bits :: 1 -> not complete, 0 -> complete (or not applicable)
**
*******************************************************************************/
BOOL IsIM_ReadinessComplete ( PID * pPid )
{
	if ( pPid->PIDLSB != 1 )
	{
		return FALSE;
	}

	//   supported bits    status bits
	if ( (pPid->Data[1] & (pPid->Data[1] >> 4)) != 0 ||
	     (pPid->Data[2] & (pPid->Data[2] >> 4)) != 0 ||
	     (pPid->Data[3] & (pPid->Data[3] >> 4)) != 0 ||
	     (pPid->Data[4] & (pPid->Data[4] >> 4)) != 0 ||
	     (pPid->Data[5] & (pPid->Data[5] >> 4)) != 0 )

	{
		return FALSE;
	}

	return TRUE;
}


/*******************************************************************************
**
**  Function:  VerifyAndEvaluateIPTData
**
**  Purpose:
**
*******************************************************************************/
STATUS VerifyAndEvaluateIPTData ( BOOL bDisplayErrorMsg )
{
	BYTE           EcuIdx;
	STATUS         eRetCode;
	STATUS         eRetVal = PASS;

	DWORD          IPTStatusFlags[IPT_STATUS_FLAG_BYTES];        // Bit Map of Items Completed


	// collect current In-Use Performance data
	if ( VerifyIPD ( ) == PASS )
	{
		for ( EcuIdx = 0;
		      EcuIdx < gNumOfECUs;
		      EcuIdx++ )
		{
			if ( gstResponse[EcuIdx].bIPDSupported == TRUE )
			{
				if ( (eRetCode = EvaluateIPD ( EcuIdx, &IPTStatusFlags[0], TRUE )) != PASS )
				{
					eRetVal |= eRetCode;
				}
			}
		}
	
	}
	else
	{
		eRetVal = FAIL;
	}

	// VerifyDTCExtData ( 0x91 ) is run just before each call to this routine
	// check current IUMPR data
	for ( EcuIdx = 0;
	      EcuIdx < gNumOfECUs;
	      EcuIdx++ )
	{
		if ( gstResponse[EcuIdx].bDTCIUMPRSupported == TRUE )
		{
			// if Test 11.1
			if ( geTestPhase == eTestPerformanceCounters && gTestSubsection == 1 )
			{
				WORD  Index;
				// Initialize pTest11CurrentDisplayData
				SaveDTCIUMPRData ( EcuIdx, &Index );
			}

			if ( (eRetCode = EvaluateDTCIUMPRData ( EcuIdx, &IPTStatusFlags[0], bDisplayErrorMsg )) != PASS )
			{
				eRetVal |= eRetCode;
			}
		}
	}

	return eRetVal;
}


/*******************************************************************************
**
**  Function:  EvaluateIPD
**
**  Purpose:
**
*******************************************************************************/
STATUS EvaluateIPD ( BYTE    EcuIdx,
                     DWORD  *pIPTStatusFlags,
                     BOOL    bDisplayErrorMsg )
{
	STATUS  eRetCode = PASS;

	WORD    Index;
	int     count;


	// if IPD (OBDCOND and IGNCNTR) not supported, then cannot do comparsions
	if ( gstResponse[EcuIdx].bIPDSupported == TRUE )
	{
		// IGNCNTR must be >= OBDCOND
		if ( gstResponse[EcuIdx].IPD[IPD_IGNCNTR_INDEX] < gstResponse[EcuIdx].IPD[IPD_OBDCOND_INDEX] )
		{
			if ( bDisplayErrorMsg == TRUE )
			{
				Log ( FAILURE, SCREENOUTPUTON, LOGOUTPUTON, NO_PROMPT,
				      "ECU %-8X  IGNCNTR less than OBDCOND\n",
				      GetEcuId ( EcuIdx ) );
			}
			eRetCode |= FAIL;
			*pIPTStatusFlags &= IGNCNTR_NOT_DONE;
		}

		// OBDCOND must be >= other monitor condition counters
		for ( Index = 0;
		      Index < gstResponse[EcuIdx].DTCIUMPRCount;
		      Index ++ )
		{
			if ( gstResponse[EcuIdx].IPD[IPD_OBDCOND_INDEX] < gstResponse[EcuIdx].DTCIUMPRList[Index].CondCounts )
			{
				if ( bDisplayErrorMsg == TRUE )
				{
					Log ( FAILURE, SCREENOUTPUTON, LOGOUTPUTON, NO_PROMPT,
					      "ECU %-8X  OBDCOND not greater than all other monitor condition counters\n",
					      GetEcuId ( EcuIdx ) );
				}
				eRetCode |= FAIL;
				*pIPTStatusFlags &= OBDCOND_NOT_DONE;
			}
		}

		// Test 11.7
		if ( gTestSubsection == 7 )
		{
			if ( (gstResponse[EcuIdx].StatusFlags & IPDDATAVALID) != 0 )
			{
				// IGNCNTR must be greater than the value in test 10.10
				count = gstResponse[EcuIdx].IPD_Test11_7[IPD_IGNCNTR_INDEX] - gstResponse[EcuIdx].IPD_Test10_10[IPD_IGNCNTR_INDEX];
				if ( count < 1 )
				{
					if ( bDisplayErrorMsg == TRUE )
					{
						Log ( FAILURE, SCREENOUTPUTON, LOGOUTPUTON, NO_PROMPT,
						      "ECU %-8X  IGNCNTR incremented less than 1 since Test 10.10\n",
						      GetEcuId ( EcuIdx ) );
					}
					eRetCode |= FAIL;
					*pIPTStatusFlags &= IGNCNTR_NOT_DONE;
				}

				// OBDCOND must have incremented by at least 1 since test 10.10
				count = gstResponse[EcuIdx].IPD_Test11_7[IPD_OBDCOND_INDEX] - gstResponse[EcuIdx].IPD_Test10_10[IPD_OBDCOND_INDEX];
				if ( count < 1 )
				{
					if ( bDisplayErrorMsg == TRUE )
					{
						Log ( FAILURE, SCREENOUTPUTON, LOGOUTPUTON, NO_PROMPT,
						      "ECU %-8X  OBDCOND incremented less than 1 since Test 10.10\n",
						      GetEcuId ( EcuIdx ) );
					}
					eRetCode |= FAIL;
					*pIPTStatusFlags &= OBDCOND_NOT_DONE;
				}
			}  // end if ( IPDDATAVALID )
		}  // end if Test 11.1 or 11.7


		// Test 11.11
		else if ( gTestSubsection == 12 )
		{
			// IGNCNTR and OBDCOND must have the same values as recorded in step 11.7
			if ( gstResponse[EcuIdx].IPD_Test11_12[IPD_IGNCNTR_INDEX]  != gstResponse[EcuIdx].IPD_Test11_7[IPD_IGNCNTR_INDEX] )
			{
				if ( bDisplayErrorMsg == TRUE )
				{
					Log ( FAILURE, SCREENOUTPUTON, LOGOUTPUTON, NO_PROMPT,
					      "ECU %-8X  IGNCNTR different from value in test 11.5\n",
					      GetEcuId ( EcuIdx ) );
				}
				eRetCode |= FAIL;
			}
	
			if ( gstResponse[EcuIdx].IPD_Test11_12[IPD_OBDCOND_INDEX]  != gstResponse[EcuIdx].IPD_Test11_7[IPD_OBDCOND_INDEX] )
			{
				if ( bDisplayErrorMsg == TRUE )
				{
					Log ( FAILURE, SCREENOUTPUTON, LOGOUTPUTON, NO_PROMPT,
					      "ECU %-8X  OBDCOND different from value in test 11.5\n",
					      GetEcuId ( EcuIdx ) );
				}
				eRetCode |= FAIL;
			}
		}  // end if ( gTestSubsection == 12 )  // Test 11.12
	}  // end if ( bIPDSupported )

	return eRetCode;
}


/*******************************************************************************
**
**  Function:  EvaluateDTCIUMPRData
**
**  Purpose:
**
*******************************************************************************/
STATUS EvaluateDTCIUMPRData ( BYTE    EcuIdx,
                              DWORD  *pIPTStatusFlags,
                              BOOL    bDisplayErrorMsg )
{
	STATUS  eRetCode = PASS;

	BOOL   bValueSupported = FALSE;
	WORD   Index;
	int    count1;
	int    count2;
	DWORD  FlagMask;
	BYTE   FlagIndex;
	BYTE   FlagMaskIndex;


	// if DTCIUMPR not supported, then cannot do comparsions
	if ( gstResponse[EcuIdx].bDTCIUMPRSupported == TRUE )
	{
		// Test 11.12
		if ( gTestSubsection == 12 )
		{
			// OBD condition counters must continue to be non-zero if they were non-zero in test 11.7
			for ( Index = 0;
			      Index < gstResponse[EcuIdx].DTCIUMPRCount;
			      Index++ )
			{
				if ( gstResponse[EcuIdx].DTCIUMPR_Test11_7[Index].CondCounts  != 0 &&
				     gstResponse[EcuIdx].DTCIUMPR_Test11_12[Index].CondCounts == 0 )
				{
					if ( bDisplayErrorMsg == TRUE )
					{
						Log ( FAILURE, SCREENOUTPUTON, LOGOUTPUTON, NO_PROMPT,
						      "ECU %-8X  An OBD counter which was non-zero in test 11.7 is now zero\n",
						      GetEcuId ( EcuIdx ) );
					}
					eRetCode |= FAIL;
				}
			}
		}  // end if ( gTestSubsection == 12 )  // Test 11.12
	
		// Test 11.1 or 11.7
		else
		{
			FlagMask = 0xFFFFFFFB;  // skip IPD flags
			FlagMaskIndex = 1;      // skip IPD flags
			FlagIndex = 0;

			for ( Index = 0;
			      Index < gstResponse[EcuIdx].DTCIUMPRCount;
			      Index ++, FlagMaskIndex++ )
			{
				if ( ((FlagMaskIndex) % 16) == 0 )
				{
					FlagMask = 0xFFFFFFFE;
					FlagMaskIndex = 0;
					FlagIndex++;
				}

				bValueSupported = FALSE;

				// Check for change
				if ( gTestSubsection == 7 )
				{
					// Check if DTCIUMPR value is supported
					if ( gstResponse[EcuIdx].DTCIUMPR_Test11_7[Index].CompCounts  > 0 ||
					     gstResponse[EcuIdx].DTCIUMPR_Test11_7[Index].CondCounts  > 0 ||
					     gstResponse[EcuIdx].DTCIUMPR_Test10_10[Index].CompCounts > 0 ||
					     gstResponse[EcuIdx].DTCIUMPR_Test10_10[Index].CondCounts > 0 )
					{
						bValueSupported = TRUE;
					}
					count1 = gstResponse[EcuIdx].DTCIUMPR_Test11_7[Index].CompCounts - gstResponse[EcuIdx].DTCIUMPR_Test10_10[Index].CompCounts;
					count2 = gstResponse[EcuIdx].DTCIUMPR_Test11_7[Index].CondCounts - gstResponse[EcuIdx].DTCIUMPR_Test10_10[Index].CondCounts;
				}
				else
				{
					// Check if DTCIUMPR value is supported
					if ( gstResponse[EcuIdx].DTCIUMPR_Test11_CurrentDisplayData[Index].CompCounts > 0 ||
					     gstResponse[EcuIdx].DTCIUMPR_Test11_CurrentDisplayData[Index].CondCounts > 0 ||
					     gstResponse[EcuIdx].DTCIUMPR_Test10_10[Index].CompCounts > 0 ||
					     gstResponse[EcuIdx].DTCIUMPR_Test10_10[Index].CondCounts > 0 )
					{
						bValueSupported = TRUE;
					}
					count1 = gstResponse[EcuIdx].DTCIUMPR_Test11_CurrentDisplayData[Index].CompCounts - gstResponse[EcuIdx].DTCIUMPR_Test10_10[Index].CompCounts;
					count2 = gstResponse[EcuIdx].DTCIUMPR_Test11_CurrentDisplayData[Index].CondCounts - gstResponse[EcuIdx].DTCIUMPR_Test10_10[Index].CondCounts;
				}

				if ( count1 == 0 && count2 == 0 && bValueSupported == FALSE )
				{
					if ( gTestSubsection == 7 &&
					     bDisplayErrorMsg == TRUE )
					{
						Log ( FAILURE, SCREENOUTPUTON, LOGOUTPUTON, NO_PROMPT,
						      "ECU %-8X  Monitor has not changed since Test 10.10\n",
						      GetEcuId ( EcuIdx ) );
					}

					// can't tell which bank is supported, so highlight them both till we see one change
					pIPTStatusFlags[FlagIndex] &= FlagMask;
					FlagMask = (FlagMask << 1) + 1;
					pIPTStatusFlags[FlagIndex] &= FlagMask;
					FlagMask = (FlagMask << 1) + 1;

					eRetCode |= FAIL;
				}
				else
				{
					if ( (count1 == 0 || count2 == 0) &&
					     bValueSupported == TRUE )
					{
						if ( gTestSubsection == 7 &&
						     bDisplayErrorMsg == TRUE )
						{
							Log ( FAILURE, SCREENOUTPUTON, LOGOUTPUTON, NO_PROMPT,
							      "ECU %-8X  Monitor has not changed since Test 10.10\n",
							      GetEcuId ( EcuIdx ) );
						}
						eRetCode |= FAIL;
					}

					if ( count1 == 0 && bValueSupported == TRUE )
					{
						pIPTStatusFlags[FlagIndex] &= FlagMask;
					}
					FlagMask = (FlagMask << 1) + 1;

					if ( count2 == 0 && bValueSupported == TRUE )
					{
						pIPTStatusFlags[FlagIndex] &= FlagMask;
					}
					FlagMask = (FlagMask << 1) + 1;
				}
			}  // end for ( DTCIUMPRIndex
		}  // end elseif Test 11.1 or 11.5
	}  // end if (bDTCIUMPRSupported)

	return eRetCode;
}


/*******************************************************************************
**
**  Function:  EvaluateINF14
**
**  Purpose:
**
*******************************************************************************/
STATUS EvaluateINF14 ( void )
{
	REQ_MSG stReqMsg;
	BYTE    EcuIdx;
	STATUS  eRetCode = PASS;


	stReqMsg.SID      = 0x22;
	stReqMsg.NumIds   = 1;
	stReqMsg.u.DID[0] = 0xF814;
	if ( IsIDSupported ( ALLECUS, INFREQUEST, INF_TYPE_EVAP_DIST ) == TRUE )
	{
		if ( RequestSID ( &stReqMsg, REQ_MSG_NORMAL ) == FAIL )
		{
			Log ( FAILURE, SCREENOUTPUTON, LOGOUTPUTON, NO_PROMPT,
			      "INF $F814 request\n" );
			return FAIL;
		}

		for ( EcuIdx = 0;
		      EcuIdx < gNumOfECUs;
		      EcuIdx++ )
		{
			if ( IsIDSupported ( EcuIdx, INFREQUEST, INF_TYPE_EVAP_DIST ) == TRUE &&
			     VerifyINF14Data ( EcuIdx ) == FAIL )
			{
				eRetCode = FAIL;
			}
		}
	}

	return eRetCode;
}


/*******************************************************************************
**
**  Function:  SavePIDF501Data
**
**  Purpose:
**
*******************************************************************************/
void SaveIMReadiness ( BYTE EcuIdx )
{
	PID  *pPid;


	pPid = (PID *)&gstResponse[EcuIdx].PID[0];

	// if Misfire supported
	if ( pPid->Data[1] & 0x01 )
	{
		Test11IMStatus[EcuIdx][0] = (pPid->Data[1] & 0x10) ? INCOMPLETE : COMPLETE;
	}
	else
	{
		Test11IMStatus[EcuIdx][0] = NOTSUPPORTED;
	}

	// if Fuel supported
	if ( pPid->Data[1] & 0x02 )
	{
		Test11IMStatus[EcuIdx][1] = (pPid->Data[1] & 0x20) ? INCOMPLETE : COMPLETE;
	}
	else
	{
		Test11IMStatus[EcuIdx][1] = NOTSUPPORTED;
	}

	// if CCM supported
	if ( pPid->Data[1] & 0x04 )
	{
		Test11IMStatus[EcuIdx][2] = (pPid->Data[1] & 0x40) ? INCOMPLETE : COMPLETE;
	}
	else
	{
		Test11IMStatus[EcuIdx][2] = NOTSUPPORTED;
	}

	// if Cool supported
	if ( pPid->Data[1] & 0x08 )
	{
		Test11IMStatus[EcuIdx][3] = (pPid->Data[1] & 0x80) ? INCOMPLETE : COMPLETE;
	}
	else
	{
		Test11IMStatus[EcuIdx][3] = NOTSUPPORTED;
	}

	// if Catalyst (CAT) (Gasoline) or NMHC Catalyst (HCCAT) (Diesel) supported
	if ( pPid->Data[2] & 0x01 || pPid->Data[2] & 0x02 )
	{
		Test11IMStatus[EcuIdx][4] = ((pPid->Data[2] & 0x10 ) || (pPid->Data[2] & 0x20)) ? INCOMPLETE : COMPLETE;
	}
	else
	{
		Test11IMStatus[EcuIdx][4] = NOTSUPPORTED;
	}

	// if Heated Catalyst (HCAT) (Gasoline) or NOx/SCR Aftertreatment (NCAT) (Diesel) supported
	if ( pPid->Data[2] & 0x04 || pPid->Data[2] & 0x08 )
	{
		Test11IMStatus[EcuIdx][5] = ((pPid->Data[2] & 0x40) || (pPid->Data[2] & 0x80)) ? INCOMPLETE : COMPLETE;
	}
	else
	{
		Test11IMStatus[EcuIdx][5] = NOTSUPPORTED;
	}

	// if Evaportive Emissions (Gasoline) supported
	if ( pPid->Data[3] & 0x01 )
	{
		Test11IMStatus[EcuIdx][6] = (pPid->Data[3] & 0x10) ? INCOMPLETE :COMPLETE;
	}
	else
	{
		Test11IMStatus[EcuIdx][6] = NOTSUPPORTED;
	}

	// if Secondary Air (Air) (Gasoline) or Boost Pressure (BP) (Diesel) supported
	if ( pPid->Data[3] & 0x02 || pPid->Data[3] & 0x04 )
	{
		Test11IMStatus[EcuIdx][7] = ((pPid->Data[3] & 0x20) || (pPid->Data[3] & 0x40)) ? INCOMPLETE : COMPLETE;
	}
	else
	{
		Test11IMStatus[EcuIdx][7] = NOTSUPPORTED;
	}

	// if Particulate Filter (PF) supported
	if ( pPid->Data[3] & 0x08 )
	{
		Test11IMStatus[EcuIdx][8] = (pPid->Data[3] & 0x80) ? INCOMPLETE : COMPLETE;
	}
	else
	{
		Test11IMStatus[EcuIdx][8] = NOTSUPPORTED;
	}

	// if Exhaust Gas Sensor (EGS) supported
	if ( pPid->Data[4] & 0x01 )
	{
		// if completed
		Test11IMStatus[EcuIdx][9] = (pPid->Data[4] & 0x10) ? INCOMPLETE : COMPLETE;
	}
	else
	{
		Test11IMStatus[EcuIdx][9] = NOTSUPPORTED;
	}

	// if crankcase ventilation (CV) supported
	if ( pPid->Data[4] & 0x02 )
	{
		Test11IMStatus[EcuIdx][10] = (pPid->Data[4] & 0x20) ? INCOMPLETE : COMPLETE;
	}
	else
	{
		Test11IMStatus[EcuIdx][10] = NOTSUPPORTED;
	}

	// if Exhaust Gas Recirculation (EGR) supported
	if ( pPid->Data[4] & 0x04 )
	{
		Test11IMStatus[EcuIdx][11] = (pPid->Data[4] & 0x40) ? INCOMPLETE : COMPLETE;
	}
	else
	{
		Test11IMStatus[EcuIdx][11] = NOTSUPPORTED;
	}

	// if Variable Valve Timing (VVT) supported
	if ( pPid->Data[4] & 0x08 )
	{
		Test11IMStatus[EcuIdx][12] = (pPid->Data[4] & 0x80) ? INCOMPLETE : COMPLETE;
	}
	else
	{
		Test11IMStatus[EcuIdx][12] = NOTSUPPORTED;
	}

	// if Direct Ozone Reduction (DOR) supported
	if ( pPid->Data[5] & 0x01 )
	{
		// if completed
		Test11IMStatus[EcuIdx][13] = (pPid->Data[5] & 0x10) ? INCOMPLETE : COMPLETE;
	}
	else
	{
		Test11IMStatus[EcuIdx][13] = NOTSUPPORTED;
	}

	// if Cold Start Emission Reduction (CSER) supported
	if ( pPid->Data[5] & 0x02 )
	{
		Test11IMStatus[EcuIdx][14] = (pPid->Data[5] & 0x20) ? INCOMPLETE : COMPLETE;
	}
	else
	{
		Test11IMStatus[EcuIdx][14] = NOTSUPPORTED;
	}

	// if NOx Adsorber (NAC) supported
	if ( pPid->Data[5] & 0x04 )
	{
		Test11IMStatus[EcuIdx][15] = (pPid->Data[5] & 0x40) ? INCOMPLETE : COMPLETE;
	}
	else
	{
		Test11IMStatus[EcuIdx][15] = NOTSUPPORTED;
	}

	// if Other (OTH) supported
	if ( pPid->Data[5] & 0x08 )
	{
		Test11IMStatus[EcuIdx][16] = (pPid->Data[5] & 0x80) ? INCOMPLETE : COMPLETE;
	}
	else
	{
		Test11IMStatus[EcuIdx][16] = NOTSUPPORTED;
	}
}


/*******************************************************************************
**
**  Function:  SavePIDF501Data
**
**  Purpose:
**
*******************************************************************************/
BOOL SavePIDF501Data ( BYTE EcuIdx,
                       BOOL bInitialSave )
{
	PID  *pPid;
	BOOL  bUpdated = FALSE;


	pPid = (PID *)&gstResponse[EcuIdx].PID[0];

	if ( pPid->PIDLSB == 0x01 )
	{
		// note any differences
		if ( bInitialSave == TRUE ||
		     gstResponse[EcuIdx].PIDF501.Data[0] != pPid->Data[0] ||
		     gstResponse[EcuIdx].PIDF501.Data[1] != pPid->Data[1] ||
		     gstResponse[EcuIdx].PIDF501.Data[2] != pPid->Data[2] ||
		     gstResponse[EcuIdx].PIDF501.Data[3] != pPid->Data[3] ||
		     gstResponse[EcuIdx].PIDF501.Data[4] != pPid->Data[4] ||
		     gstResponse[EcuIdx].PIDF501.Data[5] != pPid->Data[5] )
		{
			gstResponse[EcuIdx].PIDF501.Data[0] = pPid->Data[0];
			gstResponse[EcuIdx].PIDF501.Data[1] = pPid->Data[1];
			gstResponse[EcuIdx].PIDF501.Data[2] = pPid->Data[2];
			gstResponse[EcuIdx].PIDF501.Data[3] = pPid->Data[3];
			gstResponse[EcuIdx].PIDF501.Data[4] = pPid->Data[4];
			gstResponse[EcuIdx].PIDF501.Data[5] = pPid->Data[5];

			SaveIMReadiness ( EcuIdx );

			bUpdated = TRUE;
		}  // end if F501 changed
	}  //  end if ( pPid->PIDLSB == 1 )

	return bUpdated;
}


/*******************************************************************************
**
**  Function:  ReadIPD
**
**  Purpose:
**
*******************************************************************************/
BOOL ReadIPD ( const char *szTestSectionEnd )
{
	char   buf[256];
	char  *p;
	BYTE   count;
	BYTE   NODI = 0;
	BYTE   EcuIdx;
	DWORD  EcuId = 0;


	// search for ECU ID
	while ( fgets ( buf, sizeof ( buf ), gLogFileHandle ) != 0 )
	{
		// stop if reached end of section
		if (substring ( buf, szTestSectionEnd ) != 0 )     // end of Test XX section
		{
			return FALSE;
		}

		// if found ECU ID, p points to start of ECU ID
		if ( (p = substring ( buf, szEcuId )) != 0 )
		{
			// point to number beyond '='
			p = substring ( p, "=" ) + 1;
			// convert the text to hexadecimal number
			EcuId = strtoul ( p, NULL, 16 );
			break;
		}
	}

	// find matching ECU ID in existing ECU data
	for ( EcuIdx = 0;
	      EcuIdx < gUserNumOfECUs;
	      EcuIdx++)
	{
		if ( EcuId == GetEcuId ( EcuIdx ) )
		{
			break;
		}
	}

	// if didn't find a matching ECU ID, something is wrong, stop
	if ( EcuIdx >= gUserNumOfECUs )
	{
		return FALSE;
	}


	// clear ECU's IPD support by default
	gstResponse[EcuIdx].bIPDSupported = FALSE;

	// search for IPT SIZE
	while ( fgets ( buf, sizeof ( buf ), gLogFileHandle ) != 0 )
	{
		// stop if reached end of section
		if ( substring ( buf, szTestSectionEnd ) != 0 )     // end of Test XX section
		{
			return FALSE;
		}

		// if found IPD SIZE, p points to start of IPD SIZE
		if ( (p = substring ( buf, szIpdSize )) != 0 )
		{
			// point to number beyond '='
			p = substring ( p, "=" ) + 1;
			NODI = (BYTE)strtod ( p, NULL );
			break;
		}
	}

	// if NODI equals 0, there is no data and IPD is not supported
	if ( NODI == 0 )
	{
		return TRUE;
	}

	// read counters
	for ( count = 0;
	      count < NODI;
	      count++)
	{
		if ( fgets ( buf, sizeof ( buf ), gLogFileHandle ) == 0 )
		{
			return FALSE;
		}
		// stop if reached end of section
		if ( substring ( buf, szTestSectionEnd ) != 0 )     // end of Test XX section
		{
			return FALSE;
		}

		// if found IPD, copy it to appropriate ECU's data
		if ( (p = substring ( buf, szIpdName[count] )) != 0 )
		{
			// point to number beyond '='
			p = substring ( p, "=" ) + 1;
			gstResponse[EcuIdx].IPD_Test10_10[count] = (unsigned short)strtoul ( p, NULL, 10 );
		}
		else
		{
			return FALSE;
		}
	}

	// IPD valid
	gstResponse[EcuIdx].bIPDSupported = TRUE;

	return TRUE;
}


/*******************************************************************************
**
**  Function:  CheckIPD
**
**  Purpose:
**
*******************************************************************************/
BOOL CheckIPD ( BYTE  EcuIdx )
{
	int index;
	BOOL bUpdated = FALSE;


	// copy current counter values for active ECUs
	for ( index = 0;
	      index < 2;
	      index++ )
	{
		// note changes
		if ( gstResponse[EcuIdx].IPD_Test11_CurrentDisplayData[index] != gstResponse[EcuIdx].IPD[index] )
		{
			// save current value of rate based counters
			gstResponse[EcuIdx].IPD_Test11_CurrentDisplayData[index] = gstResponse[EcuIdx].IPD[index];

			bUpdated = TRUE;
		}
	}

	return bUpdated;
}


/*******************************************************************************
**
**  Function:  ReadDTCIUMPRData
**
**  Purpose:
**
*******************************************************************************/
BOOL ReadDTCIUMPRData ( const char *szTestSectionEnd )
{
	char   buf[256];
	char  *p;
	WORD   IUMPRcount;
	BYTE   count;
	WORD   Index;
	char   Type;
	WORD   Dtc;
	BYTE   EcuIdx;
	DWORD  EcuId = 0;


	// search for ECU ID
	while ( fgets ( buf, sizeof ( buf ), gLogFileHandle ) != 0 )
	{
		// stop if reached end of section
		if (substring ( buf, szTestSectionEnd ) != 0 )     // end of Test XX section
		{
			return FALSE;
		}

		// if found ECU ID, p points to start of ECU ID
		if ((p = substring ( buf, szEcuId )) != 0 )
		{
			// point to number beyond '='
			p = substring ( p, "=" ) + 1;
			// convert the text to hexadecimal number
			EcuId = strtoul ( p, NULL, 16 );
			break;
		}
	}

	// find matching ECU ID in existing ECU data
	for ( EcuIdx = 0;
	      EcuIdx < gUserNumOfECUs;
	      EcuIdx++)
	{
		if ( EcuId == GetEcuId(EcuIdx) )
		{
			break;
		}
	}

	// if didn't find a matching ECU ID, something is wrong, stop
	if ( EcuIdx >= gUserNumOfECUs )
	{
		return FALSE;
	}

	// clear ECU's IUMPR support by default
	gstResponse[EcuIdx].bDTCIUMPRSupported = FALSE;

	// search for IUMPR SIZE
	while ( fgets ( buf, sizeof ( buf ), gLogFileHandle ) != 0 )
	{
		// stop if reached end of section
		if ( substring ( buf, szTestSectionEnd ) != 0 )     // end of Test XX section
		{
			return FALSE;
		}

		// if found IUMPR SIZE, p points to start of IUMPR SIZE
		if ( (p = substring ( buf, szIumprSize )) != 0)
		{
			// point to number beyond '='
			p = substring ( p, "=" ) + 1;
			gstResponse[EcuIdx].DTCIUMPRCount = (unsigned int)strtod ( p, NULL );
			break;
		}
	}

	// if gstResponse[EcuIdx].DTCIUMPRCount equals 0, there is no data and IUMPR is not supported
	if ( gstResponse[EcuIdx].DTCIUMPRCount == 0 )
	{
		return TRUE;
	}

	// read counters
	for ( IUMPRcount = 0;
	      IUMPRcount < (gstResponse[EcuIdx].DTCIUMPRCount);
	      IUMPRcount++ )
	{
		// there are two entries for each DTCIUMPR (Numerator and Denominator)
		for ( count = 0;
		      count < 2;
		      count++ )
		{
			if ( fgets ( buf, sizeof ( buf ), gLogFileHandle ) == 0 )
			{
				return FALSE;
			}
			// stop if reached end of section
			if ( substring ( buf, szTestSectionEnd ) != 0 )     // end of Test XX section
			{
				return FALSE;
			}

			// if line contains "DTC", p points to start of "DTC"
			if ( (p = substring ( buf, szIumprName )) != 0 )
			{
				// point to beyond "DTC ", the DTC Number
				p += 4;
				// convert the text to hexadecimal DTC Number
				Type = *p++;
				Dtc = (WORD)strtoul ( p, NULL, 16 );
				for ( Index = 0;
				      Index < DTCTypeCharacterSize;
				      Index++ )
				{
					if ( DTCTypeCharacter[Index] == Type )
					{
						Dtc += (Index<<28);
					}
				}
				gstResponse[EcuIdx].DTCIUMPR_Test10_10[IUMPRcount].Record.Dtc.HighByte = (BYTE)((Dtc & 0xFF00)>>8);
				gstResponse[EcuIdx].DTCIUMPR_Test10_10[IUMPRcount].Record.Dtc.MidByte  = (BYTE)(Dtc & 0x00FF);

				// point to Failure type (skip past the DTC Number and space seperating Failure Type)
				p += 5;

				gstResponse[EcuIdx].DTCIUMPR_Test10_10[IUMPRcount].Record.Dtc.LowByte        = (BYTE)(strtoul ( p, NULL, 16 ));

				// also make a copy into DTCIUMPRList because this is a test reentry and original requests were skipped
				// need to be filled for future requests to work
				gstResponse[EcuIdx].DTCIUMPRList[IUMPRcount].Record.Dtc.HighByte = gstResponse[EcuIdx].DTCIUMPR_Test10_10[IUMPRcount].Record.Dtc.HighByte;
				gstResponse[EcuIdx].DTCIUMPRList[IUMPRcount].Record.Dtc.MidByte  = gstResponse[EcuIdx].DTCIUMPR_Test10_10[IUMPRcount].Record.Dtc.MidByte;
				gstResponse[EcuIdx].DTCIUMPRList[IUMPRcount].Record.Dtc.LowByte  = gstResponse[EcuIdx].DTCIUMPR_Test10_10[IUMPRcount].Record.Dtc.LowByte;

				// if line contains "NUMERATOR", p points to start of "NUMERATOR"
				if ( (p = substring( buf, szIumprData1 )) != 0 )
				{
					// point to number beyond '='
					p = substring ( p, "=" ) + 1;
					gstResponse[EcuIdx].DTCIUMPR_Test10_10[IUMPRcount].CompCounts = (unsigned short)strtoul ( p, NULL, 10 );
				}

				// if line contains "DENOMINATOR", p points to start of "DENOMINATOR"
				else if ( (p = substring( buf, szIumprData2 )) != 0 )
				{
					// point to number beyond '='
					p = substring ( p, "=" ) + 1;
					gstResponse[EcuIdx].DTCIUMPR_Test10_10[IUMPRcount].CondCounts = (unsigned short)strtoul ( p, NULL, 10 );
				}
				else
				{
					return FALSE;
				}

				// check if the DTC is already in the list
				for ( Index = 0;
				      Index < DTCIUMPRListCount;
				      Index++ )
				{
					if ( DTCIUMPRList[Index].HighByte == gstResponse[EcuIdx].DTCIUMPR_Test10_10[IUMPRcount].Record.Dtc.HighByte &&
					     DTCIUMPRList[Index].MidByte  == gstResponse[EcuIdx].DTCIUMPR_Test10_10[IUMPRcount].Record.Dtc.MidByte  &&
					     DTCIUMPRList[Index].LowByte  == gstResponse[EcuIdx].DTCIUMPR_Test10_10[IUMPRcount].Record.Dtc.LowByte )
					{
						break;
					}
				}

				// if the DTC wasn't in the list, add it
				if ( Index == DTCIUMPRListCount )
				{
					DTCIUMPRList[Index].HighByte = gstResponse[EcuIdx].DTCIUMPR_Test10_10[IUMPRcount].Record.Dtc.HighByte;
					DTCIUMPRList[Index].MidByte  = gstResponse[EcuIdx].DTCIUMPR_Test10_10[IUMPRcount].Record.Dtc.MidByte;
					DTCIUMPRList[Index].LowByte  = gstResponse[EcuIdx].DTCIUMPR_Test10_10[IUMPRcount].Record.Dtc.LowByte;
					DTCIUMPRListCount++;
				}
			}
			else
			{
				return FALSE;
			}
		}
	}

	// IUMPR data valid
	gstResponse[EcuIdx].bDTCIUMPRSupported = TRUE;

	return TRUE;
}


/*******************************************************************************
**
**  Function:  ReadDataFromLogFile
**
**  Purpose:   To read data of an earlier test from the log file.
**             The data is expected to be in the following order
**                 IPD   data for each ECU
**                 IUMPR data for each ECU
**
*******************************************************************************/
BOOL ReadDataFromLogFile ( const char *szTestSectionStart,
                           const char *szTestSectionEnd )
{
	char buf[256];
	BYTE EcuIdx;

	// log file should be open
	if ( gLogFileHandle == NULL )
	{
		Log ( FAILURE, SCREENOUTPUTON, LOGOUTPUTOFF, NO_PROMPT,
		      "Log File not open\n" );
		return FALSE;
	}

	// search from beginning of file
	fseek ( gLogFileHandle, 0, SEEK_SET );

	// read each line from the file until end
	while ( fgets ( buf, sizeof ( buf ), gLogFileHandle ) != 0 )
	{
		// if the start of the test section has been found, save the data into the appropriate ECU
		if ( substring ( buf, szTestSectionStart ) != 0 )
		{
			// read the IPD values for all ECUs
			for ( EcuIdx = 0;
			      EcuIdx < gNumOfECUs;
			      EcuIdx++ )
			{
				if ( ReadIPD ( szTestSectionEnd ) == FALSE )
				{
					break;
				}
			}

			// clear DTCIUMPR list count
			DTCIUMPRListCount = 0;
			// read the IUMPR values for all ECUs
			for ( EcuIdx = 0;
			      EcuIdx < gNumOfECUs;
			      EcuIdx++ )
			{
				if ( ReadDTCIUMPRData ( szTestSectionEnd ) == FALSE )
				{
					break;
				}
			}

			fseek ( gLogFileHandle, 0, SEEK_END );

			return TRUE;
		}
	}

	// move to end of file
	fseek ( gLogFileHandle, 0, SEEK_END );

	return FALSE;
}


/*******************************************************************************
**
**  Function:  CheckIUMPRData
**
**  Purpose:
**
*******************************************************************************/
BOOL CheckIUMPRData ( BYTE  EcuIdx,
                      BOOL  bUpdateValue )
{
	WORD index;
	BOOL bUpdated = FALSE;


	// copy current counter values for active ECUs
	for ( index = 0;
	      index < gstResponse[EcuIdx].DTCIUMPRCount;
	      index++ )
	{
		// note changes to the numerator and denominator
		if ( gstResponse[EcuIdx].DTCIUMPR_Test11_CurrentDisplayData[index].CompCounts != gstResponse[EcuIdx].DTCIUMPRList[index].CompCounts )
		{
			if ( bUpdateValue == TRUE )
			{
				// save current value of rate based counters
				gstResponse[EcuIdx].DTCIUMPR_Test11_CurrentDisplayData[index].CompCounts = gstResponse[EcuIdx].DTCIUMPRList[index].CompCounts;
			}

			bUpdated = TRUE;
		}

		if ( gstResponse[EcuIdx].DTCIUMPR_Test11_CurrentDisplayData[index].CondCounts != gstResponse[EcuIdx].DTCIUMPRList[index].CondCounts )
		{
			if ( bUpdateValue == TRUE )
			{
				// save current value of rate based counters
				gstResponse[EcuIdx].DTCIUMPR_Test11_CurrentDisplayData[index].CondCounts = gstResponse[EcuIdx].DTCIUMPRList[index].CondCounts;
			}

			bUpdated = TRUE;
		}
	}

	return bUpdated;
}


/*******************************************************************************
**
**  Function:  UpdateIPDDisplay
**
**  Purpose:
**
*******************************************************************************/
void UpdateIPDDisplay ( BYTE         EcuIdx,
                        DWORD       *pIPTStatusFlags )
{
	// if IPD is supported
	if ( gstResponse[EcuIdx].bIPDSupported == TRUE )
	{
		// update OBDCOND counter
		if ( (pIPTStatusFlags[0] & 0x01) == 0 )
		{
			setrgb ( HIGHLIGHTED_TEXT );
		}
		pIPTStatusFlags[0] >>= 1;
		SetFieldDec_1 ( OBD_MONITOR_COND_INDEX, gstResponse[EcuIdx].IPD_Test11_CurrentDisplayData[0] );
		setrgb ( NORMAL_TEXT );

		// update IGN counter
		if ( (pIPTStatusFlags[0] & 0x01) == 0 )
		{
			setrgb ( HIGHLIGHTED_TEXT );
		}
		pIPTStatusFlags[0] >>= 1;
		SetFieldDec_1 ( IGNITION_COUNTER_INDEX, gstResponse[EcuIdx].IPD_Test11_CurrentDisplayData[1] );
		setrgb ( NORMAL_TEXT );
	}
	else
	{
		SetFieldText_1 ( OBD_MONITOR_COND_INDEX, "Not Supported" );

		SetFieldText_1 ( IGNITION_COUNTER_INDEX, "Not Supported" );
	}
}


/*******************************************************************************
**
**  Function:  UpdateDTCIUMPRDisplay
**
**  Purpose:
**
*******************************************************************************/
void UpdateDTCIUMPRDisplay ( BYTE   EcuIdx,
                             BYTE   DataPage,
                             DWORD *pIPTStatusFlags )
{
	WORD  Index;
	char  DTCstring[6];


	// If Data Page 1 (IM Readiness, IPD and the first 13 IUMPR)
	if ( DataPage == 1 )// update rate based counter values
	{
		// if IUMPR is supported
		if ( gstResponse[EcuIdx].bDTCIUMPRSupported == TRUE )
		{
			for ( Index = 0;
			      Index < IUMPR_1_COL_LEN;
			      Index++ )
			{
				// write DTC
				sprintf_s ( DTCstring, sizeof ( DTCstring ),
				            "%c%02X%02X",
				            DTCTypeCharacter[(gstResponse[EcuIdx].DTCIUMPR_Test10_10[Index].Record.Dtc.HighByte & 0xC0) >> 6],  // 1st character (P, C, B, U)
				            gstResponse[EcuIdx].DTCIUMPR_Test10_10[Index].Record.Dtc.HighByte & 0x3F,                           // 2nd (0,1,2,3) and 3rd (0-F) characters
				            gstResponse[EcuIdx].DTCIUMPR_Test10_10[Index].Record.Dtc.MidByte );                                 // 4th and 5th characters (0-F)
				SetFieldText_1 ( IUMPR_DTC_INDEX+(Index), DTCstring );
	
				// write initial values for rate based counters
				SetFieldDec_1 ( IUMPR_INIT_INDEX+(2*Index),   gstResponse[EcuIdx].DTCIUMPR_Test10_10[Index].CompCounts );
	
				SetFieldDec_1 ( IUMPR_INIT_INDEX+(2*Index)+1, gstResponse[EcuIdx].DTCIUMPR_Test10_10[Index].CondCounts );
	
				// write current values for rate based counters
				if ( (pIPTStatusFlags[0] & 0x01) == 0 )
				{
					setrgb ( HIGHLIGHTED_TEXT );
				}
				pIPTStatusFlags[0] >>= 1;
				SetFieldDec_1 ( IUMPR_CUR_INDEX+(2*Index),   gstResponse[EcuIdx].DTCIUMPR_Test11_CurrentDisplayData[Index].CompCounts );
				setrgb ( NORMAL_TEXT );
	
				if ( (pIPTStatusFlags[0] & 0x01) == 0 )
				{
					setrgb ( HIGHLIGHTED_TEXT );
				}
				pIPTStatusFlags[0] >>= 1;
				SetFieldDec_1 ( IUMPR_CUR_INDEX+(2*Index)+1, gstResponse[EcuIdx].DTCIUMPR_Test11_CurrentDisplayData[Index].CondCounts );
				setrgb ( NORMAL_TEXT );
			}  // end for ( index )
		}  // end if ( bDTCIUMPRSupported )
		else
		{
			SetFieldText_1 ( IUMPR_INIT_INDEX, "Not Supported" );

			SetFieldText_1 ( IUMPR_CUR_INDEX,  "Not Supported" );
		}
	}  // end if ( DataPage == 1 )

	// if Data Page is greater than 1, display page 2+ data layout
	else
	{
		BYTE  DataOffset;
		BYTE  Col2Start;
		BYTE  Col2End;
		BYTE  DtcColOffset;
		BYTE  IniColOffset;
		BYTE  CurColOffset;
		BYTE  FieldOffset;
		BYTE  StatusFlagIndex;


		// Calculate the offset into the ECU's IUMPR array
		DataOffset = 13 + ((DataPage - 2) * (2 * IUMPR_2_COL_LEN));

		// find the flags for the IUMPR data on this Data Page
		if ( DataPage == 2 )
		{
			StatusFlagIndex = 0;
		}
		else
		{
			StatusFlagIndex = 2*(DataPage-2);
		}
		// skip the flags used on previous pages - 
		// for page 2, skip the OBDCOND, IGNCNTR (2) and IUMPR flags on page 1 (Numerator #1-13 and Denominator #1-13)(26)
		//             (Numerator #14-45 and Denominator #14-45 (64) are on page 2,
		//              they use a total of 64 flags: the last 6 bits of pIPTStatusFlags[0],
		//              all 32 bits of pIPTStatusFlags[1], and the first 28 bits of pIPTStatusFlags[2]),
		// for page 3, skip the flags done on page 2 (Numerator #14-45 and Denominator #14-45)
		//             (Numerator #46-47 and Denominator #46-47 are done on page 3,
		//              the flags for these are the last 4 bits of pIPTStatusFlags[2])
		pIPTStatusFlags[StatusFlagIndex] >>= 28;


		// if IUMPR is supported
		if ( gstResponse[EcuIdx].bDTCIUMPRSupported == TRUE )
		{
			// calculate indexes for start and end of column 2
			Col2Start = IUMPR_2_COL_LEN;
			Col2End   = Col2Start  + IUMPR_2_COL_LEN;
			DtcColOffset = IUMPR_COL1_DTC_INDEX;
			IniColOffset = IUMPR_COL1_INIT_INDEX;
			CurColOffset = IUMPR_COL1_CUR_INDEX;

			// write DTC IUMPR Column 1 and 2 data
			for ( Index = 0, FieldOffset = 0;
			      Index < Col2End &&
			      Index + DataOffset < gstResponse[EcuIdx].DTCIUMPRCount;
			      Index++, FieldOffset++ )
			{
				// if reached the end of the bits in the current array entry,
				// advance to the next and reset masks
				if ( (((Index*2) + 28 ) % 32) == 0 )
				{
					StatusFlagIndex++;
				}

				if ( Index == Col2Start )
				{
					DtcColOffset = IUMPR_COL2_DTC_INDEX;
					IniColOffset = IUMPR_COL2_INIT_INDEX;
					CurColOffset = IUMPR_COL2_CUR_INDEX;
					FieldOffset  = 0;
				}

				// write DTC
				sprintf_s ( DTCstring, sizeof ( DTCstring ),
				            "%c%02X%02X",
				            DTCTypeCharacter[(gstResponse[EcuIdx].DTCIUMPR_Test10_10[DataOffset+Index].Record.Dtc.HighByte & 0xC0) >> 6],  // 1st character (P, C, B, U)
				            gstResponse[EcuIdx].DTCIUMPR_Test10_10[DataOffset+Index].Record.Dtc.HighByte & 0x3F,                           // 2nd (0,1,2,3) and 3rd (0-F) characters
				            gstResponse[EcuIdx].DTCIUMPR_Test10_10[DataOffset+Index].Record.Dtc.MidByte );                                 // 4th and 5th characters (0-F)
				SetFieldText_2 ( DtcColOffset+FieldOffset, DTCstring );

				// write initial values for rate based counters
				SetFieldDec_2 ( IniColOffset+(2*FieldOffset),   gstResponse[EcuIdx].DTCIUMPR_Test10_10[DataOffset+Index].CompCounts );

				SetFieldDec_2 ( IniColOffset+(2*FieldOffset)+1, gstResponse[EcuIdx].DTCIUMPR_Test10_10[DataOffset+Index].CondCounts );

				// write current values for rate based counters
				if ( (pIPTStatusFlags[StatusFlagIndex] & 0x01) == 0 )
				{
					setrgb ( HIGHLIGHTED_TEXT );
				}
				pIPTStatusFlags[StatusFlagIndex] >>= 1;
				SetFieldDec_2 ( CurColOffset+(2*FieldOffset),   gstResponse[EcuIdx].DTCIUMPR_Test11_CurrentDisplayData[DataOffset+Index].CompCounts );
				setrgb ( NORMAL_TEXT );

				if ( (pIPTStatusFlags[StatusFlagIndex] & 0x01) == 0 )
				{
					setrgb ( HIGHLIGHTED_TEXT );
				}
				pIPTStatusFlags[StatusFlagIndex] >>= 1;
				SetFieldDec_2 ( CurColOffset+(2*FieldOffset)+1, gstResponse[EcuIdx].DTCIUMPR_Test11_CurrentDisplayData[DataOffset+Index].CondCounts );
				setrgb ( NORMAL_TEXT );
			}
		}
		else
		{
			SetFieldText_2 ( IUMPR_COL1_INIT_INDEX, "Not Supported" );

			SetFieldText_2 ( IUMPR_COL1_CUR_INDEX,  "Not Supported" );

			SetFieldText_2 ( IUMPR_COL2_INIT_INDEX, "Not Supported" );

			SetFieldText_2 ( IUMPR_COL2_CUR_INDEX,  "Not Supported" );
		}
	}
}

/*******************************************************************************
**
**  Function:  UpdatePID01Display
**
**  Purpose:
**
*******************************************************************************/
void UpdatePID01Display ( BYTE EcuIdx )
{
	int index;


	for ( index = 0;
	      index < IMREADINESS_SIZE;
	      index++ )
	{
		eCurrentIMStatus[index] = Test11IMStatus[EcuIdx][index];
		if ( Test11IMStatus[EcuIdx][index] == INCOMPLETE )
		{
			setrgb ( HIGHLIGHTED_TEXT );
		}
		else if ( Test11IMStatus[EcuIdx][index] == DISABLED )
		{
			setrgb ( RED_TEXT );
		}
		SetFieldText_IM ( IM_READINESS_INDEX+index, szIM_Status[Test11IMStatus[EcuIdx][index]] );
		setrgb ( NORMAL_TEXT );
	}
}


/*******************************************************************************
**
**  Function:  UpdateECUDisplay
**
**  Purpose:
**
*******************************************************************************/
void UpdateECUDisplay ( BYTE EcuPageOffset )
{
	BYTE           EcuIdx;


	for ( EcuIdx=0;
	      EcuIdx < gNumOfECUs &&
	      EcuIdx < MAX_ECUS_PER_PAGE;
	      EcuIdx++ )
	{
		SetFieldHex ( ECU_ID_INDEX+EcuIdx, GetEcuId ( EcuPageOffset+EcuIdx ) );

		// If I/M Readiness not supported for the current ECU, mark it as done
		if ( IsIDSupported ( EcuPageOffset+EcuIdx, PF5REQUEST, 0xF501 ) == FALSE )
		{
			gstResponse[EcuPageOffset+EcuIdx].StatusFlags |= IMREADYDONE;
		}

		// If IPD not supported for the current ECU, mark it as done
		if ( gstResponse[EcuPageOffset+EcuIdx].bIPDSupported      == FALSE )
		{
			gstResponse[EcuPageOffset+EcuIdx].StatusFlags |= IPDDONE;
		}

		// If IUMPR not supported for the current ECU, mark it as done
		if ( gstResponse[EcuPageOffset+EcuIdx].bDTCIUMPRSupported == FALSE )
		{
			gstResponse[EcuPageOffset+EcuIdx].StatusFlags |= IUMPRDONE;
		}

		// If I/M Readiness and IPD are done for the curnent ECU, mark the ECU as done
		if ( (gstResponse[EcuPageOffset+EcuIdx].StatusFlags & IMREADYDONE) != 0 &&
		     (gstResponse[EcuPageOffset+EcuIdx].StatusFlags & IPDDONE)     != 0 &&
		     (gstResponse[EcuPageOffset+EcuIdx].StatusFlags & IUMPRDONE)   != 0 )
		{
			gstResponse[EcuPageOffset+EcuIdx].StatusFlags |= ECUDONE;
		}

		// If I/M Readiness, IPD and IUMPR are done for the curnent ECU, mark the ECU as done
		if ( (gstResponse[EcuPageOffset+EcuIdx].StatusFlags & ECUDONE) != 0 )
		{
			SetFieldText ( ECU_STATUS_INDEX+EcuIdx, "Done" );
		}
		else
		{
			SetFieldText ( ECU_STATUS_INDEX+EcuIdx, "    " );
		}
	}
}


/*******************************************************************************
**
**  Function:  SelectECU
**
**  Purpose:
**
*******************************************************************************/
void SelectECU ( int NewIndex,
                 int OldIndex ,
                 int Offset )
{
	int EcuIdx;


	// un-highlite previous selection
	setrgb ( NORMAL_TEXT );
	EcuIdx = ECU_ID_INDEX + OldIndex;
	if ( ECU_ID_INDEX <= EcuIdx &&
	     EcuIdx <= (ECU_ID_INDEX+MAX_ECUS_PER_PAGE) )
	{
		SetFieldHex ( EcuIdx, GetEcuId ( Offset+OldIndex ) );
	}

	// highlite new selection
	setrgb ( HIGHLIGHTED_TEXT );
	EcuIdx = ECU_ID_INDEX + NewIndex;
	if ( ECU_ID_INDEX <= EcuIdx &&
	     EcuIdx <= (ECU_ID_INDEX+MAX_ECUS_PER_PAGE) )
	{
		SetFieldHex ( EcuIdx, GetEcuId ( Offset+NewIndex ) );
	}

	// restore screen attributes
	setrgb ( NORMAL_TEXT );
}


/*******************************************************************************
**
**  Function:  UpdateTestStatus
**
**  Purpose:
**
*******************************************************************************/
void UpdateTestStatus ( BYTE SubTestStatusFlags )
{
	if ( (SubTestStatusFlags & SUB_TEST_FAIL) == SUB_TEST_FAIL )
	{
		setrgb ( HIGHLIGHTED_TEXT );
		SetFieldText ( TESTSTATUS_INDEX, "FAILURE" );
		setrgb ( NORMAL_TEXT );
	}
	else
	{
		SetFieldText ( TESTSTATUS_INDEX, "Normal" );
	}

	if ( (SubTestStatusFlags & SUB_TEST_DTC) == SUB_TEST_DTC )
	{
		setrgb ( HIGHLIGHTED_TEXT );
		SetFieldText ( TESTSTATUS_INDEX + 1, "DTC Detected" );
		setrgb ( NORMAL_TEXT );
	}
	else
	{
		SetFieldText ( TESTSTATUS_INDEX + 1, "             " );
	}
}


/*******************************************************************************
**
**  Function:  ResetDisplay
**
**  Purpose:
**
*******************************************************************************/
void ResetDisplay ( BYTE EcuPageOffset,
                    BYTE CurrentEcuIdx,
                    BYTE EcuPage,
                    BYTE DataPage )
{
	BYTE   EcuIdx = EcuPageOffset + CurrentEcuIdx;
	DWORD  IPTStatusFlags[IPT_STATUS_FLAG_BYTES];      // Bit Map of IPT items completed for current EcuIdx

	unsigned int    LastDataPage = 1;
	char   String[8];


	if ( gstResponse[EcuIdx].DTCIUMPRCount > IUMPR_1_COL_LEN )
	{
		LastDataPage++;
		if ( gstResponse[EcuIdx].DTCIUMPRCount > (IUMPR_1_COL_LEN +(2*IUMPR_2_COL_LEN)) )
		{
			LastDataPage += (gstResponse[EcuIdx].DTCIUMPRCount - (IUMPR_1_COL_LEN + (2*IUMPR_2_COL_LEN)))/(2*IUMPR_2_COL_LEN);
			if ( (gstResponse[EcuIdx].DTCIUMPRCount - (IUMPR_1_COL_LEN + (2*IUMPR_2_COL_LEN)))%(2*IUMPR_2_COL_LEN) != 0 )
			{
				LastDataPage++;
			}
		}
	}
	sprintf_s ( String, 8,
	            "%d of %d",
	            DataPage,
	            LastDataPage );

	// clear display and print static ECU elements
	init_screen ( _string_elements_11, _num_string_elements_11 );

	// display ECU ID and Status
	UpdateECUDisplay ( EcuPageOffset );

	SelectECU ( CurrentEcuIdx, -1, EcuPageOffset );

	// display ECU page number
	SetFieldDec ( ECU_PAGE_INDEX, EcuPage );

	// Update Data page number
	SetFieldText ( DATA_PAGE_INDEX, String );

	// print static IM Readiness and IUMPR elements
	if ( DataPage == 1 )
	{
		place_screen_text ( _string_elements_imr, _num_string_elements_imr );
		place_screen_text ( _string_elements_iumpr1, _num_string_elements_iumpr1 );
	}
	else
	{
		place_screen_text ( _string_elements_iumpr2, _num_string_elements_iumpr2 );
	}

	// by default all items completed
	memset ( &IPTStatusFlags,
	         0xFF,
	         sizeof ( IPTStatusFlags ) );
	EvaluateIPD          ( EcuIdx, &IPTStatusFlags[0], FALSE );
	EvaluateDTCIUMPRData ( EcuIdx, &IPTStatusFlags[0], FALSE );
	if ( DataPage == 1 )
	{
		UpdatePID01Display ( EcuIdx );

		UpdateIPDDisplay ( EcuIdx, &IPTStatusFlags[0] );
	}

	UpdateDTCIUMPRDisplay ( EcuIdx, DataPage, &IPTStatusFlags[0] );
}


/*******************************************************************************
**
**  Function:  RunDynamicTest11
**
**  Purpose:
**
*******************************************************************************/
STATUS RunDynamicTest11 ( BOOL          bDisplayCARBTimers,
                          unsigned long EngineStartTimestampMsecs )
{
	REQ_MSG        stReqMsg;
	PID           *pPid;
	BYTE           EcuIdx;
	STATUS         eRetVal = PASS;
	STATUS         eRetCode = PASS;

	BYTE           CurrentEcuIdx;
	BYTE           EcuPageOffset;
	BYTE           EcuPage;
	BOOL           bECUPageUpdate = FALSE;

	WORD           DataPageOffset;
	BYTE           DataPage;
	BOOL           bDataPageUpdate = FALSE;
	WORD           OffsetIncrement = 0;

	BOOL           bIPDSupported = FALSE;
	BOOL           bIUMPRSupported = FALSE;
	DWORD          IPTStatusFlags[IPT_STATUS_FLAG_BYTES];      // Bit Map of IPT items completed for current EcuIdx
	WORD           IUMPRIndex;
	BOOL           bECUDataUpdate = FALSE;

	BYTE           EcuDoneCount = 0;

	BYTE           SubTestStatusFlags  = SUB_TEST_NORMAL;
	BYTE           LastTestStatusFlags = SUB_TEST_INITIAL; // set to force an update the first time

	BOOL           bLogMessage;
	BOOL           bIUMPRUpdate = FALSE;
	BOOL           bECUStatusUpdate = FALSE;

	BOOL           b1Sec = FALSE;
	unsigned long  LastCheckTimestampMsecs = 0;
	unsigned long  CurrentTimestampMsecs;

	// variables for CARB Timers
	unsigned short TestStartTimestampSecs;     // time the test started (in seconds)
	unsigned short LastCheckTimestampSecs = 0;
	unsigned short CheckDTCsTimerSecs = 0;     // # of seconds until DTCs are checked
	unsigned short IdleTimeSecs = 0;           // time at idle (in seconds, stops at 30)
	unsigned short AtSpeedTimeSecs = 0;        // time at speeds > 25mph (in seconds, stops at 300)
	unsigned short RPM = 0;                    // engine RPM (used if RUNTM is not supported)
	unsigned short SpeedMPH = 0;               // vehicle speed in MPH
	unsigned short SpeedKPH = 0;               // vehicle speed in KPH
	unsigned short RunTimeSecs = 0;            // engine run time (in seconds)
	BOOL           bRunTimeSupport;            // 1 = RUNTM PID supported
	BYTE           TestState = 0xFF;
	BYTE           TimeStatusFlags = 0;
	BOOL           bLoop;


	TestStartTimestampSecs = (unsigned short)(EngineStartTimestampMsecs / 1000);


	// determine PID support
	bRunTimeSupport = IsIDSupported ( ALLECUS, PIDREQUEST, 0xF41F );

	if ( IsIDSupported ( ALLECUS, PIDREQUEST, 0xF40C ) == FALSE &&
	     bRunTimeSupport == FALSE )
	{
		Log ( INFORMATION, SCREENOUTPUTON, LOGOUTPUTON, NO_PROMPT,
		      "PIDs $F40C (RPM) and $F41F (RUNTM) not supported\n" );
		bDisplayCARBTimers = FALSE;
	}

	// check for SID $22 OBDCOND and IGNCNTR support
	if ( IsIDSupported ( ALLECUS, INFREQUEST, INF_TYPE_IPD_UDS ) == TRUE )
	{
		bIPDSupported = TRUE;
	}

	// check for DTC IUMPR support
	if ( DTCIUMPRListCount != 0 )
	{
		bIUMPRSupported = TRUE;
	}


	// save PID $F501
	stReqMsg.SID      = 0x22;
	stReqMsg.NumIds   = 1;
	stReqMsg.u.DID[0] = 0xF501;
	if ( RequestSID ( &stReqMsg, REQ_MSG_NO_PERIODIC_DISABLE ) == FAIL )
	{
		for ( EcuIdx = 0;
		      EcuIdx < gNumOfECUs;
		      EcuIdx++ )
		{
			gstResponse[EcuIdx].PIDF501.Data[0] = 0xFF;
			gstResponse[EcuIdx].PIDF501.Data[1] = 0xFF;
			gstResponse[EcuIdx].PIDF501.Data[2] = 0xFF;
			gstResponse[EcuIdx].PIDF501.Data[3] = 0xFF;
			gstResponse[EcuIdx].PIDF501.Data[4] = 0xFF;
			gstResponse[EcuIdx].PIDF501.Data[5] = 0xFF;
		}

		Log ( FAILURE, SCREENOUTPUTOFF, LOGOUTPUTON, NO_PROMPT,
		      "PID $F501 request\n" );
	}
	else
	{
		for ( EcuIdx = 0;
		      EcuIdx < gUserNumOfECUs;
		      EcuIdx++ )
		{
			if ( gstResponse[EcuIdx].PIDSize > 0 )
			{
				SavePIDF501Data ( EcuIdx, TRUE );
			}
		}
	}


	// initialize Test 11 IPD and IUMPR display data
	for ( EcuIdx=0;
	      EcuIdx < gNumOfECUs;
	      EcuIdx++ )
	{
		// initialize current Test 11 IPD
		memcpy ( &gstResponse[EcuIdx].IPD_Test11_CurrentDisplayData,
		         &gstResponse[EcuIdx].IPD,
		         sizeof ( BYTE ) * 2 );

		// initialize current Test 11 IUMPR
		memcpy ( &gstResponse[EcuIdx].DTCIUMPR_Test11_CurrentDisplayData,
		         &gstResponse[EcuIdx].DTCIUMPRList,
		         sizeof ( DTCIUMPR ) * gstResponse[EcuIdx].DTCIUMPRCount );
	}  // end for ( EcuIdx )


	gbSuspendLogOutput = TRUE;
	gbSuspendScreenOutput = TRUE;


	// set static text elements based on Fueltype
	if ( gstUserInput.eFuelType == GASOLINE )
	{
		for ( BYTE index = 0;
		           index < 4;
		           index++ )
		{
			memcpy ( &_string_elements_imr[IMSTATUS_GD_ELEMENT1_START_INDEX + index],
			         &_string_elements11_Gas[index],
			         sizeof ( _string_elements11_Gas[index] ) );
		}
		memcpy ( &_string_elements_imr[IMSTATUS_GD_ELEMENT2_INDEX],
		         &_string_elements11_Gas[4],
		         sizeof ( _string_elements11_Gas[4] ) );
	}
	else
	{
		for ( BYTE index = 0;
		           index < 4;
		           index++ )
		{
			memcpy ( &_string_elements_imr[IMSTATUS_GD_ELEMENT1_START_INDEX + index],
			         &_string_elements11_Diesel[index],
			         sizeof ( _string_elements11_Diesel[index] ) );
		}
		memcpy ( &_string_elements_imr[IMSTATUS_GD_ELEMENT2_INDEX],
		         &_string_elements11_Diesel[4],
		         sizeof ( _string_elements11_Diesel[4] ) );
	}


	// initialize screen
	// ECU Page and Offset
	EcuPageOffset = 0;
	EcuPage = 1;

	CurrentEcuIdx = 0;

	// Data Page and Offset
	DataPage = 1;
	DataPageOffset = 0;

	ResetDisplay ( EcuPageOffset, CurrentEcuIdx, EcuPage, DataPage );


	// display CARB Timers, if required
	if ( bDisplayCARBTimers == TRUE )
	{
		// add CARB Timer display text to the screen
		place_screen_text ( _string_elements_timers, _num_string_elements_timers );
		SetFieldDec_T ( IDLE_TIMER_INDEX, IdleTimeSecs );
		SetFieldDec_T ( SPEED_25_MPH_TIMER_INDEX, AtSpeedTimeSecs );
		SetFieldDec_T ( TOTAL_DRIVE_TIMER_INDEX, RunTimeSecs );
	}


	// flush the STDIN stream of any user input before loop
	ClearKeyboardBuffer ( );

	CurrentTimestampMsecs = LastCheckTimestampMsecs = GetTickCount ( );

	IUMPRIndex = 0;

//	unsigned long  LastTxTimestampMsecs = GetTickCount();
	//-------------------------------------------
	// loop until test completes
	//-------------------------------------------
	for (;;)
	{
		//-------------------------------------------
		// Things to do each loop:
		// Check for key press, act accordingly
		// Request next IPD
		// Request next IUMPR data
		// Update CurrentTimestamp
		//-------------------------------------------
		if ( _kbhit ( ) != 0 )
		{
			unsigned char c = _getch ( );
			if ( c == 27 )                    // ESC key
			{
				eRetCode = ABORT;
			}

			else if ( c == 'F' || c == 'f' )   // "FAIL" key
			{
				eRetCode = FAIL;
			}

			else if ( c >= '1' && c <= '8' )   // new ECU index
			{
				EcuIdx = (c - '1');         // zero-based index
				if ( EcuPageOffset+EcuIdx < gNumOfECUs &&
				     EcuIdx != CurrentEcuIdx )
				{
					CurrentEcuIdx = EcuIdx;
					bECUPageUpdate = TRUE;
				}
			}

			else if ( c == 73 )  //PGUP
			{
				if ( EcuPage < MAX_ECU_PAGE &&
				     EcuPageOffset+MAX_ECUS_PER_PAGE < gNumOfECUs )
				{
					EcuPageOffset += MAX_ECUS_PER_PAGE;
					EcuPage++;
					CurrentEcuIdx = 0;
					bECUPageUpdate = TRUE;
				}
			}

			else if ( c == 81 )  //PGDN
			{
				if ( EcuPage > 1 )
				{
					EcuPageOffset -= MAX_ECUS_PER_PAGE;
					EcuPage--;
					CurrentEcuIdx = 0;
					bECUPageUpdate = TRUE;
				}
			}

			else if ( c == 224 )  // Extended Range
			{
				c = _getch ( );
				if ( c == 77 )  //Right Arrow
				{
					if ( DataPage < MAX_DATA_PAGE )
					{
						OffsetIncrement = MAX_DATA_PAGE_1 + ((DataPage-1) * MAX_DATA_PER_PAGE);

						if ( OffsetIncrement <= gstResponse[EcuPageOffset+CurrentEcuIdx].DTCIUMPRCount )
						{
							DataPageOffset = (BYTE)OffsetIncrement;
							DataPage++;
							bDataPageUpdate = TRUE;
						}
					}
				}

				else if ( c == 75 )  //Left Arrow
				{
					if ( DataPage > 1 )
					{
						if ( DataPage == 2 )
						{
							DataPageOffset -= MAX_DATA_PAGE_1;
						}
						else
						{
							DataPageOffset -= MAX_DATA_PER_PAGE;
						}
						DataPage--;
						bDataPageUpdate = TRUE;
					}
				}
			}
		}  // end if kbhit


		//-------------------------------------------
		// Update screen, if a key press requires it
		//-------------------------------------------
		if ( bECUPageUpdate  == TRUE ||
		     bDataPageUpdate == TRUE)
		{
			// if ECU view changed and Data Page wasn't already == 1, reset
			if ( bECUPageUpdate  == TRUE &&
			     DataPage != 1 )
			{
				// reset to Data Page 1
				DataPageOffset = 0;
				DataPage = 1;
			}

			ResetDisplay ( EcuPageOffset, CurrentEcuIdx, EcuPage, DataPage );

			// display CARB Timers, if required
			if ( bDisplayCARBTimers == TRUE )
			{
				// add CARB Timer display text to the screen
				place_screen_text ( _string_elements_timers, _num_string_elements_timers );
				SetFieldDec_T ( IDLE_TIMER_INDEX, IdleTimeSecs );
				SetFieldDec_T ( SPEED_25_MPH_TIMER_INDEX, AtSpeedTimeSecs );
				SetFieldDec_T ( TOTAL_DRIVE_TIMER_INDEX, RunTimeSecs );
			}

			UpdateTestStatus ( SubTestStatusFlags );

			bDataPageUpdate = FALSE;
			bECUPageUpdate  = FALSE;
		}  // end if page update true


		//-------------------------------------------
		// Get next IUMPR
		//-------------------------------------------
		if ( bIUMPRSupported == TRUE )
		{
			// print currently requested DTC and place in list
			// column 18, line 3
			gotoxy ( 18, 3 );
			printf ( "Current Request: %c%02X%02X  %2d of %d\n",
			          DTCTypeCharacter[(DTCIUMPRList[IUMPRIndex].HighByte & 0xC0) >> 6],  // 1st character (P, C, B, U)
			          DTCIUMPRList[IUMPRIndex].HighByte & 0x3F,                           // 2nd (0,1,2,3) and 3rd (0-F) characters
			          DTCIUMPRList[IUMPRIndex].MidByte,                                   // 4th and 5th characters (0-F)
			          IUMPRIndex + 1,
			          DTCIUMPRListCount );

			if ( (eRetVal = GetDTCExtData ( 0x91, IUMPRIndex ))!= PASS )
			{
				// early/late message, note it and keep going
				SubTestStatusFlags |= SUB_TEST_FAIL;
			}

			bLogMessage = FALSE;

			// Save the responses
			for ( EcuIdx = 0;
			      EcuIdx < gNumOfECUs;
			      EcuIdx++ )
			{
				if ( gstResponse[EcuIdx].bDTCIUMPRSupported == TRUE &&
				     gstResponse[EcuIdx].bResponseReceived  == TRUE )
				{
					WORD Index;
					SaveDTCIUMPRData ( EcuIdx, &Index );

					if ( CheckIUMPRData ( EcuIdx, FALSE ) == TRUE )
					{
						bLogMessage  = TRUE;
					}
				}  // if ECU received data
			}  // end for ( EcuIdx )

			if ( bLogMessage == TRUE )
			{
				LogLastTransaction ( );
			}

			if ( ++IUMPRIndex >= DTCIUMPRListCount )
			{
				IUMPRIndex = 0;
			}
		}  // end if ( DTCIUMPRListCount != 0 )


		//-------------------------------------------
		// Update current time
		//-------------------------------------------
		CurrentTimestampMsecs = GetTickCount ( );


		//-------------------------------------------
		// END things to do each loop
		//-------------------------------------------


		//-------------------------------------------
		// Things to do once a second:
		// Update LastCheckTimestamp
		// Request PID $F501, update screen if changed
		// Request OBDCOND and IGNCNTR, update screen if changed
		// Check IUMPR data, update screen if changed
		// Request PID $0C (RPM), PID $0D (Speed), and PID $1F (Runtime), determine test state
		//-------------------------------------------
		if ( (CurrentTimestampMsecs - LastCheckTimestampMsecs) >= 1000 );
		{
			LastCheckTimestampMsecs = CurrentTimestampMsecs;
			CheckDTCsTimerSecs--;

			// Alternate requesting PID $F501 and INF $F882
			if ( b1Sec )
			{
				//-------------------------------------------
				// request and update PID $F501
				//-------------------------------------------
				stReqMsg.SID      = 0x22;
				stReqMsg.NumIds   = 1;
				stReqMsg.u.DID[0] = 0xF501;
				if ( (eRetVal = RequestSID ( &stReqMsg, REQ_MSG_NO_PERIODIC_DISABLE )) == FAIL )
				{
					Log ( FAILURE, SCREENOUTPUTOFF, LOGOUTPUTON, NO_PROMPT,
					      "PID $F501 request\n" );
					DumpTransactionBuffer ( );
				}
				else
				{
					bLogMessage = FALSE;

					if ( eRetVal == ERRORS )
					{
						// early/late message, note it and keep going
						SubTestStatusFlags |= SUB_TEST_FAIL;
						bLogMessage = TRUE;
					}

					for ( EcuIdx = 0;
					      EcuIdx < gNumOfECUs;
					      EcuIdx++ )
					{
						if ( gstResponse[EcuIdx].PIDSize > 0 )
						{
							if ( SavePIDF501Data ( EcuIdx, FALSE ) == TRUE )
							{
								bLogMessage = TRUE;

								pPid = (PID *)&gstResponse[EcuIdx].PID[0];
								if ( IsIM_ReadinessComplete ( pPid ) == TRUE &&
								     (gstResponse[EcuIdx].StatusFlags & IMREADYDONE) == 0 )
								{
									gstResponse[EcuIdx].StatusFlags |= IMREADYDONE;
									bECUStatusUpdate = TRUE;
								}

								if ( EcuIdx == EcuPageOffset+CurrentEcuIdx &&
								     DataPage == 1 )
								{
									UpdatePID01Display ( EcuIdx );
								}
							}
						}
					}  // end for ( EcuIdx )

					if ( bLogMessage == TRUE )
					{
						LogLastTransaction ( );
					}
				}  // end if ( eRetVal != FAIL )
			}  // end if ( b1Sec )
			else
			{
				//-------------------------------------------
				// Get OBDCOND and IGNCNTR
				//-------------------------------------------
				if ( bIPDSupported == TRUE )
				{
					stReqMsg.SID      = 0x22;
					stReqMsg.NumIds   = 1;
					stReqMsg.u.DID[0] = 0xF882;
					if ( (eRetVal = RequestSID ( &stReqMsg, REQ_MSG_NO_PERIODIC_DISABLE )) == FAIL )
					{
						Log ( FAILURE, SCREENOUTPUTOFF, LOGOUTPUTON, NO_PROMPT,
						      "SID $22 IPD request\n" );
						DumpTransactionBuffer ( );
					}
					else
					{
						bLogMessage = FALSE;

						if ( eRetVal == ERRORS )
						{
							// early/late message, note it and keep going
							SubTestStatusFlags |= SUB_TEST_FAIL;
							bLogMessage = TRUE;
						}

						for ( EcuIdx = 0;
						      EcuIdx < gNumOfECUs;
						      EcuIdx++ )
						{
							if ( GetIPD ( EcuIdx ) == PASS )
							{
								if ( CheckIPD ( EcuIdx ) == TRUE )
								{
									bLogMessage = TRUE;

									IPTStatusFlags[0] = 0xFFFFFFFF;   // by default all items completed
									eRetVal = EvaluateIPD ( EcuIdx, &IPTStatusFlags[0], FALSE );
									if ( (eRetVal & FAIL) == PASS &&
									     (gstResponse[EcuIdx].StatusFlags & IPDDONE) == 0 )
									{
										gstResponse[EcuIdx].StatusFlags |= IPDDONE;
										bECUStatusUpdate = TRUE;
									}

									if ( EcuIdx == EcuPageOffset+CurrentEcuIdx &&
									     DataPage == 1 )
									{
										UpdateIPDDisplay ( EcuIdx, &IPTStatusFlags[0] );
									}
								}
							}
						}  // end for ( EcuIdx )

						if ( bLogMessage == TRUE )
						{
							LogLastTransaction ( );
						}
					}  // end if ( eRetVal != FAIL )
				}  // end if ( bIPDSupported )
			}  // end if ( !b1Sec )


			//-------------------------------------------
			// Check if need to update IUMPR on-screen
			//-------------------------------------------
			if ( bIUMPRSupported == TRUE )
			{
				// something changed, update the display because EVAP from one ECU
				// could affect another ECU's display
				for ( EcuIdx = 0;
				      EcuIdx < gNumOfECUs;
				      EcuIdx++ )
				{
					if ( CheckIUMPRData ( EcuIdx, TRUE ) == TRUE )
					{
						// by default all items completed
						memset ( &IPTStatusFlags,
						         0xFF,
						         sizeof ( IPTStatusFlags ) );

						eRetVal = EvaluateIPD ( EcuIdx, &IPTStatusFlags[0], FALSE );
						if ( (eRetVal & FAIL) == PASS &&
						     (gstResponse[EcuIdx].StatusFlags & IPDDONE) == 0 )
						{
							gstResponse[EcuIdx].StatusFlags |= IPDDONE;
							bECUStatusUpdate = TRUE;
						}
						eRetVal = EvaluateDTCIUMPRData ( EcuIdx, &IPTStatusFlags[0], FALSE );
						if ( (eRetVal & FAIL) == PASS &&
						     (gstResponse[EcuIdx].StatusFlags & IUMPRDONE) == 0 )
						{
							gstResponse[EcuIdx].StatusFlags |= IUMPRDONE;
							bECUStatusUpdate = TRUE;
						}

						if ( EcuIdx == EcuPageOffset+CurrentEcuIdx )
						{
							// if data page 1, also update IPD, other counters can affect the status of OBDCOND
							if ( DataPage == 1 )
							{
								UpdateIPDDisplay ( EcuIdx, &IPTStatusFlags[0] );
							}
							UpdateDTCIUMPRDisplay ( EcuIdx, DataPage, &IPTStatusFlags[0] );
						}
					}  // end if ECU data changed (or initial)
				}  // end for ( EcuIdx )
			}


			//-------------------------------------------
			// Check if need to update ECU Status on-screen
			//-------------------------------------------
			if ( bECUStatusUpdate == TRUE )
			{
				// display ECU ID and Status
				UpdateECUDisplay ( EcuPageOffset );
				bECUStatusUpdate = FALSE;
			}


			//-------------------------------------------
			// Display Timers
			//-------------------------------------------
			if ( bDisplayCARBTimers == TRUE )
			{
				// Alternate requesting PID $F41F or $F40C and INF $F882
				if ( b1Sec )
				{
					//-------------------------------------------
					// if RunTime supported,
					// request engine RunTime (PID $F41F)
					//-------------------------------------------
					if ( bRunTimeSupport == TRUE )
					{
						stReqMsg.SID      = 0x22;
						stReqMsg.NumIds   = 1;
						stReqMsg.u.DID[0] = 0xF41F;
						eRetVal = RequestSID ( &stReqMsg, REQ_MSG_NO_PERIODIC_DISABLE );
						if ( eRetVal == FAIL )
						{
							Log ( FAILURE, SCREENOUTPUTOFF, LOGOUTPUTON, NO_PROMPT,
							      "PID $F41F request\n" );
							DumpTransactionBuffer ( );
						}
						else
						{
							bLogMessage = FALSE;

							if ( eRetVal != PASS )
							{
								// cover the case where a response was early/late
								SubTestStatusFlags |= SUB_TEST_FAIL;
								bLogMessage = TRUE;
							}

							for ( EcuIdx = 0;
							      EcuIdx < gNumOfECUs;
							      EcuIdx++ )
							{
								if ( gstResponse[EcuIdx].PIDSize > 0 )
								{
									pPid = (PID *)&gstResponse[EcuIdx].PID[0];

									if ( pPid->PIDLSB == 0x1F )
									{
										RunTimeSecs = pPid->Data[0];
										RunTimeSecs = (RunTimeSecs << 8) | (pPid->Data[1]);    // 1 cnt = 1 sec
										break;  // leave ECU for loop
									}
								}
							}

							if ( EcuIdx >= gNumOfECUs )
							{
								Log ( FAILURE, SCREENOUTPUTOFF, LOGOUTPUTON, NO_PROMPT,
								      "PID $F41F missing response\n" );
								SubTestStatusFlags |= SUB_TEST_FAIL;
								bLogMessage = TRUE;
							}

							if ( bLogMessage == TRUE )
							{
								LogLastTransaction ( );
							}
						}  // end if ( eRetVal != FAIL )
					}  // end if ( bRunTimeSupport )

					//-------------------------------------------
					// if RunTime is not supported,
					// get RunTime from system clock,
					// request RPM (PID $F40C)
					//-------------------------------------------
					else
					{
						RunTimeSecs = (unsigned short)(GetTickCount ( ) / 1000) - TestStartTimestampSecs;

						stReqMsg.SID      = 0x22;
						stReqMsg.NumIds   = 1;
						stReqMsg.u.DID[0] = 0xF40C;
						eRetVal = RequestSID ( &stReqMsg, REQ_MSG_NO_PERIODIC_DISABLE );
						if ( eRetVal == FAIL )
						{
							Log ( FAILURE, SCREENOUTPUTOFF, LOGOUTPUTON, NO_PROMPT,
							      "PID $F40C request\n" );
							DumpTransactionBuffer ( );
						}
						else
						{
							bLogMessage = FALSE;
						
							if ( eRetVal != PASS )
							{
								// cover the case where a response was early/late
								SubTestStatusFlags |= SUB_TEST_FAIL;
								bLogMessage = TRUE;
							}

							for ( EcuIdx = 0;
							      EcuIdx < gNumOfECUs;
							      EcuIdx++ )
							{
								if ( gstResponse[EcuIdx].PIDSize > 0 )
								{
									pPid = (PID *)&gstResponse[EcuIdx].PID[0];

									if ( pPid->PIDLSB == 0x0c )
									{
										RPM = pPid->Data[0];
										RPM = (RPM << 8) | (pPid->Data[1]);

										// convert from 1 cnt = 1/4 RPM to 1 cnt = 1 RPM
										RPM >>= 2;
										break;  // leave ECU for loop
									}
								}
							}

							if ( EcuIdx >= gNumOfECUs )
							{
								Log ( FAILURE, SCREENOUTPUTOFF, LOGOUTPUTON, NO_PROMPT,
								      "PID $F40C missing response\n" );
								SubTestStatusFlags |= SUB_TEST_FAIL;
								bLogMessage = TRUE;
							}

							if ( bLogMessage == TRUE )
							{
								LogLastTransaction ( );
							}
						}  // end if ( eRetVal != FAIL )
					}    // end if ( !bRunTimeSupport )

					// Display RunTime
					SetFieldDec_T ( TOTAL_DRIVE_TIMER_INDEX, RunTimeSecs );
				}  // end if ( b1Sec )

				else if ( (TimeStatusFlags & ATSPEED_TIME) == 0 )
				{
					//-------------------------------------------
					// request Speed - PID $F40D
					//-------------------------------------------
					stReqMsg.SID      = 0x22;
					stReqMsg.NumIds   = 1;
					stReqMsg.u.DID[0] = 0xF40D;
					eRetVal = RequestSID ( &stReqMsg, REQ_MSG_NO_PERIODIC_DISABLE );
					if ( eRetVal == FAIL )
					{
						Log ( FAILURE, SCREENOUTPUTOFF, LOGOUTPUTON, NO_PROMPT,
						      "PID $F40D request\n" );
						DumpTransactionBuffer ( );
					}
					else
					{
						bLogMessage = FALSE;

						if ( eRetVal != PASS )
						{
							// cover the case where a response was early/late
							SubTestStatusFlags |= SUB_TEST_FAIL;
							bLogMessage = TRUE;
						}

						for ( EcuIdx = 0;
						      EcuIdx < gNumOfECUs;
						      EcuIdx++ )
						{
							if ( gstResponse[EcuIdx].PIDSize > 0 )
							{
								pPid = (PID *)&gstResponse[EcuIdx].PID[0];

								if ( pPid->PIDLSB == 0x0D )
								{
									double dTemp = pPid->Data[0];

									SpeedKPH = pPid->Data[0];

									// convert from km/hr to mile/hr
									SpeedMPH = (unsigned short)( ((dTemp * 6214) / 10000) + 0.5 );
									break;  // leave ECU for loop
								}
							}
						}  // end for ( EcuIdx )

						if ( EcuIdx >= gNumOfECUs )
						{
							Log ( FAILURE, SCREENOUTPUTOFF, LOGOUTPUTON, NO_PROMPT,
							      "PID $F40D missing response\n" );
							SubTestStatusFlags |= SUB_TEST_FAIL;
							bLogMessage = TRUE;
						}

						if ( bLogMessage == TRUE )
						{
							LogLastTransaction ( );
						}
					}  // end if ( eRetVal != FAIL )
				}  // end if ( !b1Sec )


				//-------------------------------------------
				// Determine phase of dynamic test
				// update screen
				//-------------------------------------------
				bLoop = TRUE;
				while ( bLoop )
				{
					bLoop = FALSE;
					switch ( TestState )
					{
						case 0xFF:   // First Time ONLY - covers the case were the engine has already been idling
						{
							LastCheckTimestampSecs = RunTimeSecs;

							if ( (bRunTimeSupport == TRUE) ? (RunTimeSecs > 0) : (RPM > 450) )
							{

								if ( SpeedMPH <= 1 )
								{
									TestState = 1;
									IdleTimeSecs = RunTimeSecs;
									bLoop = TRUE;
									break;
								}
							}

							// engine is not running or SpeedMPH >1... don't know if there was an idle
							// set-up to intentionally fall thru to case 0
							TestState = 0;
						}
						//  0xFF falls through

						case 0:     // wait until idle  (RunTimeSecs > 0 or RPM > 450)
						{
							if ( (bRunTimeSupport == TRUE) ? (RunTimeSecs > 0) : (RPM > 450) )
							{
								// check 600 seconds cumulative time
								if ( (TimeStatusFlags & CUMULATIVE_TIME) != CUMULATIVE_TIME &&
								     RunTimeSecs >= 600 )
								{
									TimeStatusFlags |= CUMULATIVE_TIME;
									SaveTransactionStart ( ); // treat as a seperate transaction to avoid duplication
									Log ( INFORMATION, SCREENOUTPUTON, LOGOUTPUTON, NO_PROMPT,
									      "Idle Time = %d;  Speed Time = %d;  Run Time = %d  *RUN TIME DONE*\n",
									      IdleTimeSecs, AtSpeedTimeSecs, RunTimeSecs );
									LogLastTransaction ( );
								}

								if ( SpeedMPH <= 1 &&
								     IdleTimeSecs < 30 )
								{
									TestState = 1;
									IdleTimeSecs = 0;
									bLoop = TRUE;
								}
								else if ( SpeedMPH >= 25 &&
								          AtSpeedTimeSecs < 300 )
								{
									TestState = 2;
									bLoop = TRUE;
								}
								else if ( TimeStatusFlags == (IDLE_TIME | ATSPEED_TIME | CUMULATIVE_TIME) )
								{
									TestState = 3;
									bLoop = TRUE;
								}
								else
								{
									LastCheckTimestampSecs = RunTimeSecs;
								}
							}
						}
						break;

						case 1:     // 30 seconds continuous time at idle
						{
							if ( SpeedMPH <= 1 &&
							     (bRunTimeSupport == FALSE) ? (RPM > 450) : 1 )
							{
								// check idle time
								IdleTimeSecs = min ( IdleTimeSecs + (RunTimeSecs - LastCheckTimestampSecs), 30 );
								LastCheckTimestampSecs = RunTimeSecs;

								SetFieldDec_T ( IDLE_TIMER_INDEX, IdleTimeSecs );

								if ( IdleTimeSecs >= 30 )
								{
									TimeStatusFlags |= IDLE_TIME;
									TestState = 0;
									bLoop = TRUE;
									SaveTransactionStart ( ); // treat as a seperate transaction to avoid duplication
									Log ( INFORMATION, SCREENOUTPUTON, LOGOUTPUTON, NO_PROMPT,
									      "Idle Time = %d;  Speed Time = %d;  Run Time = %d  *IDLE DONE*\n",
									      IdleTimeSecs, AtSpeedTimeSecs, RunTimeSecs );
									LogLastTransaction ( );
								}

								// check 600 seconds cumulative time
								if ( (TimeStatusFlags & CUMULATIVE_TIME) != CUMULATIVE_TIME &&
								     RunTimeSecs >= 600 )
								{
									TimeStatusFlags |= CUMULATIVE_TIME;
									SaveTransactionStart ( ); // treat as a seperate transaction to avoid duplication
									Log ( INFORMATION, SCREENOUTPUTON, LOGOUTPUTON, NO_PROMPT,
									      "Idle Time = %d;  Speed Time = %d;  Run Time = %d  *RUN TIME DONE*\n",
									      IdleTimeSecs, AtSpeedTimeSecs, RunTimeSecs );
									LogLastTransaction ( );
								}
							}
							else
							{
								TestState = 0;
								bLoop = TRUE;
							}
						}
						break;

						case 2:     // 300 seconds cumulative time at SpeedMPH >= 25 MPH
						{
							if ( SpeedMPH >= 25 &&
							     (bRunTimeSupport == FALSE) ? (RPM > 450) : 1 )
							{
								// check at speed time
								AtSpeedTimeSecs = min ( AtSpeedTimeSecs + (RunTimeSecs - LastCheckTimestampSecs), 300 );
								LastCheckTimestampSecs = RunTimeSecs;

								SetFieldDec_T ( SPEED_25_MPH_TIMER_INDEX, AtSpeedTimeSecs );

								if ( AtSpeedTimeSecs >= 300 )
								{
									TimeStatusFlags |= ATSPEED_TIME;
									TestState = 0;
									bLoop = TRUE;
									SaveTransactionStart ( ); // treat as a seperate transaction to avoid duplication
									Log ( INFORMATION, SCREENOUTPUTON, LOGOUTPUTON, NO_PROMPT,
									      "Idle Time = %d;  Speed Time = %d;  Run Time = %d  *AT SPEED DONE*\n",
									      IdleTimeSecs, AtSpeedTimeSecs, RunTimeSecs );
									LogLastTransaction ( );
								}

								// check 600 seconds cumulative time
								if ( (TimeStatusFlags & CUMULATIVE_TIME) != CUMULATIVE_TIME &&
								     RunTimeSecs >= 600 )
								{
									TimeStatusFlags |= CUMULATIVE_TIME;
									SaveTransactionStart ( ); // treat as a seperate transaction to avoid duplication
									Log ( INFORMATION, SCREENOUTPUTON, LOGOUTPUTON, NO_PROMPT,
									      "Idle Time = %d;  Speed Time = %d;  Run Time = %d  *RUN TIME DONE*\n",
									      IdleTimeSecs, AtSpeedTimeSecs, RunTimeSecs );
									LogLastTransaction ( );
								}
							}
							else
							{
								TestState = 0;
								bLoop = TRUE;
							}
						}
						break;

						default:     // 'idle' and 'at speed' times done
						{

						}
						break;
					} // end switch ( TestState )
				} // end while ( bLoop )
			} // end if (bDisplayCARBTimers == TRUE)


			//-------------------------------------------
			// Check for DTCs
			//-------------------------------------------
			if ( CheckDTCsTimerSecs <= 0 )
			{
				// initially clear the DTC flag
				SubTestStatusFlags &= (~SUB_TEST_DTC);

				eRetVal = IsDTCPending ( (REQ_MSG_NO_PERIODIC_DISABLE|REQ_MSG_ALLOW_NO_RESPONSE) );
				if ( eRetVal == PASS )
				{
					// log and flag the pending DTCs
					LogLastTransaction ( );
					SubTestStatusFlags |= SUB_TEST_DTC;
				}
				else if ( eRetVal == ERRORS )
				{
					// log the error
					LogLastTransaction ( );
				}

				eRetVal = IsDTCStored ( (REQ_MSG_NO_PERIODIC_DISABLE|REQ_MSG_ALLOW_NO_RESPONSE) );
				if ( eRetVal == PASS )
				{
					// log and flag the stored DTCs
					LogLastTransaction ( );
					SubTestStatusFlags |= SUB_TEST_DTC;
				}
				else if ( eRetVal == ERRORS )
				{
					// log the error
					LogLastTransaction ( );
				}

#ifdef _DEBUG
				CheckDTCsTimerSecs = 2;
#else
				CheckDTCsTimerSecs = 30;
#endif
			}


			// update on-screen test status
			if ( gbTestSubsectionFailed )
			{
				// some function logged a FAILURE
				SubTestStatusFlags |= SUB_TEST_FAIL;
			}

			if ( SubTestStatusFlags != LastTestStatusFlags )
			{
				UpdateTestStatus ( SubTestStatusFlags );

				LastTestStatusFlags = SubTestStatusFlags;
			}

			//-------------------------------------------
			// Check if test is complete
			//-------------------------------------------
			EcuDoneCount = 0;
			for ( EcuIdx=0;
			      EcuIdx < gNumOfECUs;
			      EcuIdx++ )
			{
				if ( (gstResponse[EcuIdx].StatusFlags & ECUDONE) == 0 )
				{
					if ( (gstResponse[EcuIdx].StatusFlags & IMREADYDONE) != 0 &&
					     (gstResponse[EcuIdx].StatusFlags & IPDDONE)     != 0 &&
					     (gstResponse[EcuIdx].StatusFlags & IUMPRDONE)   != 0 )
					{
						gstResponse[EcuIdx].StatusFlags |= ECUDONE;
						if ( EcuIdx >= EcuPageOffset &&
						     EcuIdx < (EcuPageOffset+MAX_ECUS_PER_PAGE) )
						{
							SetFieldText ( ECU_STATUS_INDEX+(EcuIdx-EcuPageOffset), "Done" );
						}
						EcuDoneCount++;
					}
				}
				else
				{
					EcuDoneCount++;
				}
			}

			if ( EcuDoneCount == gNumOfECUs )
			{
				if ( (SubTestStatusFlags & SUB_TEST_FAIL) == SUB_TEST_FAIL )
				{
					eRetCode =  FAIL;
				}
				else
				{
					eRetCode =  PASS;
					break;  // leave infinite for loop
				}
			}


			if ( eRetCode != PASS )
			{
				break;  // leave infinite for loop
			}

			// Toggle 1 sec Flag
			b1Sec = ~b1Sec;
		}  // end if time since last check greater than 1 sec
	} // end for (;;)

	gotoxy ( 0, PRESS_ESC_ROW+3 );
	gbSuspendLogOutput = FALSE;
	gbSuspendScreenOutput = FALSE;
	if ( eRetCode == ABORT )
	{
		Log ( INFORMATION, SCREENOUTPUTON, LOGOUTPUTON, NO_PROMPT,
		      "Test 11 aborted by user\n\n" );
	}
	else if ( eRetCode == FAIL &&
	          (SubTestStatusFlags & SUB_TEST_FAIL) != SUB_TEST_FAIL )
	{
		Log ( FAILURE, SCREENOUTPUTON, LOGOUTPUTON, NO_PROMPT,
		      "Test 11 failed by user\n\n" );
	}

	PrintECUData ( );

	if ( bDisplayCARBTimers == TRUE )
	{
		Log ( INFORMATION, SCREENOUTPUTON, LOGOUTPUTON, NO_PROMPT,
		      "Idle Time = %d;  Speed Time = %d;  Run Time = %d\n\n",
		      IdleTimeSecs, AtSpeedTimeSecs, RunTimeSecs );
	}

	if ( eRetCode == ABORT )
	{
		ABORT_RETURN;
	}
	return eRetCode;
}


/*******************************************************************************
**
**  Function:  PrintECUData
**
**  Purpose:
**
*******************************************************************************/
void PrintECUData ( void )
{
	BYTE         EcuIdx;

	char DTCstring[6];
	WORD index;
	WORD pindex;


	Log ( INFORMATION, SCREENOUTPUTOFF, LOGOUTPUTON, NO_PROMPT,
	      "On exit from Test 11.2 the following were the conditions:\n" );

	for ( EcuIdx = 0;
	      EcuIdx < gNumOfECUs;
	      EcuIdx++ )
	{
		Log ( INFORMATION, SCREENOUTPUTOFF, LOGOUTPUTON, NO_PROMPT,
		      "ECU ID: %02X    Status: %s\n",
		      GetEcuId ( EcuIdx ),
		      (gstResponse[EcuIdx].StatusFlags & ECUDONE) ? "Done" : "    " );


		// write IM Status
		pindex = IMSTATUS_ELEMENT_INDEX;
		Log ( INFORMATION, SCREENOUTPUTOFF, LOGOUTPUTON, NO_PROMPT,
		      "%s\n",
		      _string_elements_imr[pindex++] );
		for ( index = 0;
		      index < IMREADINESS_SIZE;
		      index++, pindex++ )
		{
			Log ( INFORMATION, SCREENOUTPUTOFF, LOGOUTPUTON, NO_PROMPT,
			      "%-19s  %s\n",
			      _string_elements_imr[pindex].szLabel,
			      szIM_Status[Test11IMStatus[EcuIdx][index]] );
		}

		// write In-Use Performance Data (OBDCOND and IGNCNTR) Status
		pindex = IPDSTATUS_ELEMENT_INDEX;
		Log ( INFORMATION, SCREENOUTPUTOFF, LOGOUTPUTON, NO_PROMPT,
		      "%-19s  Initial    Current\n", _string_elements_iumpr1[pindex++].szLabel );

		if ( gstResponse[EcuIdx].bIPDSupported == FALSE )
		{
			// write rate based counter
			for ( index = 0;
			      index < 2;
			      index++, pindex++ )
			{
				Log ( INFORMATION, SCREENOUTPUTOFF, LOGOUTPUTON, NO_PROMPT,
				      "%-19s  Not Supported\n",
				      _string_elements_iumpr1[pindex].szLabel );
			}
		}
		else
		{
			// write rate based counter
			for ( index = 0;
			      index < 2;
			      index++, pindex++ )
			{
				Log ( INFORMATION, SCREENOUTPUTOFF, LOGOUTPUTON, NO_PROMPT,
				      "%-19s  %-7d    %-7d\n",
				      _string_elements_iumpr1[pindex].szLabel,
				      gstResponse[EcuIdx].IPD_Test10_10[index],
				      gstResponse[EcuIdx].IPD_Test11_CurrentDisplayData[index] );
			}
		}

		// write IUMPR Data Status
		Log ( INFORMATION, SCREENOUTPUTOFF, LOGOUTPUTON, NO_PROMPT,
		      "%-19s  N    D     N    D\n",
		      " " );

		if ( gstResponse[EcuIdx].bDTCIUMPRSupported == FALSE )
		{
			Log ( INFORMATION, SCREENOUTPUTOFF, LOGOUTPUTON, NO_PROMPT,
			      "%-19s  Not Supported\n",
			      "IUMPR" );
		}
		else
		{
			for ( index = 0;
			      index < (unsigned)(gstResponse[EcuIdx].DTCIUMPRCount);
			      index++ )
			{
				sprintf_s ( DTCstring, sizeof ( DTCstring ),
				            "%c%02X%02X",
				            DTCTypeCharacter[(DTCIUMPRList[index].HighByte & 0xC0) >> 6],  // 1st character (P, C, B, U)
				            DTCIUMPRList[index].HighByte & 0x3F,                           // 2nd (0,1,2,3) and 3rd (0-F) characters
				            DTCIUMPRList[index].MidByte );                                 // 4th and 5th characters (0-F)

				Log ( INFORMATION, SCREENOUTPUTOFF, LOGOUTPUTON, NO_PROMPT,
				      "%-19s  %-4d %-4d  %-4d %-4d\n",
				      DTCstring,
				      gstResponse[EcuIdx].DTCIUMPR_Test10_10[index].CompCounts,
				      gstResponse[EcuIdx].DTCIUMPR_Test10_10[index].CondCounts,
				      gstResponse[EcuIdx].DTCIUMPR_Test11_CurrentDisplayData[index].CompCounts,
				      gstResponse[EcuIdx].DTCIUMPR_Test11_CurrentDisplayData[index].CondCounts );
			}  // end for ( index )
		}  // end if ( gstResponse[EcuIdx].bDTCIUMPRSupported )
	}  // end for ( EcuIdx )

	Log ( BLANK, SCREENOUTPUTON, LOGOUTPUTON, NO_PROMPT, "\n" );

	// move to end of file
//	fseek ( gLogFileHandle, 0, SEEK_END );
}


/*******************************************************************************
**
**  Function:  AllocateMemoryForTest11_PerformanceCounters
**
**  Purpose:
**
*******************************************************************************/
void AllocateMemoryForTest11_PerformanceCounters ( void )
{
	BYTE          EcuIdx;
	unsigned int  IMReadinessIndex;


	// Allocate memory for saving PID $F501
	if ( (pTest11_PIDF501 = (PID *) malloc ( sizeof ( PID ) * gNumOfECUsForAlloc )) == NULL )
	{
		Log ( ERROR_FAILURE, SCREENOUTPUTON, LOGOUTPUTON, YES_NO_PROMPT,
		      "Unable to allocate memory (%d bytes) for pTest11_PIDF501.",
		      sizeof ( PID ) * gNumOfECUsForAlloc );
		exit ( FAIL );
	}
	memset ( pTest11_PIDF501,
	         0x00,
	         sizeof ( PID ) * gNumOfECUsForAlloc );


//	if ( (pDTCIUMPR_Test11_5[EcuIdx] = (DTCIUMPR **) malloc ( sizeof ( DTCIUMPR *) * gNumOfECUsForAlloc )) == NULL )
//	{
//		Log ( ERROR_FAILURE, SCREENOUTPUTON, LOGOUTPUTON, YES_NO_PROMPT,
//		      "Unable to allocate memory (%d bytes) for pDTCIUMPR_Test11_5.",
//		      sizeof ( IPT ) * gNumOfECUsForAlloc );
//		exit ( FAIL );
//	}
//	memset ( pDTCIUMPR_Test11_5,
//	         0x00,
//	         sizeof ( IPT ) * gNumOfECUsForAlloc );
//
//	if ( (pDTCIUMPR_Test11_11 = (DTCIUMPR **) malloc ( sizeof ( DTCIUMPR *) * gNumOfECUsForAlloc )) == NULL )
//	{
//		Log ( ERROR_FAILURE, SCREENOUTPUTON, LOGOUTPUTON, YES_NO_PROMPT,
//		      "Unable to allocate memory (%d bytes) for pDTCIUMPR_Test11_11.",
//		      sizeof ( IPT ) * gNumOfECUsForAlloc );
//		exit ( FAIL );
//	}
//	memset ( pDTCIUMPR_Test11_11,
//	         0x00,
//	         sizeof ( IPT ) * gNumOfECUsForAlloc );
//
//	if ( (pTest11CurrentDisplayData = (DTCIUMPR **) malloc ( sizeof ( DTCIUMPR *) * gNumOfECUsForAlloc )) == NULL )
//	{
//		Log ( ERROR_FAILURE, SCREENOUTPUTON, LOGOUTPUTON, YES_NO_PROMPT,
//		      "Unable to allocate memory (%d bytes) for pTest11CurrentDisplayData.",
//		      sizeof ( IPT ) * gNumOfECUsForAlloc );
//		exit ( FAIL );
//	}
//	memset ( pTest11CurrentDisplayData,
//	         0x00,
//	         sizeof ( IPT ) * gNumOfECUsForAlloc );
//
//	for ( EcuIdx = 0;
//	      EcuIdx < gNumOfECUs;
//	      EcuIdx++ )
//	{
//		if ( (pDTCIUMPR_Test11_5[EcuIdx] = (DTCIUMPR *) malloc ( sizeof ( DTCIUMPR ) * MAX_DTCIUMPR_COUNT )) == NULL )
//		{
//			Log ( ERROR_FAILURE, SCREENOUTPUTON, LOGOUTPUTON, YES_NO_PROMPT,
//			      "Unable to allocate memory (%d bytes) for pDTCIUMPR_Test11_5.",
//			      sizeof ( IPT ) * gNumOfECUsForAlloc );
//			exit ( FAIL );
//		}
//		memset ( pDTCIUMPR_Test11_5,
//		         0x00,
//		         sizeof ( IPT ) * gNumOfECUsForAlloc );
//
//		if ( (pDTCIUMPR_Test11_11[EcuIdx] = (DTCIUMPR *) malloc ( sizeof ( DTCIUMPR ) * MAX_DTCIUMPR_COUNT )) == NULL )
//		{
//			Log ( ERROR_FAILURE, SCREENOUTPUTON, LOGOUTPUTON, YES_NO_PROMPT,
//			      "Unable to allocate memory (%d bytes) for pDTCIUMPR_Test11_11.",
//			      sizeof ( IPT ) * gNumOfECUsForAlloc );
//			exit ( FAIL );
//		}
//		memset ( pDTCIUMPR_Test11_11,
//		         0x00,
//		         sizeof ( IPT ) * gNumOfECUsForAlloc );
//
//		if ( (pTest11CurrentDisplayData[EcuIdx] = (DTCIUMPR *) malloc ( sizeof ( DTCIUMPR ) * MAX_DTCIUMPR_COUNT )) == NULL )
//		{
//			Log ( ERROR_FAILURE, SCREENOUTPUTON, LOGOUTPUTON, YES_NO_PROMPT,
//			      "Unable to allocate memory (%d bytes) for pTest11CurrentDisplayData.",
//			      sizeof ( IPT ) * gNumOfECUsForAlloc );
//			exit ( FAIL );
//		}
//		memset ( pTest11CurrentDisplayData,
//		         0x00,
//		         sizeof ( IPT ) * gNumOfECUsForAlloc );
//	}


//	if ( (Test11IMStatus = (enum IM_Status **) malloc( sizeof ( enum IM_Status ) * gNumOfECUsForAlloc )) == NULL )
//	{
//		Log ( ERROR_FAILURE, SCREENOUTPUTON, LOGOUTPUTON, YES_NO_PROMPT,
//		      "Unable to allocate memory (%d bytes) for Test11IMStatus.",
//		      sizeof ( enum IM_Status ) * gNumOfECUsForAlloc );
//		exit ( FAIL );
//	}
//
//	for ( EcuIdx = 0;
//	      EcuIdx < gNumOfECUs;
//	      EcuIdx++ )
//	{
//		if ( (Test11IMStatus[EcuIdx] = (enum IM_Status *) malloc ( sizeof ( enum IM_Status ) * IMREADINESS_SIZE )) == NULL )
//		{
//			Log ( ERROR_FAILURE, SCREENOUTPUTON, LOGOUTPUTON, YES_NO_PROMPT,
//			      "Unable to allocate memory (%d bytes) for Test11IMStatus[%d].",
//			      sizeof ( enum IM_Status ) * IMREADINESS_SIZE,
//			      EcuIdx );
//			exit ( FAIL );
//		}
//	}

	for ( IMReadinessIndex = 0;
	      IMReadinessIndex < IMREADINESS_SIZE;
	      IMReadinessIndex++ )
	{
		for ( EcuIdx = 0;
		      EcuIdx < gNumOfECUs;
		      EcuIdx++ )
		{
			Test11IMStatus[EcuIdx][IMReadinessIndex] = NOTSUPPORTED;
		}
		eCurrentIMStatus[IMReadinessIndex] = INVALID;
	}
}


/*******************************************************************************
**
**  Function:  FreeMemoryForTest11_PerformanceCounters
**
**  Purpose:
**
*******************************************************************************/
void   FreeMemoryForTest11_PerformanceCounters ( void )
{
	BYTE        EcuIdx;
//	extern IPT *pLast_Ipt;


//	free ( pLast_Ipt );

	free ( pTest11_PIDF501 );

//	free ( pDTCIUMPR_Test11_5 );
//
//	free ( pDTCIUMPR_Test11_11 );
//
//	free ( pTest11CurrentDisplayData );

	for ( EcuIdx = 0;
	      EcuIdx < gNumOfECUs;
	      EcuIdx++ )
	{
//		free ( Test11IMStatus[EcuIdx] );

		// free informantion leftover from Test 10.10
//		free ( gstResponse[EcuIdx].DTCIUMPR_Test10_10 );
		free ( gstResponse[EcuIdx].pTest10_10_FEOCNTR );
//		free ( gstResponse[EcuIdx].pIPT );
	}

//	free ( Test11IMStatus );
}
