/*------------------------------------------------------------------------------

   Copyright (C) 1998 : Space Systems Finland Ltd.

   Space Systems Finland Ltd (SSF) allows you to use this version of
   the DEBIE-I DPU software for the specific purpose and under the
   specific conditions set forth in the Terms Of Use document enclosed
   with or attached to this software. In particular, the software
   remains the property of SSF and you must not distribute the software
   to third parties without written and signed authorization from SSF.

      System Name:   DEBIE DPU SW, test harness for WCET analysis
      Subsystem  :   DNI (DEBIE Null Interface)
      Module     :   harness.c

   Implementations of the DNI operations, suitable for compiling
   and linking the DEBIE I DPU Software as a standard C program,
   on any processor that supports the required data types (eg. 8-bit
   char, 32-bit long) and has enough program and data memory.

   This implementation is designed to support measurement-based
   timing analysis by feeding the DEBIE SW with selected sets of
   test data.

   Options: if the preprocessor symbol TRACE_HARNESS is defined,
   the harness operations generate trace message on stdout.

   Based, with extensive changes, on the SSF file rtx_if.c, rev 1.13,
   Fri May 21 00:14:00 1999.

  - * --------------------------------------------------------------------------
*/

/* This file contains several sets of operations, as follows:

   > processor registers as variables: reg52.h

   > kernel operations: taskctrl.h, isr_ctrl.h, msg_ctrl.h

   > some DPU operations: dpu_ctrl.h

   > telecommand and telemetry interface operations: ttc_ctrl.h, isr_ctrl.h

   > operations for the A/D converted: ad_conv.h

   > operations on the sensor units: su_ctrl.h, dpu_ctrl.h

   > accessing the VALUE_OF "unsigned short" telemetry items: keyword.h

   > "calling" a "patch function": keyword.h

   > checking the sizes of various types (for analysis only).

   For testing purposes the main operation is StartSystem(). The DEBIE
   main function calls StartSystem(), which normally activates the
   kernel and the DEBIE application tasks and interrupt handlers. In this
   version, StartSystem() takes over the show and runs the test scenario.

   Target-specific harness operations are implemented in target.c. There
   is a specific version of target.c for each target processor/compiler.

*/

#if defined(TRACE_HARNESS)
#include <stdio.h>
#endif

#include "keyword.h"
#include "health.h"
#include "measure.h"
#include "tc_hand.h"
#include "telem.h"


/*   Processor registers : reg52.h    */


unsigned char EA;


/*   Kernel operations : taskctrl.h    */

#include "taskctrl.h"

void CreateTask ( task_info_t EXTERNAL *new_task )
/* Purpose        : Task is created in the RTX.                              */
/* Interface      : input:   - new_task                                      */
/*                  output:  - telemetry_data.os_create_task_error           */
/* Preconditions  : none                                                     */
/* Algorithm      : -In case of an error, 'new_task' is stored to telemetry  */
/*                   as an error indication.                                 */
{
  #if defined(TRACE_HARNESS)
  printf ( "CreateTask %d\n", new_task->rtx_task_number );
  #endif

  switch ( new_task->rtx_task_number ) {

    case TC_TM_INTERFACE_TASK:

      InitTelecommandTask ();

      break;

    case ACQUISITION_TASK:

      InitAcquisitionTask ();

      break;

    case HIT_TRIGGER_ISR_TASK:

      InitHitTriggerTask ();

      break;

    default:

      #if defined(TRACE_HARNESS)
      printf ( "CreateTask: unknown task number\n" );
      #endif

      break;
  }
}


void WaitInterval( unsigned char time )
/* Purpose        : Interval is waited with RTX.                             */
/* Interface      : input:   - time                                          */
/*                  output:  - telemetry_data.os_wait_error                  */
/* Preconditions  : none                                                     */
/* Postconditions : Interval for wait is set.                                */
/* Algorithm      : -In case of an error, 'K_IVL' is stored to telemetry     */
/*                   as an error indication and error bit is set in          */
/*                   software_error register.                                */
{
  #if defined(TRACE_HARNESS)
  printf ( "WaitInterval %d\n", time );
  #endif
}


void WaitTimeout( unsigned char time ) COMPACT REENTRANT_FUNC
/* Purpose        : Timeout is waited with RTX.                              */
/* Interface      : input:   - time                                          */
/*                  output:  - telemetry_data.os_wait_error                  */
/* Preconditions  : none                                                     */
/* Postconditions : Specified time has elapsed.                              */
/* Algorithm      : -In case of an error, 'K_TMO' is stored to telemetry     */
/*                   as an error indication and error bit is set in          */
/*                   software_error register.                                */
{
  #if defined(TRACE_HARNESS)
  printf ( "WaitTimeout %d\n", time );
  #endif
}


void SetTimeSlice( unsigned int time_slice )
/* Purpose        : Time slice in the RTX is set.                            */
/* Interface      : input:   - time_slice                                    */
/*                  output:  - telemetry_data.os_set_slice_error             */
/* Preconditions  : none                                                     */
/* Postconditions : Timeslice which defines the time interval in number of   */
/*                  processor cycles is set.                                 */
/* Algorithm      :  In case of an error, indication bit is set in           */
/*                   the software_error register.                            */
{
  #if defined(TRACE_HARNESS)
  printf ( "SetTimeSlice %d\n", time_slice );
  #endif
}


static unsigned int ad_converting = 0;
/* Whether the ADC is now busy with a conversion.
   0 = no, 1 = yes.
   Declared here, because it is cleared by ShortDelay.
*/


void ShortDelay ( uint_least8_t delay_loops )
{
  #if defined(TRACE_HARNESS)
  printf ( "ShortDelay %d\n", delay_loops );
  #endif

  ad_converting = 0;
  /* Any on-going A/D conversion is assumed to end during the delay.
     ShortDelay is sometimes used, instead of End_Of_ADC, when the
     A/D converter is switched between unipolar and bipolar modes.
  */
}


unsigned char isr_send_message (
  unsigned char mailbox,
  uint16_t      message )
/* Purpose        : Send mail from ISR to a requested mailbox.              */
/* Interface      : input:   - mailbox, message                             */
/*                  output:  - telemetry_data.os_send_message_error         */
/* Preconditions  : Mailbox number should be a value 0 - 7                  */
/* Postconditions : Mail is send to a given mailbox.                        */
/* Algorithm      : - In case of an error, failed 'mailbox' is stored to    */
/*                    telemetry.                                            */
{
  #if defined(TRACE_HARNESS)
  printf ( "isr_send_message to %d, message %d = 0x%x\n",
           mailbox, message, message );
  #endif
  SendTaskMail ( mailbox, message, 0 );
  return OK;
}


/*   Interrupt services : isr_ctrl.h   */


#include "isr_ctrl.h"


void AttachInterrupt( unsigned char ISR_VectorNumber )
/* Purpose        : Interrupt with a given number is assigned to a task in   */
/* Interface      : input:   - ISR_VectorNumber                              */
/*                  output:  - telemetry_data.os_attach_interrupt_error      */
/* Preconditions  : none                                                     */
/* Postconditions : Interrupt is attached to a calling task.                 */
/* Algorithm      : -In case of an error, 'ISR_VectorNumber' is stored to    */
/*                   telemetry as an error indication.                       */
{
  #if defined(TRACE_HARNESS)
  printf ( "AttachInterrupt %d\n", ISR_VectorNumber );
  #endif
}


void EnableInterrupt( unsigned char ISR_VectorNumber )
/* Purpose        : Interrupt with a given number is enabled in the RTX.     */
/* Interface      : input:   - ISR_VectorNumber                              */
/*                  output:  - telemetry_data.os_enable_isr_error            */
/* Preconditions  : none                                                     */
/* Postconditions : Interrupt is enabled.                                    */
/* Algorithm      : -In case of an error, 'ISR_VectorNumber' is stored to    */
/*                   telemetry as an error indication.                       */
{
  #if defined(TRACE_HARNESS)
  printf ( "EnableInterrupt %d\n", ISR_VectorNumber );
  #endif
}


void DisableInterrupt( unsigned char ISR_VectorNumber )
/* Purpose        : Interrupt with a given number is disabled in the RTX.    */
/* Interface      : input:   - ISR_VectorNumber                              */
/*                  output:  - telemetry_data.os_disable_isr_error           */
/* Preconditions  : none                                                     */
/* Postconditions : Interrupt is enabled.                                    */
/* Algorithm      : -In case of an error, 'ISR_VectorNumber' is stored to    */
/*                   telemetry as an error indication.                       */
{
  #if defined(TRACE_HARNESS)
  printf ( "DisableInterrupt %d\n", ISR_VectorNumber );
  #endif
}


signed char SetInterruptMask( unsigned char ISR_MaskNumber )
/* Purpose        : Interrupt mask bit is set is in the RTX.                 */
/* Interface      : Return value, which describes the execution result, is   */
/*                  always zero as this function does no parameter checking. */
/*                  Used to manipulate special bits, which are part of the   */
/*                  interrupt enable registers or to modify interrupt enable */
/*                  bits from inside a C51 interrupt function.               */
/*                  Not to be used for interrupt sources attached to RTX51   */
/*                  tasks.                                                   */
/* Preconditions  :                                                          */
/* Postconditions : Interrupt mask is set.                                   */
/* Algorithm      : RTX syntax is used.                                      */
{
  #if defined(TRACE_HARNESS)
  printf ( "SetInterruptMask 0x%x\n", ISR_MaskNumber );
  #endif
  return 0;  /* Success. */
}


signed char ResetInterruptMask( unsigned char ISR_MaskNumber )
/* Purpose        : Interrupt mask bit is reset is in the RTX.               */
/* Interface      : Return value, which describes the execution result, is   */
/*                  always zero as this function does no parameter checking. */
/*                  Used to manipulate special bits, which are part of the   */
/*                  interrupt enable registers or to modify interrupt enable */
/*                  bits from inside a C51 interrupt function.               */
/*                  Not to be used for interrupt sources attached to RTX51   */
/*                  tasks.                                                   */
/* Preconditions  :                                                          */
/* Postconditions : Interrupt mask is reset.                                 */
/* Algorithm      : RTX syntax is used.                                      */
{
  #if defined(TRACE_HARNESS)
  printf ( "ResetInterruptMask 0x%x\n", ISR_MaskNumber );
  #endif
  return 0;  /* Success. */
}


void WaitInterrupt ( unsigned char ISR_VectorNumber, unsigned char timer )
/* Purpose        : Interrupt is waited in the RTX.                          */
/* Interface      : input:   - ISR_VectorNumber,timer                        */
/*                  output:  - telemetry_data.os_wait_error                  */
/* Preconditions  : none                                                     */
/* Postconditions : Interrupt is waited.                                     */
/* Postconditions : Interrupt is enabled.                                    */
/* Algorithm      : -In case of an error, 'K_INT' is stored to telemetry     */
/*                   as an error indication and error bit is set in          */
/*                   software_error register.                                */
{
  #if defined(TRACE_HARNESS)
  printf ( "WaitInterrupt %d, time %d\n", ISR_VectorNumber, timer );
  #endif
}

/* See below, Telecommand and telemetry interface, for the ISR */
/* operations related to the TC timer.                         */

/* See below for the ISR operations related to the Hit interrupt. */



/*   Message sending : msg_ctrl.h */


#include "msg_ctrl.h"


static uint16_t mail_message[ 8 ];
/* The last message in the mailbox. */

static int mail_count[ 8 ] = {0, 0, 0, 0, 0, 0, 0, 0};
/* The number of messages in the mailbox. */
/* Should be between 0 and 1.             */

static int mail_overflows = 0;
/* The number of times a mailbox has overflowed. */


void SendTaskMail (
  unsigned char mailbox,
  uint16_t      message,
  unsigned char timeout )
/* Purpose        : Send mail to a requested mailbox.                       */
/* Interface      : input:   - mailbox, message, timeout                    */
/*                  output:  - telemetry_data.os_send_message_error         */
/* Preconditions  : Mailbox number should be a value 0 - 7                  */
/*                  timeout should be a value 0 - 255                       */
/* Postconditions : Mail is send to a given mailbox.                        */
/* Algorithm      : - In case of an error, failed 'mailbox' is stored to    */
/*                    telemetry.                                            */
{
  #if defined(TRACE_HARNESS)
  printf ( "SendTaskMail to %d, message %d, timeout %d\n",
           mailbox, message, timeout );
  #endif

  if ( mail_count[ mailbox ] == 0 )
    mail_message[ mailbox ] = message;
  else {
    #if defined(TRACE_HARNESS)
    printf ( "Overflow on mailbox %d, already %d message(s)\n",
             mailbox, mail_count[ mailbox ] );
    #endif
    mail_overflows ++;
  }

  mail_count[ mailbox ] ++;
}


void WaitMail ( incoming_mail_t EXTERNAL *message ) COMPACT_DATA REENTRANT_FUNC
/* Purpose        : Mail is waited from the given mailbox.                   */
/* Interface      : Return value, which describes the execution result, is   */
/*                  stored in to a struct.                                   */
/* Preconditions  : Mailbox number should be a value 0 - 7                   */
/*                  Time-out should have a value 0 - 255.                    */
/* Postconditions : Message is received or timeout has occurred or error has */
/*                  occurred due to unvalid parameter values.                */
/* Algorithm      : -In case of an error, 'event_selector' is stored to      */
/*                   telemetry as an error indication and error bit is set   */
/*                   in software_error register.                             */
{
  #if defined(TRACE_HARNESS)
  printf ( "WaitMail from %d, timeout %d\n",
           message -> mailbox_number, message -> timeout );
  #endif

  if ( mail_count[ message -> mailbox_number ] > 0 )
  {
    message -> wait_result      = MSG_RECEIVED;
    message -> execution_result = MSG_RECEIVED;
    *( message -> message )       = mail_message[ message -> mailbox_number ];

    #if defined(TRACE_HARNESS)
    printf ( "Message from %d is %d = 0x%x\n",
             message -> mailbox_number,
             *( message -> message ),
             *( message -> message ) );
    #endif

    mail_count[ message -> mailbox_number ] --;
  } else
  {
    message -> wait_result      = TIMEOUT_OCCURRED;
    message -> execution_result = TIMEOUT_OCCURRED;
    *( message -> message )       = 0;
  }
}


static void FlushMail ( unsigned char mailbox )
/* Remove all mail from the mailbox. Harness use only. */
{
  #if defined(TRACE_HARNESS)
  printf ( "FlushMail from box %d, which had %d messages.\n",
           mailbox, mail_count[ mailbox ] );
  #endif

  mail_count[ mailbox ] = 0;
}


/*   DPU operations : dpu_ctrl.h    */


#include "dpu_ctrl.h"


static unsigned int check_current_errors = 0;
/* The number of consecutive error indications to be returned
   by the next calls of Check_Current.
*/


unsigned char Check_Current ( unsigned char bits )
{
  unsigned char val;
  #if defined(TRACE_HARNESS)
  printf ( "Check_Current 0x%x\n", bits );
  #endif
  switch ( bits ) {
    case   3:
      val =  1;
      break;
    case  12:
      val =  4;
      break;
    case  48:
      val = 16;
      break;
    case 192:
      val = 64;
      break;
    default :
      val =  0;
      #if defined(TRACE_HARNESS)
      printf ( "Check_Current param error\n" );
      break;
      #endif
  }

  if ( check_current_errors > 0 ) {
    val = ~val;  /* Wrong value => alarm. */
    check_current_errors --;
  }

  return val;
}


/* The memory operations (Get_Data_Byte etc) are implemented in
   target.c for each target processor/compiler.
*/


/* The following routines are implemented in das/hw_if.c:
   Init_DPU
   GetResetClass
   SignalMemoryErrors
   SetMemoryConfiguration
   GetMemoryConfiguration
   PatchCode
*/


void Reboot( reset_class_t boot_type )
{
  #if defined(TRACE_HARNESS)
  printf ( "Reboot %d\n", boot_type );
  #endif

  if ( boot_type == checksum_reset_e ) {
    /* Make it not happen (at once) again: */
    reference_checksum = code_checksum;
  }

  TARGET_REBOOT;
}


/* Functions originally implemented in assembly-language (asmfuncs.a51): */


unsigned char TestMemBits ( data_address_t address )
/*
   Test each bit of a cell in RAM memory.
   For each of the eight bits, the value with this bit ON and
   all others bits OFF is written to the cell, and the written
   value is read back and verified.
   The original content of the cell is destroyed.
   Parameters:
      address  Cell address.
   TestMemBits returns zero for success, and otherwise the
   first bit pattern for which the cell failed.

*/
{
  #if defined(TRACE_HARNESS)
  printf ( "TestMemBits 0x%x\n", address );
  #endif
  return 0;
}


unsigned char TestMemData (
  data_address_t start,
  uint_least8_t  bytes )
/*
   Test an area of RAM memory.
   For each RAM cell in the given area, the values AAh
   and 55h are written to the cell, and the written value
   is read back and verified.
   The original content of each cell is destroyed.
   Parameters:
      start   Starting address
      bytes   Length of area
              1 .. 255. Note, zero is NOT allowed!
   TestMem returns the number of bytes remaining to test
   when the first memory failure occurred.
   If this value is nonzero, the first failing address is
   found by subtracting the return value from 'start+bytes'.

*/
{
  #if defined(TRACE_HARNESS)
  printf ( "TestMemData start 0x%x, bytes %d\n", start, bytes );
  #endif
  return 0;
}


unsigned char TestMemSeq (
  data_address_t start,
  uint_least8_t  bytes )
/*
   Test an area of RAM memory.
   For each RAM cell in the given area, the logical complement of
   the low byte of the cell address is written into the cell, in
   one pass from the start to the end. Then, in a second pass,
   each cell is read and the value is is verified.
   The original content of each cell is destroyed.
   Parameters:
      start   Starting address
      bytes   Length of area
              1 .. 255. Note, zero is NOT allowed!
   TestMemSeq returns the number of bytes remaining to test
   when the first memory failure occurred.
   If this value is nonzero, the first failing address is
   found by subtracting the return value from 'start+bytes'.

*/
{
  #if defined(TRACE_HARNESS)
  printf ( "TestMemSeq start 0x%x, bytes %d\n", start, bytes );
  #endif
  return 0;
}


/*    Telecommand and telemetry interface : ttc_ctrl.h    */


#include "ttc_ctrl.h"


static unsigned char tc_msb, tc_lsb;
/* Simulated TC interface registers. */

static uint16_t tc_word;
/* The simulated TC word, composed of tc_msb and tc_lsb. */


unsigned char Read_TC_MSB ( void )
{
  #if defined(TRACE_HARNESS)
  printf ( "Read_TC_MSB\n" );
  #endif
  return tc_msb;
}


unsigned char Read_TC_LSB ( void )
{
  #if defined(TRACE_HARNESS)
  printf ( "Read_TC_LSB\n" );
  #endif
  return tc_lsb;
}


static unsigned char tm_msb, tm_lsb;
/* Simulated TM interface registers. */


void Write_TM_LSB ( unsigned char value )
{
  #if defined(TRACE_HARNESS)
  printf ( "Write_TM_LSB %d = 0x%x\n", value, value );
  #endif
  tm_lsb = value;
}


void Write_TM_MSB ( unsigned char value )
{
  #if defined(TRACE_HARNESS)
  printf ( "Write_TM_MSB %d = 0x%x\n", value, value );
  #endif
  tm_msb = value;
}


/*    TC timer operations : isr_ctrl.h    */


static unsigned char tc_timer_overflow = 1;
/* Simulated overflow flag on the TC timer. */
/* 1 = overflow = good, sufficient interval between TCs. */
/* 0 = no overflow = bad. */


unsigned char TC_Timer_Overflow_Flag ( void )
{
  #if defined(TRACE_HARNESS)
  printf ( "TC_Timer_Overflow_Flag\n" );
  #endif
  return tc_timer_overflow;
}


void Clear_TC_Timer_Overflow_Flag ( void )
{
  #if defined(TRACE_HARNESS)
  printf ( "Clear_TC_Timer_Overflow_Flag\n" );
  #endif
  tc_timer_overflow = 0;
}


void Set_TC_Timer_Overflow_Flag ( void )
{
  #if defined(TRACE_HARNESS)
  printf ( "Set_TC_Timer_Overflow_Flag\n" );
  #endif
  tc_timer_overflow = 1;
}


/*    A/D Converter operations : ad_ctrl.h    */


#include "ad_conv.h"

/*  A/D channels

  Channel     Meaning
  -------     -------
  dec hex
  0  00     SU 1, plasma 1 +
  1  01     SU 1, plasma 1 -
  2  02     SU 1, piezo 1
  3  03     SU 1, piezo 2
  4  04     SU 1, plasma 2 +
  5  05     SU 1, temperature 0
  6  06     SU 1, temperature 1
  7  07
  8  08     SU 2, plasma 1 +
  9  09     SU 2, plasma 1 -
  10  0A     SU 2, piezo 1
  11  0B     SU 2, piezo 2
  12  0C     SU 2, plasma 2 +
  13  0D     SU 2, temperature 0
  14  0E     SU 2, temperature 1
  15  0F
  16  10     SU 1 & SU 2, +5V  supply
  17  11     SU 3 & SU 4, +5V  supply
  18  12     SU 1,2,3,4 , +50V supply
  19  13     DPU          +5V  supply
  20  14     SU 1 & SU 2, -5V  supply
  21  15     SU 3 & SU 4, -5V  supply
  22  16     SU 1,2,3,4,  -50V supply
  23  17
  24  18     SU 3, plasma 1 +
  25  19     SU 3, plasma 1 -
  26  1A     SU 3, piezo 1
  27  1B     SU 3, piezo 2
  28  1C     SU 3, plasma 2 +
  29  1D     SU 3, temperature 0
  30  1E     SU 3, temperature 1
  31  1F
  32  20     SU 4, plasma 1 +
  33  21     SU 4, plasma 1 -
  34  22     SU 4, piezo 1
  35  23     SU 4, piezo 2
  36  24     SU 4, plasma 2 +
  37  25     SU 4, temperature 0
  38  26     SU 4, temperature 1
  39  27
  40  28

  A/D channel-selector register:

  Bit         Meaning
  ---         -------
  7         1 = do not use interleave calibration
  6         1 = bipolar, 0 = unipolar
  5         Channel index, 32
  4         Channel index, 16
  3         Channel index,  8
  2         Channel index,  4
  1         Channel index,  2
  0         Channel index,  1

*/


typedef struct {
  uint16_t min;
  uint16_t max;
} ad_limit_t;
/* Limits on the value of a simulated A/D reading. */

static ad_limit_t ad_limit[ AD_CHANNELS ] = {
  {0, 0xffff}, {0, 0xffff}, {0, 0xffff}, {0, 0xffff}, {0, 0xffff},
  {0, 0xffff}, {0, 0xffff}, {0, 0xffff}, {0, 0xffff}, {0, 0xffff},
  {0, 0xffff}, {0, 0xffff}, {0, 0xffff}, {0, 0xffff}, {0, 0xffff},
  {0, 0xffff}, {0, 0xffff}, {0, 0xffff}, {0, 0xffff}, {0, 0xffff},
  {0, 0xffff}, {0, 0xffff}, {0, 0xffff}, {0, 0xffff}, {0, 0xffff},
  {0, 0xffff}, {0, 0xffff}, {0, 0xffff}, {0, 0xffff}, {0, 0xffff},
  {0, 0xffff}, {0, 0xffff}, {0, 0xffff}, {0, 0xffff}, {0, 0xffff},
  {0, 0xffff}, {0, 0xffff}, {0, 0xffff}, {0, 0xffff}, {0, 0xffff}
};
/* Limits on the simulated A/D readings for all channels. */


void Set_AD_Unlimited ( void )
/* Sets no A/D limits. */
{
  uint_least8_t c;

  #if defined(TRACE_HARNESS)
  printf ( "Set AD Unlimited\n" );
  #endif

  _Pragma( "loopbound min 40 max 40" )
  for ( c = 0; c < AD_CHANNELS; c++ ) {
    ad_limit[ c ].min = 0;
    ad_limit[ c ].max = 0xffff;
  }
}


void Set_AD_Nominal ( void )
/* Sets A/D limits to ensure nominal (in-range) readings. */
{
  #if defined(TRACE_HARNESS)
  printf ( "Set AD Nominal\n" );
  #endif

  /* SU +5V supply: */

  ad_limit[ 16 ].min = 0xba00;
  ad_limit[ 16 ].max = 0xe4ff;

  ad_limit[ 17 ].min = 0xba00;
  ad_limit[ 17 ].max = 0xe4ff;

  /* SU -5V supply: */

  ad_limit[ 20 ].min = 0x0d00;
  ad_limit[ 20 ].max = 0x22ff;

  ad_limit[ 21 ].min = 0x0d00;
  ad_limit[ 21 ].max = 0x22ff;

  /* SU +50V supply: */

  ad_limit[ 18 ].min = 0xa800;
  ad_limit[ 18 ].max = 0xe3ff;

  /* SU -50V supply: */

  ad_limit[ 22 ].min = 0x0e00;
  ad_limit[ 22 ].max = 0x2cff;

  /* DPU +5V supply: */

  ad_limit[ 19 ].min = 0xba00;
  ad_limit[ 19 ].max = 0xe4ff;

  /* SU 1 temperatures: */

  ad_limit[ 5 ].min = 0x0000;
  ad_limit[ 5 ].max = 0xfaff;
  ad_limit[ 6 ].min = 0x0000;
  ad_limit[ 6 ].max = 0xf4ff;

  /* SU 2 temperatures: */

  ad_limit[ 13 ].min = 0x0000;
  ad_limit[ 13 ].max = 0xfaff;
  ad_limit[ 14 ].min = 0x0000;
  ad_limit[ 14 ].max = 0xf4ff;

  /* SU 3 temperatures: */

  ad_limit[ 29 ].min = 0x0000;
  ad_limit[ 29 ].max = 0xfaff;
  ad_limit[ 30 ].min = 0x0000;
  ad_limit[ 30 ].max = 0xf4ff;

  /* SU 4 temperatures: */

  ad_limit[ 37 ].min = 0x0000;
  ad_limit[ 37 ].max = 0xfaff;
  ad_limit[ 38 ].min = 0x0000;
  ad_limit[ 38 ].max = 0xf4ff;
}


static int ad_conv_timer;
/* Simulate some delay in the conversion. This is a signed
   number so that the delay can be made large by initializing
   the timer to a large negative value.
*/

#define AD_NUM_CONV 6
/* The number of consecutive A/D conversions separately simulated. */

static int ad_conv_delay[ AD_NUM_CONV ];
/* The conversion finishes when ad_conv_timer >= ad_conv_delay,
   with provision for up to AD_NUM_CONV different delays for
   many consecutive conversions. A value of 0 or 1 means that
   the conversion is immediately ready, assuming that ad_conv_time
   starts counting from zero (and not from a negative value).
*/

static unsigned int ad_conv_num = 0;
/* Counts the consecutive conversions for ad_conv_delay[  ]. */


#define AD_NUM_RAND 319
/* The amount of random A/D data defined below. */

static unsigned char ad_random[ AD_NUM_RAND ] = {
  0x6a, 0xde, 0xba, 0x90, 0xf2, 0x18, 0x48, 0xf3,
  0x9e, 0x2b, 0x31, 0xdb, 0xe0, 0x7e, 0xc6, 0x18,
  0x43, 0xd0, 0xd7, 0x6e, 0xbc, 0xee, 0x93, 0x9a,
  0x06, 0xb2, 0x3d, 0x1f, 0xc1, 0x51, 0x66, 0x69,
  0xbf, 0x1c, 0x9c, 0xfc, 0x9b, 0xf7, 0xf2, 0xd0,
  0xf4, 0x26, 0x60, 0x69, 0xc4, 0xd9, 0xdb, 0xd4,
  0xe7, 0x2b, 0x8a, 0xea, 0x9f, 0xab, 0x40, 0x3e,
  0xc3, 0xd8, 0x21, 0x61, 0x3b, 0x0f, 0xc1, 0x49,
  0xd3, 0x09, 0x9a, 0x4d, 0x33, 0x52, 0x7b, 0x8e,
  0x7e, 0x7b, 0x6a, 0x88, 0x4f, 0x84, 0xa2, 0xb4,
  0x83, 0xd9, 0xba, 0x79, 0x7d, 0x8f, 0xdf, 0xb2,
  0x8c, 0x86, 0x77, 0x4f, 0x29, 0x86, 0xd4, 0x8b,
  0x11, 0x65, 0x55, 0x74, 0xf4, 0x76, 0x83, 0x88,
  0xd6, 0xa6, 0xa7, 0x33, 0x22, 0xa3, 0x2e, 0x88,
  0x06, 0x54, 0x90, 0x37, 0xd5, 0xdb, 0xce, 0x7c,
  0x0b, 0xd1, 0xe1, 0xc0, 0x7d, 0xa5, 0x0b, 0xc9,
  0xaf, 0xe3, 0x75, 0xc5, 0xf5, 0xaf, 0xaa, 0xe2,
  0x2a, 0xff, 0x6e, 0x84, 0x0e, 0x04, 0x10, 0xf0,
  0x78, 0xdc, 0x96, 0x3d, 0x22, 0x96, 0x64, 0x5b,
  0x7b, 0x9e, 0x83, 0x45, 0xba, 0xb8, 0xe1, 0x31,
  0xc7, 0x0a, 0xe0, 0x31, 0xce, 0x29, 0x3d, 0x01,
  0xb8, 0xfc, 0x79, 0x83, 0x3d, 0xd1, 0x40, 0xe1,
  0x46, 0xfa, 0xe7, 0xc5, 0xdc, 0xc4, 0x1c, 0x24,
  0x29, 0x5a, 0xef, 0xeb, 0x92, 0x57, 0xba, 0x06,
  0x13, 0x1d, 0x35, 0xef, 0xb0, 0x2d, 0x69, 0x20,
  0x92, 0xb1, 0x82, 0x00, 0x8e, 0x3b, 0x12, 0xb3,
  0x78, 0xd7, 0x18, 0xb3, 0x54, 0x0f, 0xd1, 0x8e,
  0x88, 0x5d, 0x4e, 0x2b, 0x30, 0x30, 0x2d, 0x85,
  0xaa, 0x21, 0x01, 0xe1, 0x2c, 0x35, 0xa1, 0xee,
  0xa2, 0x17, 0xed, 0x60, 0x1b, 0x98, 0xea, 0x12,
  0x85, 0x21, 0xde, 0x45, 0x26, 0xef, 0x12, 0x3c,
  0x02, 0x8c, 0xd7, 0x49, 0xbd, 0x02, 0xa7, 0x7d,
  0xe7, 0x1c, 0x15, 0xf9, 0xaa, 0x44, 0x15, 0xb1,
  0xaa, 0x76, 0x5e, 0xf2, 0xb4, 0xfb, 0x85, 0x77,
  0xb9, 0x32, 0xb4, 0xc9, 0x70, 0xe1, 0xdb, 0x44,
  0x9f, 0x5b, 0x87, 0xca, 0xaa, 0xcb, 0x43, 0x53,
  0x7e, 0x49, 0xec, 0x1a, 0x13, 0x1d, 0xe1, 0x1b,
  0x13, 0xc3, 0x34, 0x95, 0x5d, 0x5a, 0xc3, 0xd0,
  0x33, 0x05, 0x82, 0x4a, 0x2e, 0x6d, 0x39, 0xeb,
  0x9c, 0x65, 0x81, 0x7f, 0xa1, 0x62, 0x11
};
/* Random A/D data. */


void Next ( unsigned int *index )
/* Go on to the next random value. */
{
  ( *index ) ++;
  if ( ( *index ) >= AD_NUM_RAND ) *index = 0;
}


unsigned char Next_Rand ( unsigned int *index )
/* Go on to the next random value and return it. */
{
  Next ( index );
  return ad_random[ *index ];
}


void Set_AD_Delay ( int delay )
/* Sets ad_conv_delay[ all ] = delay. */
{
  int i;
  _Pragma( "loopbound min 6 max 6" )
  for ( i = 0; i < AD_NUM_CONV; i++ )
    ad_conv_delay[ i ] = delay;
}


static unsigned int ad_delay_rand = 0;
/* A roving index to randomize the A/D delays. */


void Random_AD_Delay ( void )
/* Sets random ad_conv_delay[  ]. */
{
  int i;
  _Pragma( "loopbound min 6 max 6" )
  for ( i = 0; i < AD_NUM_CONV; i++ )
    ad_conv_delay[ i ] = ( Next_Rand ( &ad_delay_rand ) % ( ADC_MAX_TRIES + 10 ) );
}


static unsigned int ad_rand_index = 0;
/* A roving index to the random A/D data. */

static unsigned int max_adc_hits = 0;
/* Maximum number of particle "hits" that may occur during
   A/D conversions (in the Monitoring task, we assume).
*/

static unsigned int ad_hit_rand_index = 0;
/* A roving index to the random A/D data, for randomizing
   the occurrence of "hits" during A/D conversions.
*/

static unsigned int total_adc_hits = 0;
/* The number of simulated particle "hits" during A/D conversion. */

static unsigned int ad_random_failures = 0;
/* The number of A/D conversion failures (conversion never done)
   to be simulated, at random times, in the next conversions
   (calls of Start_Conversion).
*/

static unsigned int ad_fail_index = 0;
/* A roving index to the random A/D data, for randomizing
   the occurrence of A/D failures (conversion never done).
*/

/* See also ad_converting, declarer earlier. */


void Update_ADC_Channel_Reg ( unsigned char channel )
{
  #if defined(TRACE_HARNESS)
  printf ( "Update_ADC_Channel_Reg %x\n", channel );
  #endif
}


static unsigned int start_conversion_count = 0;
/* Counts the number of calls of Start_Conversion. */

static uint16_t ad_value = 0;
/* Simulated ADC value, set at Start_Conversion and reported
   byte by byte.
*/


void Start_Conversion ( void )
{
  uint_least8_t channel;

  channel = ADC_channel_register & 0x3f;

  #if defined(TRACE_HARNESS)
  printf ( "Start_Conversion on channel %d\n", channel );
  #endif

  if ( ad_converting != 0 ) {
    #if defined(TRACE_HARNESS)
    printf ( "- previous conversion did not end.\n" );
    #endif
  }

  /* Pick an A/D reading for this channel: */

  ad_value = ( ( uint16_t )Next_Rand ( &ad_rand_index ) ) << 8;
  ad_value |= ( uint16_t )Next_Rand ( &ad_rand_index );

  if ( ad_value < ad_limit[ channel ].min )
    ad_value = ad_limit[ channel ].min;
  else
    if ( ad_value > ad_limit[ channel ].max )
      ad_value = ad_limit[ channel ].max;

  start_conversion_count ++;

  ad_conv_timer = 0;
  /* Normal case, may be changed below. */

  ad_conv_num ++;
  if ( ad_conv_num >= AD_NUM_CONV ) ad_conv_num = 0;

  if ( ad_random_failures > 0 ) {
    if ( Next_Rand ( &ad_fail_index ) > 0x3f ) {
      /* Pretend that this conversion will fail (not end). */

      #if defined(TRACE_HARNESS)
      printf ( "Start_Conversion starts a failing A/D conversion.\n" );
      #endif
      ad_conv_timer = -5000;

      ad_random_failures --;
    }
  }

  ad_converting = 1;
}


void Report_Start_Conversion_Count ( int problem )
/* Reports and then clears the count of Start_Conversion calls.
   The problem parameter associates this count with a given
   analysis problem for this benchmark.
*/
{
  #if defined(TRACE_HARNESS)
  printf ( "Called Start_Conversion %d times in problem %d.\n",
           start_conversion_count, problem );
  #endif

  start_conversion_count = 0;
}


static unsigned int end_of_adc_count = 0;
/* Counts the number of calls of End_Of_ADC. */


unsigned char End_Of_ADC ( void )
/* Is the A/D conversion done, that is, ready for readout?
   0 (CONVERSION_ACTIVE) means yes, any other value means no.
*/
{
  #if defined(TRACE_HARNESS)
  printf ( "End_Of_ADC\n" );
  #endif

  if ( ad_converting == 0 ) {
    #if defined(TRACE_HARNESS)
    printf ( "- conversion not going on.\n" );
    #endif
  }

  end_of_adc_count ++;

  ad_conv_timer ++;

  if ( ad_conv_timer >= ad_conv_delay[ ad_conv_num ] ) {
    /* Conversion done. */

    ad_converting = 0;

    return 0;
  } else {
    /* Conversion still going on. */

    /* Perhaps get a preemptive "hit": */

    if ( max_adc_hits > 0 ) {
      /* We have some hits in reserve. */

      if ( Next_Rand ( &ad_hit_rand_index ) > 0x7f ) {
        /* Hit me again, Sam. */

        #if defined(TRACE_HARNESS)
        printf ( "Hit during A/D\n" );
        #endif
        confirm_hit_result = 1;

        total_adc_hits ++;
        max_adc_hits --;

        ad_converting = 0;

        return 0;
        /* No point in going on. */
      }
    }

    /* Conversion not yet finished and no hit. */

    return 1;
  }
}


void Report_End_Of_ADC_Count ( int problem )
/* Reports and then clears the count of End_Of_ADC calls.
   The problem parameter associates this count with a given
   analysis problem for this benchmark.
*/
{
  #if defined(TRACE_HARNESS)
  printf ( "Called End_Of_ADC %d times in problem %d.\n",
           end_of_adc_count, problem );
  #endif

  end_of_adc_count = 0;
}


unsigned char Get_Result ( void )
{
  unsigned char value;

  /* Return the current MSB of ad_value: */

  value = ( ad_value >> 8 ) & 0xff;

  #if defined(TRACE_HARNESS)
  printf ( "Get_Result %d = 0x%x\n", value, value );
  #endif

  /* Shift the LSB to the MSB, for next Get_Result: */

  ad_value <<= 8;

  return value;
}

void Set_DAC_Output ( unsigned char level )
{
  #if defined(TRACE_HARNESS)
  printf ( "Set_DAC_Output %d\n", level );
  #endif
}



/* The variable ADC_channel_register is defined in health.c. */


/*    Sensor Unit operations : su_ctrl.h, dpu_ctrl.h, isr_ctrl.h    */


#include "su_ctrl.h"


/* Variables simulating the event sensors: */

static unsigned char hit_enabled       = 0;
static unsigned char trigger_flag      = 1;
static unsigned char event_flag        = ACCEPT_EVENT;
static unsigned char trigger_source_0  = 0;
static unsigned char trigger_source_1  = 0;

static unsigned char msb_counter       = 134;
static unsigned char lsb1_counter      = 77;
static unsigned char lsb2_counter      = 88;
static unsigned char rise_time_counter = 102;

static unsigned char sim_self_test = 0;
/* Whether to simulate (successful) SU Self Test. */

static unsigned char self_test_pulse = 0;
/* The level of the SU Self Test pulse. */


void Sim_Self_Test_Trigger ( void )
/* If conditions are such that a Self Test Hit Trigger should
   be generated, simulate this occurrence by modifying the
   SU_state for the SU being tested as the Hit Trigger ISR would do.
   This supports the testing of the SU Self Test sequences in the
   Monitoring task.

   The simulation does not actually invoke the Hit Trigger ISR
   nor does it process the Self Test event. These DEBIE functions
   are tested by calling Hit Trigger ISR directly, not from the
   SU Self Test sequences.
*/
{
  if ( ( sim_self_test   == 1 )
       &&  ( hit_enabled     == 1 )
       &&  ( self_test_pulse >  0 )
       &&  ( self_test_SU_number != NO_SU )
       &&  ( SU_state[ self_test_SU_number - SU1 ] == self_test_trigger_e ) ) {
    #if defined(TRACE_HARNESS)
    printf ( "SU Self Test trigger!\n" );
    #endif
    SU_state[ self_test_SU_number - SU1 ] = self_test_e;
  }
}


static unsigned int event_rand_index = 0;
/* Roving index for ad_random, for randomizing the event data. */


void Random_Event ( void )
/* Sets random data in the event sensors. */
{
  unsigned char val;

  val = Next_Rand ( &event_rand_index );

  trigger_flag     = ( val >> 7 ) & 1;
  event_flag       = ( val >> 6 ) & 1;
  trigger_source_0 = ( val >> 5 ) & 1;
  trigger_source_1 = ( val >> 4 ) & 1;

  msb_counter       = Next_Rand ( &event_rand_index );
  lsb1_counter      = Next_Rand ( &event_rand_index );
  lsb2_counter      = Next_Rand ( &event_rand_index );
  rise_time_counter = Next_Rand ( &event_rand_index );
}


static void Set_Trigger_SU ( sensor_index_t SU )
/* Sets the given SU in trigger_source_0/1. */
{
  #if defined(TRACE_HARNESS)
  printf ( "Set Trigger SU index %d\n", SU );
  #endif

  trigger_source_0 =  SU       & 1;
  trigger_source_1 = ( SU >> 1 ) & 1;
}


void Enable_Hit_Trigger  ( void )
{
  #if defined(TRACE_HARNESS)
  printf ( "Enable_Hit_Trigger\n" );
  #endif
  hit_enabled = 1;
  Sim_Self_Test_Trigger ();
}

void Disable_Hit_Trigger ( void )
{
  #if defined(TRACE_HARNESS)
  printf ( "Disable_Hit_Trigger\n" );
  #endif
  hit_enabled = 0;
  Sim_Self_Test_Trigger ();
}


unsigned char Hit_Trigger_Flag ( void )
{
  #if defined(TRACE_HARNESS)
  printf ( "Hit_Trigger_Flag\n" );
  #endif
  return trigger_flag;
  /* 1 means hit trigger ITs are enabled
     0 means they are disabled.
  */
}


unsigned char Event_Flag ( void )
{
  #if defined(TRACE_HARNESS)
  printf ( "Event_Flag \n" );
  #endif
  return event_flag;
}


unsigned char Get_MSB_Counter ( void )
{
  #if defined(TRACE_HARNESS)
  printf ( "Get_MSB_Counter\n" );
  #endif
  return msb_counter;
}


unsigned char Get_LSB1_Counter  ( void )
{
  #if defined(TRACE_HARNESS)
  printf ( "Get_LSB1_Counter\n" );
  #endif
  return lsb1_counter;
}


unsigned char Get_LSB2_Counter  ( void )
{
  #if defined(TRACE_HARNESS)
  printf ( "Get_LSB2_Counter\n" );
  #endif
  return lsb2_counter;
}


unsigned char Rise_Time_Counter ( void )
{
  #if defined(TRACE_HARNESS)
  printf ( "Rise_Time_Counter\n" );
  #endif
  return rise_time_counter;
}


unsigned char Trigger_Source_0 ( void )
{
  #if defined(TRACE_HARNESS)
  printf ( "Trigger_Source_0\n" );
  #endif
  return trigger_source_0;
}


unsigned char Trigger_Source_1 ( void )
{
  #if defined(TRACE_HARNESS)
  printf ( "Trigger_Source_1\n" );
  #endif
  return trigger_source_1;
}


void Set_SU_Self_Test_Ch ( unsigned char value )
/* Set the SU Self Test Channel selectors. */
{
  #if defined(TRACE_HARNESS)
  printf ( "Set SU Self-Test Channel %x\n", value );
  #endif
}


void Set_Test_Pulse_Level ( unsigned char level )
/* Set the SU Self Test pulse level. */
{
  #if defined(TRACE_HARNESS)
  printf ( "Set SU Self-Test Pulse Level %d\n", level );
  #endif
  self_test_pulse = level;
  Sim_Self_Test_Trigger ();
}


static unsigned int v_down_errors = 0;
/* The number of consecutive error results to
   be returned from the next calls of V_Down.
*/


unsigned char V_Down ( void )
{
  unsigned char result;

  if ( v_down_errors > 0 ) {
    result = 0;  /* Bad. */
    v_down_errors --;
  } else
    result = 1;  /* Good. */

  #if defined(TRACE_HARNESS)
  printf ( "V_Down %d\n", result );
  #endif

  return result;
}


void SignalPeakDetectorReset(
  unsigned char low_reset_value,
  unsigned char high_reset_value )
{
  #if defined(TRACE_HARNESS)
  printf ( "SignalPeakDetectorReset low %d, high %d\n",
           low_reset_value, high_reset_value );
  #endif
}


/* The following operations are implemented in hw_if.c:
   ReadDelayCounters
   ReadRiseTimeCounter
   ResetDelayCounters
   ResetPeakDetector
   SetTriggerLevel
   SetTestPulseLevel
   GetVoltageStatus
   Switch_SU_On
   Switch_SU_Off
   EnableAnalogSwitch
   DisableAnalogSwitch
   SelectSelfTestChannel
   SelectTriggerSwitchLevel
   SelectStartSwitchLevel
*/


/*    Accessing unaligned tm_ushort_t data   */


unsigned short int Short_Value ( tm_ushort_t *x )
{
  unsigned short val;
  wcc_memcpy ( &val, x, sizeof ( val ) );
  return val;
}


/*    "Calling" a "patch function"   */


void Call_Patch ( fptr_t func )
{
  #if defined(TRACE_HARNESS)
  printf ( "Call_Patch 0x%x\n", ( code_address_t )func );
  #endif
}


/*    Checking the sizes of some types    */

#include "tm_data.h"

static volatile uint_least8_t  tsize_s;
static volatile uint_least16_t tsize_l;

void Check_Type_Size ( void )
/*
   Disassemble the code of this function to see the sizes of
   the types char, short, ..., science_data_file_t.
   This function is never called.

*/
{
  tsize_s = sizeof ( char );
  tsize_s = sizeof ( short );
  tsize_s = sizeof ( int  );
  tsize_s = sizeof ( long );

  tsize_s = sizeof ( tm_ushort_t     );
  tsize_s = sizeof ( dpu_time_t      );
  tsize_s = sizeof ( tm_dpu_time_t   );
  tsize_s = sizeof ( SU_settings_t   );
  tsize_s = sizeof ( telemetry_data_t );

  tsize_s = sizeof ( event_record_t     );
  tsize_l = sizeof ( science_data_file_t );
}


/*   Test scenarios    */

/*
  These test cases are not intended as a full functional test.
  They are intended to provide sufficient coverage for measurement-
  based WCET analysis of the tested code. Built-in checks of test
  execution (program state) are used only to check that the DEBIE
  software has done the expected thing in each test case, but the
  checks are definitely not exhaustive.

  Each test cases contributes measurements towards one of the
  "analysis problems" defined for this benchmark. The macro
  FOR_PROBLEM(P) is defined in problems.h and marks the start of
  a test case to be included in problem P. Here P is an integer
  number that identifies the analysis problem as follows:

  P    Problem
  --   -------
  10   1 : TC interrupt handler
  21   2a: TM interrupt handler, most common case
  22   2b: TM interrupt handler, send internal time register
  23   2c: TM interrupt handler, end of TM
  31   3a: Hit Trigger interrupt handler, no ADC errors
  32   3b: Hit Trigger interrupt handler, at most one ADC error
  33   3c: Hit Trigger interrupt handler, any number of ADC errors
  41   4a: TC Execution task, general case
  42   4b: TC Execution task, start TC buffering, MSB
  43   4c: TC Execution task, start TC buffering, LSB
  44   4d: TC Execution task, buffer TC word
  51   5a: Acquisition task, science data not full
  52   5b: Acquisition task, science data full
  61   6a: Monitoring task, no hits or errors
  62   6b: Monitoring task, one hit during A/D conversion
  63   6c: Monitoring task, many hits during A/D conversion
  64   6d: Monitoring task, some SU error detected
  65   6e: Monitoring task, any number of hits and errors

  The macro END_PROBLEM is also defined in problems.h and marks
  the end of execution of a case belonging to the problem named
  by the most recently executed FOR_PROBLEM. Note that more cases
  for the same problem can be added later with another FOR_PROBLEM.
*/

#define Prob1  10

#define Prob2a 21
#define Prob2b 22
#define Prob2c 23

#define Prob3a 31
#define Prob3b 32
#define Prob3c 33

#define Prob4a 41
#define Prob4b 42
#define Prob4c 43
#define Prob4d 44

#define Prob5a 51
#define Prob5b 52

#define Prob6a 61
#define Prob6b 62
#define Prob6c 63
#define Prob6d 64
#define Prob6e 65

#include "problems.h"

#if defined(TRACE_HARNESS)
#define CASE(TXT) printf ("\nCASE: %s:\n\n", TXT)
#else
#define CASE(TXT)
#endif


static int checks = 0;
/* Counts the number of checks done in Check/Zero/Nonzero. */

static int check_errors = 0;
/* Counts the number of errors found in Check/Zero/Nonzero. */


static void Report_Checks ( void )
/* Print number of checks and check-errors. */
{
  TARGET_MARK;
  #if defined(TRACE_HARNESS)
  printf ( "Total checks done %d, failed %d\n", checks, check_errors );
  #endif
}


static void Fail_Check ( char *message )
/* Reports a failed check. */
{
  check_errors ++;
  #if defined(TRACE_HARNESS)
  printf ( "%s: FAILED (#%d)\n", message, check_errors );
  #endif
}


static void Check_Zero ( int cond )
/* Checks that cond == 0. */
{
  checks ++;
  if ( cond )
    Fail_Check( "Check_Zero" );
}


static void Check_Nonzero ( int cond )
/* Checks that cond != 0. */
{
  checks ++;
  if ( cond == 0 )
    Fail_Check ( "Check_Nonzero" );
}


#define Check Check_Nonzero
/* Check that the (boolean) parameter is true (not zero). */


static void Check_No_Errors ( void )
/* Checks that no errors are flagged in telemetry_data.error_status. */
{
  Check_Zero ( telemetry_data.error_status );
}


static void TC_Interrupt ( void )
/* Runs the TC Interrupt Service. */
{
  FOR_PROBLEM( Prob1 );
  TC_InterruptService();
  END_PROBLEM;
}


static void Send_TC ( unsigned char address, unsigned char code )
/* Invokes TC_InterruptService with a TC composed of the
   given address and code, provided with valid (even) parity.
*/
{
  unsigned char par;
  /* The parity. */

  /* Encode the address and code in the TC interface registers: */

  tc_msb = address << 1;
  tc_lsb = code;

  /* Generate the even parity bit: */

  par = tc_msb ^ tc_lsb;
  par = ( par & 0x0F ) ^ ( par >> 4 );
  par = ( par & 0x03 ) ^ ( par >> 2 );
  par = ( par & 0x01 ) ^ ( par >> 1 );

  tc_msb |= par;

  tc_word = ( tc_msb << 8 ) | tc_lsb;

  /* Invoke the TC ISR: */

  tc_timer_overflow = 1;

  TC_Interrupt ();
}


static void Send_TC_Word ( uint_least16_t word )
/* Invokes TC_InterruptService with the given TC word,
   exactly as given.
*/
{
  /* Set the high and low TC bytes: */

  tc_msb = ( word >> 8 ) & 0xff;
  tc_lsb =  word       & 0xff;

  tc_word = ( tc_msb << 8 ) | tc_lsb;

  /* Invoke the TC ISR: */

  tc_timer_overflow = 1;

  TC_Interrupt ();
}

static void TC_ISR_Tests ( void )
/* Test of TC_InterruptService. */
{

  #if defined(TRACE_HARNESS)
  printf ( "\nTC_ISR_Tests\n" );
  #endif

  CASE( "TC rejected because timer overflow is not set" );

  tc_timer_overflow = 0;
  TC_Interrupt ();

  CASE( "TC rejected because TC_state is SC_TM_e" );

  TC_state = SC_TM_e;
  Send_TC ( 0, 0 );

  CASE( "TC rejected because TC_state is memory_dump_e" );

  TC_state = memory_dump_e;
  Send_TC ( 0, 0 );

  CASE( "TC in TC_state = memory_patch_e" );

  TC_state = memory_patch_e;
  Send_TC ( 0, 0 );
  Check ( mail_count[ TCTM_MAILBOX ] == 1 );
  FlushMail ( TCTM_MAILBOX );

  CASE( "TC with parity error" );

  TC_state = TC_handling_e;
  tc_timer_overflow = 1;
  tc_msb = 0;
  tc_lsb = 1;
  tc_word = 1;
  TC_Interrupt ();

  Check_Nonzero ( telemetry_data.error_status & PARITY_ERROR );

  CASE( "TC = ERROR_STATUS_CLEAR, ok" );

  Send_TC ( ERROR_STATUS_CLEAR, ERROR_STATUS_CLEAR );

  /* The parity-error flag is not yet reset, because */
  /* the TC was not yet executed:                    */

  Check_Nonzero ( telemetry_data.error_status & PARITY_ERROR );

  Check ( mail_count[ TCTM_MAILBOX ] == 1 );
  FlushMail ( TCTM_MAILBOX );

  /* Clear the error manually: */

  telemetry_data.error_status = 0;

  Check_No_Errors ();

  CASE( "TC = SEND_STATUS_REGISTER, ok" );

  Send_TC ( SEND_STATUS_REGISTER, 8 );

  Check_No_Errors ();
  Check ( TC_state == register_TM_e );
  Check ( mail_count[ TCTM_MAILBOX ] == 1 );
  FlushMail ( TCTM_MAILBOX );


  CASE( "TC type ALL_INVALID" );

  /* An invalid TC code: */

  Send_TC ( 4, 4 );

  Check ( telemetry_data.error_status == TC_ERROR );
  Check ( mail_count[ TCTM_MAILBOX ] == 0 );
  telemetry_data.error_status = 0;

  CASE( "TC type ONLY_EQUAL, fail" );

  Send_TC ( ERROR_STATUS_CLEAR, ~ERROR_STATUS_CLEAR );

  Check ( telemetry_data.error_status == TC_ERROR );
  Check ( mail_count[ TCTM_MAILBOX ] == 0 );
  telemetry_data.error_status = 0;

  CASE ( "TC type ON_OFF_TC, fail" );

  /* Neither ON_VALUE nor OFF_VALUE nor SELF_TEST. */

  Send_TC ( SWITCH_SU_3, 0x3F );

  Check ( telemetry_data.error_status == TC_ERROR );
  Check ( mail_count[ TCTM_MAILBOX ] == 0 );
  telemetry_data.error_status = 0;

  CASE ( "TC type ONLY_EVEN, fail (odd)" );

  Send_TC ( SEND_STATUS_REGISTER, 5 );

  Check ( TC_state == TC_handling_e );
  Check ( telemetry_data.error_status == TC_ERROR );
  Check ( mail_count[ TCTM_MAILBOX ] == 0 );
  telemetry_data.error_status = 0;

  CASE ( "TC type ONLY_EVEN, fail (too large)" );

  Send_TC ( SEND_STATUS_REGISTER, LAST_EVEN + 2 );

  Check ( TC_state == TC_handling_e );
  Check ( telemetry_data.error_status == TC_ERROR );
  Check ( mail_count[ TCTM_MAILBOX ] == 0 );
  telemetry_data.error_status = 0;


  /* Other tests of TC ISR:
    TC in TC_state = register_TM_e: See TM_Tests.
  */
}


static void Handle_TC ( int problem )
/* Checks that the TC mailbox has a message, then invokes
   HandleTelecommand to handle (usually, execute) the message.
   The problem parameter shows in which analysis problem the
   HandleTelecommand should be included.
*/
{
  Check ( mail_count[ TCTM_MAILBOX ] == 1 );

  FOR_PROBLEM( problem );
  HandleTelecommand ();
  END_PROBLEM;

  Check ( mail_count[ TCTM_MAILBOX ] == 0 );
}


static void Exec_TC (
  unsigned char address, unsigned char code,
  int problem )
/* Invokes TC_InterruptService with a TC composed of the
   given address and code, provided with valid (even) parity,
   then invokes HandleTelecommand to execute the TC.
   The problem parameter shows in which analysis problem the
   HandleTelecommand should be included.
*/
{
  Send_TC ( address, code );
  Handle_TC ( problem );
}


static void Send_Patch_Code (
  uint16_t       address,
  unsigned char *checksum )
/* Sends the multi-word TC to patch code memory at the given address,
   with some arbitary contents. Returns the checksum of the patch,
   for use in the final word of the TC, which is not sent here.
*/
{
  uint_least8_t i;
  unsigned char sum;

  /* Send the patch address: */

  Exec_TC ( WRITE_CODE_MEMORY_MSB, ( address >> 8 ) & 0xff, Prob4b );

  sum = ( tc_word >> 8 ) ^ ( tc_word & 0xff );

  Check_No_Errors ();
  Check ( TC_state == write_memory_e );

  Exec_TC ( WRITE_CODE_MEMORY_LSB,  address       & 0xff, Prob4c );

  sum ^= ( tc_word >> 8 ) ^ ( tc_word & 0xff );

  Check_No_Errors ();
  Check ( TC_state == memory_patch_e );

  /* Send the patch contents, 16 words = 32 octets: */

  _Pragma( "loopbound min 16 max 16" )
  for ( i = 0; i < 16; i++ ) {
    Send_TC_Word ( ( ( uint_least16_t ) i ) << 6 );
    sum ^= ( tc_word >> 8 ) ^ ( tc_word & 0xff );
    Handle_TC ( Prob4d );
  }

  /* The last word remains to be sent. */

  *checksum = sum;
}


static void Send_Patch_Data (
  uint16_t       address,
  unsigned char *checksum )
/* Sends the multi-word TC to patch data memory at the given address,
   with some arbitary contents. Returns the checksum of the patch,
   for use in the final word of the TC, which is not sent here.
*/
{
  uint_least8_t i;
  unsigned char sum;

  /* Send the patch address: */

  Exec_TC ( WRITE_DATA_MEMORY_MSB, ( address >> 8 ) & 0xff, Prob4b );

  sum = ( tc_word >> 8 ) ^ ( tc_word & 0xff );

  Check_No_Errors ();
  Check ( TC_state == write_memory_e );

  Exec_TC ( WRITE_DATA_MEMORY_LSB,  address       & 0xff, Prob4c );

  sum ^= ( tc_word >> 8 ) ^ ( tc_word & 0xff );

  Check_No_Errors ();
  Check ( TC_state == memory_patch_e );

  /* Send the patch contents, 16 words = 32 octets: */

  _Pragma( "loopbound min 16 max 16" )
  for ( i = 0; i < 16; i++ ) {
    Send_TC_Word ( ( ( uint_least16_t ) i ) << 6 );
    sum ^= ( tc_word >> 8 ) ^ ( tc_word & 0xff );
    Handle_TC ( Prob4d );
  }

  /* The last word remains to be sent. */

  *checksum = sum;
}


static void Read_Data_Memory ( uint16_t address )
/* Sends the TC to read 32 octets of data memory, starting at
   the given address, receives the corresponding TM block, and
   handles the TM_READY message to the TC Execution task.
*/
{
  uint_least8_t i;

  Exec_TC ( READ_DATA_MEMORY_MSB, ( address >> 8 ) & 0xff, Prob4a );

  Check_No_Errors ();
  Check ( TC_state == read_memory_e );

  Exec_TC ( READ_DATA_MEMORY_LSB, address & 0xff, Prob4a );

  Check_No_Errors ();
  Check ( TC_state == memory_dump_e );

  /* The 16 first words of data: */

  _Pragma( "loopbound min 16 max 16" )
  for ( i = 0; i < 16; i++ ) {
    Check ( telemetry_pointer < telemetry_end_pointer );

    /* The first TM IT, below, acknowledges the immediate TC
       response and transmits the first data word.
    */

    FOR_PROBLEM( Prob2a );
    TM_InterruptService ();
    END_PROBLEM;

    Check ( TC_state == memory_dump_e );
  }

  /* The last word, with the checksum: */

  Check ( telemetry_pointer == telemetry_end_pointer );

  /* The TM IT below acknowledges the last data word and
     transmits the very last word of the Read Data Memory
     sequence, containing the data checksum.
  */

  FOR_PROBLEM( Prob2c );
  TM_InterruptService ();
  END_PROBLEM;

  /* The TM_READY message: */

  Handle_TC ( Prob1 );

  Check_No_Errors ();
  Check ( TC_state == TC_handling_e );

  /* There is a design error in TM_InterruptService:
     when the Read Data Memory sequence ends, with the
     transmission of the checksum word, the TM IT is
     left enabled. If the TC Execution task does not
     react quickly to the TM_READY message, and disable
     the TM IT before the transmission of the checksum
     word is completed, a new TM IT can invoke
     TM_InterruptService once again, sending the checksum
     word a second time, and perhaps a third time etc.
  */
}


void Clear_Errors ( void )
/* Executes the ERROR_STATUS_CLEAR TC. */
{
  Exec_TC ( ERROR_STATUS_CLEAR, ERROR_STATUS_CLEAR, Prob4a );

  Check_No_Errors ();
  Check ( TC_state == TC_handling_e );
}


static void TC_Task_Tests ( void )
/* Test of TelecommandExecutionTask. */
{
  unsigned char chsum;

  #if defined(TRACE_HARNESS)
  printf ( "\nTC_Task_Tests\n" );
  #endif

  CASE( "TC = ERROR_STATUS_CLEAR, ok" );

  /* Flag an error manually: */
  telemetry_data.error_status = PARITY_ERROR;

  Send_TC ( ERROR_STATUS_CLEAR, ERROR_STATUS_CLEAR );

  /* The parity-error flag is not yet reset, because */
  /* the TC was not yet executed:                    */

  Check_Nonzero ( telemetry_data.error_status & PARITY_ERROR );

  Handle_TC ( Prob4a );

  /* Now the parity-error flag is reset: */

  Check_No_Errors ();

  CASE( "TC to set SU parameters" );

  Exec_TC ( SET_SU_1_PLASMA_1P_THRESHOLD,  23, Prob4a );
  Exec_TC ( SET_SU_2_PLASMA_1P_THRESHOLD,  26, Prob4a );
  Exec_TC ( SET_SU_3_PLASMA_1P_THRESHOLD,  32, Prob4a );
  Exec_TC ( SET_SU_4_PLASMA_1P_THRESHOLD, 102, Prob4a );

  Check_No_Errors ();

  Exec_TC ( SET_SU_1_PLASMA_1M_THRESHOLD, 205, Prob4a );
  Exec_TC ( SET_SU_2_PLASMA_1M_THRESHOLD, 123, Prob4a );
  Exec_TC ( SET_SU_3_PLASMA_1M_THRESHOLD,  99, Prob4a );
  Exec_TC ( SET_SU_4_PLASMA_1M_THRESHOLD,   1, Prob4a );

  Check_No_Errors ();

  Exec_TC ( SET_SU_1_PIEZO_THRESHOLD,  14, Prob4a );
  Exec_TC ( SET_SU_2_PIEZO_THRESHOLD,  54, Prob4a );
  Exec_TC ( SET_SU_3_PIEZO_THRESHOLD,  74, Prob4a );
  Exec_TC ( SET_SU_4_PIEZO_THRESHOLD, 104, Prob4a );

  Check_No_Errors ();

  Exec_TC ( SET_SU_1_PLASMA_1P_CLASS_LEVEL, 104, Prob4a );
  Exec_TC ( SET_SU_2_PLASMA_1P_CLASS_LEVEL, 204, Prob4a );
  Exec_TC ( SET_SU_3_PLASMA_1P_CLASS_LEVEL, 214, Prob4a );
  Exec_TC ( SET_SU_4_PLASMA_1P_CLASS_LEVEL, 234, Prob4a );

  Check_No_Errors ();

  Exec_TC ( SET_SU_1_PLASMA_1M_CLASS_LEVEL, 104, Prob4a );
  Exec_TC ( SET_SU_2_PLASMA_1M_CLASS_LEVEL,  88, Prob4a );
  Exec_TC ( SET_SU_3_PLASMA_1M_CLASS_LEVEL,  66, Prob4a );
  Exec_TC ( SET_SU_4_PLASMA_1M_CLASS_LEVEL,  33, Prob4a );

  Check_No_Errors ();

  Exec_TC ( SET_SU_1_PLASMA_2P_CLASS_LEVEL,  61, Prob4a );
  Exec_TC ( SET_SU_2_PLASMA_2P_CLASS_LEVEL,  21, Prob4a );
  Exec_TC ( SET_SU_3_PLASMA_2P_CLASS_LEVEL,  81, Prob4a );
  Exec_TC ( SET_SU_4_PLASMA_2P_CLASS_LEVEL,  11, Prob4a );

  Check_No_Errors ();

  Exec_TC ( SET_SU_1_PIEZO_1_CLASS_LEVEL,  14, Prob4a );
  Exec_TC ( SET_SU_2_PIEZO_1_CLASS_LEVEL,  24, Prob4a );
  Exec_TC ( SET_SU_3_PIEZO_1_CLASS_LEVEL,  33, Prob4a );
  Exec_TC ( SET_SU_4_PIEZO_1_CLASS_LEVEL,  77, Prob4a );

  Check_No_Errors ();

  Exec_TC ( SET_SU_1_PIEZO_2_CLASS_LEVEL,  14, Prob4a );
  Exec_TC ( SET_SU_2_PIEZO_2_CLASS_LEVEL,  14, Prob4a );
  Exec_TC ( SET_SU_3_PIEZO_2_CLASS_LEVEL,  14, Prob4a );
  Exec_TC ( SET_SU_4_PIEZO_2_CLASS_LEVEL,  14, Prob4a );

  Check_No_Errors ();

  Exec_TC ( SET_SU_1_PLASMA_1E_1I_MAX_TIME, 191, Prob4a );
  Exec_TC ( SET_SU_2_PLASMA_1E_1I_MAX_TIME, 171, Prob4a );
  Exec_TC ( SET_SU_3_PLASMA_1E_1I_MAX_TIME, 161, Prob4a );
  Exec_TC ( SET_SU_4_PLASMA_1E_1I_MAX_TIME, 151, Prob4a );

  Check_No_Errors ();

  Exec_TC ( SET_SU_1_PLASMA_1E_PZT_MIN_TIME,  11, Prob4a );
  Exec_TC ( SET_SU_2_PLASMA_1E_PZT_MIN_TIME,  22, Prob4a );
  Exec_TC ( SET_SU_3_PLASMA_1E_PZT_MIN_TIME,  33, Prob4a );
  Exec_TC ( SET_SU_4_PLASMA_1E_PZT_MIN_TIME,  44, Prob4a );

  Check_No_Errors ();

  Exec_TC ( SET_SU_1_PLASMA_1E_PZT_MAX_TIME, 111, Prob4a );
  Exec_TC ( SET_SU_2_PLASMA_1E_PZT_MAX_TIME, 122, Prob4a );
  Exec_TC ( SET_SU_3_PLASMA_1E_PZT_MAX_TIME, 133, Prob4a );
  Exec_TC ( SET_SU_4_PLASMA_1E_PZT_MAX_TIME, 144, Prob4a );

  Check_No_Errors ();

  Exec_TC ( SET_SU_1_PLASMA_1I_PZT_MIN_TIME,  11, Prob4a );
  Exec_TC ( SET_SU_2_PLASMA_1I_PZT_MIN_TIME,  10, Prob4a );
  Exec_TC ( SET_SU_3_PLASMA_1I_PZT_MIN_TIME,   9, Prob4a );
  Exec_TC ( SET_SU_4_PLASMA_1I_PZT_MIN_TIME,   8, Prob4a );

  Check_No_Errors ();

  Exec_TC ( SET_SU_1_PLASMA_1I_PZT_MAX_TIME, 211, Prob4a );
  Exec_TC ( SET_SU_2_PLASMA_1I_PZT_MAX_TIME, 210, Prob4a );
  Exec_TC ( SET_SU_3_PLASMA_1I_PZT_MAX_TIME, 209, Prob4a );
  Exec_TC ( SET_SU_4_PLASMA_1I_PZT_MAX_TIME, 208, Prob4a );

  Check_No_Errors ();

  CASE( "TC to set classification coefficients" );

  Exec_TC ( SET_COEFFICIENT_1, 1, Prob4a );
  Exec_TC ( SET_COEFFICIENT_2, 2, Prob4a );
  Exec_TC ( SET_COEFFICIENT_3, 3, Prob4a );
  Exec_TC ( SET_COEFFICIENT_4, 4, Prob4a );
  Exec_TC ( SET_COEFFICIENT_5, 5, Prob4a );

  Check_No_Errors ();

  CASE( "TC to patch code memory, call patch" );

  Send_Patch_Code ( 0x1100, &chsum );

  chsum ^= 0x5A;   /* Call patch function. */

  Check_Zero ( telemetry_data.mode_status & MEMORY_WRITE_ERROR );

  Send_TC_Word ( 0x5A00 | chsum );
  Handle_TC ( Prob4a );

  Check_Zero ( telemetry_data.mode_status & MEMORY_WRITE_ERROR );

  Check_No_Errors ();
  Check ( TC_state == TC_handling_e );

  CASE( "TC to patch code memory, no action" );

  Send_Patch_Code ( 0x1300, &chsum );

  chsum ^= 0x00;   /* No action. */

  Check_Zero ( telemetry_data.mode_status & MEMORY_WRITE_ERROR );

  Send_TC_Word ( 0x0000 | chsum );
  Handle_TC ( Prob4a );

  Check_Zero ( telemetry_data.mode_status & MEMORY_WRITE_ERROR );

  Check_No_Errors ();
  Check ( TC_state == TC_handling_e );

  CASE( "TC to patch code memory, soft reset" );

  Send_Patch_Code ( 0x1400, &chsum );

  chsum ^= 0x09;   /* Sof Reset. */

  Check_Zero ( telemetry_data.mode_status & MEMORY_WRITE_ERROR );

  Send_TC_Word ( 0x0900 | chsum );
  Handle_TC ( Prob4a );

  Check_Zero ( telemetry_data.mode_status & MEMORY_WRITE_ERROR );

  Check_No_Errors ();
  Check ( TC_state == TC_handling_e );

  CASE( "TC to patch code memory, warm reset" );

  Send_Patch_Code ( 0x2100, &chsum );

  chsum ^= 0x37;   /* Soft Reset. */

  Check_Zero ( telemetry_data.mode_status & MEMORY_WRITE_ERROR );

  Send_TC_Word ( 0x3700 | chsum );
  Handle_TC ( Prob4a );

  Check_Zero ( telemetry_data.mode_status & MEMORY_WRITE_ERROR );

  Check_No_Errors ();
  Check ( TC_state == TC_handling_e );

  CASE( "TC to patch code memory, invalid action" );

  Send_Patch_Code ( 0x2400, &chsum );

  chsum ^= 0x62;   /* Invalid. */

  Check_Zero ( telemetry_data.mode_status & MEMORY_WRITE_ERROR );

  Send_TC_Word ( 0x6200 | chsum );
  Handle_TC ( Prob4a );

  Check ( telemetry_data.error_status == TC_ERROR );

  Check_Zero ( telemetry_data.mode_status & MEMORY_WRITE_ERROR );

  Clear_Errors ();

  CASE( "TC to patch code memory, invalid address" );

  Send_Patch_Code ( 0x0fff, &chsum );

  chsum ^= 0x00;   /* No action. */

  Check_Zero ( telemetry_data.mode_status & MEMORY_WRITE_ERROR );

  Send_TC_Word ( 0x0000 | chsum );
  Handle_TC ( Prob4a );

  Check ( telemetry_data.error_status == TC_ERROR );

  Check_Zero ( telemetry_data.mode_status & MEMORY_WRITE_ERROR );

  Clear_Errors ();

  CASE( "TC to patch code memory, checksum error" );

  Send_Patch_Code ( 0x1200, &chsum );

  chsum ^= 0x5A;   /* Correct checksum. */
  chsum ^= 0xff;   /* Wrong   checksum. */

  Check_Zero ( telemetry_data.mode_status & MEMORY_WRITE_ERROR );

  Send_TC_Word ( 0x5A00 | chsum );
  Handle_TC ( Prob4a );

  Check_Nonzero ( telemetry_data.mode_status & MEMORY_WRITE_ERROR );
  Check ( TC_state == TC_handling_e );

  Clear_Errors ();

  CASE( "TC to patch code, TC sequence error at first word" );

  Exec_TC ( WRITE_CODE_MEMORY_LSB, 0x32, Prob4a );

  Check ( telemetry_data.error_status == TC_ERROR );

  Check_Zero ( telemetry_data.mode_status & MEMORY_WRITE_ERROR );

  Clear_Errors ();

  CASE( "TC to patch code, TC sequence error at second word" );

  Exec_TC ( WRITE_CODE_MEMORY_MSB, 0x32, Prob4a );

  Check_No_Errors ();
  Check ( TC_state == write_memory_e );

  Exec_TC ( CLEAR_WATCHDOG_FAILURES, CLEAR_WATCHDOG_FAILURES, Prob4a );

  Check ( telemetry_data.error_status == TC_ERROR );

  Check_Zero ( telemetry_data.mode_status & MEMORY_WRITE_ERROR );

  Clear_Errors ();

  CASE( "TC to patch data memory" );

  Send_Patch_Data ( 0x2200, &chsum );

  chsum ^= 0x11;   /* Irrelevant for data patch. */

  Check_Zero ( telemetry_data.mode_status & MEMORY_WRITE_ERROR );

  Send_TC_Word ( 0x1100 | chsum );
  Handle_TC ( Prob4a );

  Check_Zero ( telemetry_data.mode_status & MEMORY_WRITE_ERROR );

  Check_No_Errors ();
  Check ( TC_state == TC_handling_e );

  CASE( "TC to patch data memory, address error" );

  Send_Patch_Data ( 0xfef0, &chsum );

  chsum ^= 0x11;   /* Irrelevant for data patch. */

  Check_Zero ( telemetry_data.mode_status & MEMORY_WRITE_ERROR );

  Send_TC_Word ( 0x1100 | chsum );
  Handle_TC ( Prob4a );

  Check ( telemetry_data.error_status == TC_ERROR );
  Check_Zero ( telemetry_data.mode_status & MEMORY_WRITE_ERROR );

  Clear_Errors ();

  CASE( "TC to patch data memory, TC sequence error at first word" );

  Exec_TC ( WRITE_DATA_MEMORY_LSB, 0x32, Prob4a );

  Check ( telemetry_data.error_status == TC_ERROR );

  Check_Zero ( telemetry_data.mode_status & MEMORY_WRITE_ERROR );

  Clear_Errors ();

  CASE( "TC to patch data memory, TC sequence error at second word" );

  Exec_TC ( WRITE_DATA_MEMORY_MSB, 0x32, Prob4a );

  Check_No_Errors ();
  Check ( TC_state == write_memory_e );

  Exec_TC ( CLEAR_WATCHDOG_FAILURES, CLEAR_WATCHDOG_FAILURES, Prob4a );

  Check ( telemetry_data.error_status == TC_ERROR );

  Check_Zero ( telemetry_data.mode_status & MEMORY_WRITE_ERROR );

  Clear_Errors ();

  CASE( "TC to patch data memory, checksum error" );

  Send_Patch_Data ( 0x2300, &chsum );

  chsum ^= 0x11;   /* Correct checksum. */
  chsum ^= 0xff;   /* Wrong   checksum. */

  Check_Zero ( telemetry_data.mode_status & MEMORY_WRITE_ERROR );

  Send_TC_Word ( 0x1100 | chsum );
  Handle_TC ( Prob4a );

  Check_Nonzero ( telemetry_data.mode_status & MEMORY_WRITE_ERROR );
  Check ( TC_state == TC_handling_e );

  Clear_Errors ();

  CASE( "TC to patch data memory, time-out on TC word reception" );

  Send_Patch_Data ( 0x2200, &chsum );

  chsum ^= 0x11;   /* Irrelevant for data patch. */

  Check ( TC_state == memory_patch_e );
  Check_Zero ( telemetry_data.mode_status & MEMORY_WRITE_ERROR );

  Check ( mail_count[ TCTM_MAILBOX ] == 0 );

  /* Empty mailbox => WaitMail signals "time-out". */

  FOR_PROBLEM( Prob4a );
  HandleTelecommand ();
  END_PROBLEM;

  Check ( TC_state == TC_handling_e );
  Check ( telemetry_data.error_status == TC_ERROR );

  Clear_Errors ();

  CASE( "TC timeout during TC handling, normal" );

  Check ( mail_count[ TCTM_MAILBOX ] == 0 );

  /* Empty mailbox => WaitMail signals "time-out". */

  FOR_PROBLEM( Prob4a );
  HandleTelecommand ();
  END_PROBLEM;

  Check ( TC_state == TC_handling_e );
  Check_No_Errors ();

  CASE( "TC to read data memory" );

  Read_Data_Memory ( 0x2041 );

  CASE( "TC to read data memory, address error" );

  Exec_TC ( READ_DATA_MEMORY_MSB, 0xfe, Prob4a );

  Check_No_Errors ();
  Check ( TC_state == read_memory_e );

  Exec_TC ( READ_DATA_MEMORY_LSB, 0xf0, Prob4a );

  Check ( telemetry_data.error_status == TC_ERROR );

  Clear_Errors ();

  CASE( "TC to read data memory, TC sequence error at first word" );

  Exec_TC ( READ_DATA_MEMORY_LSB, 0xfe, Prob4a );

  Check ( telemetry_data.error_status == TC_ERROR );

  Clear_Errors ();

  CASE( "TC to read data memory, TC sequence error at second word" );

  Exec_TC ( READ_DATA_MEMORY_MSB, 0xab, Prob4a );

  Check_No_Errors ();
  Check ( TC_state == read_memory_e );

  Exec_TC ( CLEAR_WATCHDOG_FAILURES, CLEAR_WATCHDOG_FAILURES, Prob4a );

  Check ( telemetry_data.error_status == TC_ERROR );

  Clear_Errors ();

  CASE( "TC = CLEAR_WATCHDOG_FAILURES" );

  telemetry_data.watchdog_failures = 3;

  Exec_TC ( CLEAR_WATCHDOG_FAILURES, CLEAR_WATCHDOG_FAILURES, Prob4a );

  Check_No_Errors ();
  Check_Zero ( telemetry_data.watchdog_failures );

  CASE( "TC = CLEAR_CHECKSUM_FAILURES" );

  telemetry_data.checksum_failures = 9;

  Exec_TC ( CLEAR_CHECKSUM_FAILURES, CLEAR_CHECKSUM_FAILURES, Prob4a );

  Check_No_Errors ();
  Check_Zero ( telemetry_data.checksum_failures );

  CASE( "TC to set DEBIE time" );

  Exec_TC ( SET_TIME_BYTE_3, 0x44, Prob4a );

  Check_No_Errors ();

  Exec_TC ( SET_TIME_BYTE_2, 0x33, Prob4a );

  Check_No_Errors ();

  Exec_TC ( SET_TIME_BYTE_1, 0x22, Prob4a );

  Check_No_Errors ();

  Exec_TC ( SET_TIME_BYTE_0, 0x11, Prob4a );

  Check_No_Errors ();

  Check ( internal_time == 0x44332211 );

  CASE( "TC to set DEBIE time, sequence error at byte 2" );

  internal_time = 0x01122334;

  Exec_TC ( SET_TIME_BYTE_3, 0x44, Prob4a );

  Check_No_Errors ();

  Exec_TC ( SET_TIME_BYTE_1, 0x33, Prob4a );

  Check ( telemetry_data.error_status == TC_ERROR );

  Check ( internal_time == 0x44000000 );

  Clear_Errors ();

  CASE( "TC to set DEBIE time, sequence error at byte 1" );

  internal_time = 0x01122334;

  Exec_TC ( SET_TIME_BYTE_3, 0x44, Prob4a );

  Check_No_Errors ();

  Exec_TC ( SET_TIME_BYTE_2, 0x33, Prob4a );

  Check_No_Errors ();

  Exec_TC ( SET_TIME_BYTE_0, 0x22, Prob4a );

  Check ( telemetry_data.error_status == TC_ERROR );

  Check ( internal_time == 0x44330000 );

  Clear_Errors ();

  CASE( "TC to set DEBIE time, sequence error at byte 0" );

  internal_time = 0x01122334;

  Exec_TC ( SET_TIME_BYTE_3, 0x44, Prob4a );

  Check_No_Errors ();

  Exec_TC ( SET_TIME_BYTE_2, 0x33, Prob4a );

  Check_No_Errors ();

  Exec_TC ( SET_TIME_BYTE_1, 0x22, Prob4a );

  Check_No_Errors ();

  Exec_TC ( SET_TIME_BYTE_2, 0x11, Prob4a );

  Check ( telemetry_data.error_status == TC_ERROR );

  Check ( internal_time == 0x44332200 );

  Clear_Errors ();

  CASE( "TC = SOFT_RESET" );

  Exec_TC ( SOFT_RESET, SOFT_RESET, Prob4a );

  Check_No_Errors ();

  CASE( "TC = STOP_ACQUISITION in STAND_BY, fail" );

  Check ( GetMode() == STAND_BY );

  Exec_TC ( STOP_ACQUISITION, STOP_ACQUISITION, Prob4a );

  Check ( telemetry_data.error_status == TC_ERROR );
  Check ( GetMode() == STAND_BY );

  Clear_Errors ();

  /* Telecommands tested elsewhere:

    ERROR_STATUS_CLEAR        See TC_ISR_Tests, TM_Tests, others.
    SEND_STATUS_REGISTER      See TM_Tests.
    SEND_SCIENCE_DATA_FILE    See TM_Tests, Acquisition_Tests.
    START_ACQUISITION         See Acquisition_Tests.
    STOP_ACQUISITION          See Acquisition_Tests.
    SWITCH_SU_1/2/3/4         See Acquisition_Tests, SU_Self_Test_Tests.
    TM_READY message          See TM_Tests.
  */
}


void Monitor_Health ( int problem )
/* Executes HandleHealthMonitoring for a particular analysis problem. */
{
  start_conversion_count = 0;
  end_of_adc_count       = 0;

  FOR_PROBLEM( problem );
  HandleHealthMonitoring();
  END_PROBLEM;

  Report_Start_Conversion_Count ( problem );
  Report_End_Of_ADC_Count       ( problem );
}


void Monitoring_Task_Tests ( void )
/* Tests of the Health Monitoring task. */
{
  int sec;
  int tot_errors, max_errors = 20;

  /* Reset all cycles: */

  health_mon_round   = HEALTH_COUNT ;
  temp_meas_count    = TEMP_COUNT   ;
  voltage_meas_count = VOLTAGE_COUNT;
  checksum_count     = CHECK_COUNT  ;

  Set_AD_Nominal ();
  /* To avoid monitoring alarms. */

  CASE( "Monitoring without errors or interrupting hits" );

  /* A/D conversions ready on second poll: */
  Set_AD_Delay ( 2 );

  Check ( health_mon_round   == HEALTH_COUNT );
  Check ( temp_meas_count    == TEMP_COUNT   );
  Check ( voltage_meas_count == VOLTAGE_COUNT );
  Check ( checksum_count     == CHECK_COUNT  );

  /* The first 9 seconds: */

  _Pragma( "loopbound min 9 max 9" )
  for ( sec = 1; sec <= 9; sec ++ ) {
    Monitor_Health ( Prob6a );

    Check ( health_mon_round   == HEALTH_COUNT - sec );
    Check ( temp_meas_count    == TEMP_COUNT   );
    Check ( voltage_meas_count == VOLTAGE_COUNT );
    Check ( checksum_count     == CHECK_COUNT  - sec );
  }

  /* The 10th second: */

  Monitor_Health ( Prob6a );

  Check ( health_mon_round   == HEALTH_COUNT      );
  Check ( temp_meas_count    == TEMP_COUNT    - 1 );
  Check ( voltage_meas_count == VOLTAGE_COUNT - 1 );
  Check ( checksum_count     == CHECK_COUNT   - 10 );

  /* The remaining 170 seconds of the full period: */

  _Pragma( "loopbound min 170 max 170" )
  for ( sec = 11; sec <= 180; sec ++ )
    Monitor_Health ( Prob6a );

  Check ( health_mon_round   == HEALTH_COUNT );
  Check ( temp_meas_count    == TEMP_COUNT   );
  Check ( voltage_meas_count == VOLTAGE_COUNT );
  Check ( checksum_count     == CHECK_COUNT  );

  Check_No_Errors ();


  CASE( "Monitoring without errors, at most one interrupting hit" );

  total_adc_hits = 0;

  _Pragma( "loopbound min 180 max 180" )
  for ( sec = 1; sec <= 180; sec ++ ) {
    max_adc_hits = 1;
    Monitor_Health ( Prob6b );
  }

  #if defined(TRACE_HARNESS)
  printf ( "Total hits %d\n", total_adc_hits );
  #endif

  Check_Nonzero ( total_adc_hits );

  Check ( health_mon_round   == HEALTH_COUNT );
  Check ( temp_meas_count    == TEMP_COUNT   );
  Check ( voltage_meas_count == VOLTAGE_COUNT );
  Check ( checksum_count     == CHECK_COUNT  );

  Check_No_Errors ();

  CASE( "Monitoring without errors, many interrupting hits" );

  total_adc_hits = 0;

  _Pragma( "loopbound min 180 max 180" )
  for ( sec = 1; sec <= 180; sec ++ ) {
    max_adc_hits = HIT_BUDGET_DEFAULT;
    Monitor_Health ( Prob6c );
  }

  #if defined(TRACE_HARNESS)
  printf ( "Total hits %d\n", total_adc_hits );
  #endif

  Check_Nonzero ( total_adc_hits );

  Check ( health_mon_round   == HEALTH_COUNT );
  Check ( temp_meas_count    == TEMP_COUNT   );
  Check ( voltage_meas_count == VOLTAGE_COUNT );
  Check ( checksum_count     == CHECK_COUNT  );

  Check_No_Errors ();

  CASE( "Monitoring with SU errors, no interrupting hits" );

  Set_AD_Unlimited ();
  max_adc_hits  = 0;

  tot_errors = 0;

  _Pragma( "loopbound min 106 max 106" )
  do {
    if ( tot_errors == 4 ) check_current_errors = 5;

    if ( tot_errors == max_errors - 1 ) v_down_errors = 1;
    /* The V_DOWN error has a dramatic effect, so
       we save it for last.
    */

    _Pragma( "loopbound min 180 max 180" )
    for ( sec = 1; sec <= 180; sec ++ )
      Monitor_Health ( Prob6d );

    if ( telemetry_data.error_status != 0 ) {
      tot_errors ++;

      #if defined(TRACE_HARNESS)
      printf ( "Monitoring (6d) error %d, error status %x\n",
               tot_errors, telemetry_data.error_status );
      #endif
      Clear_Errors ();
    }
  } while ( tot_errors < max_errors );

  Check_No_Errors ();

  CASE( "Monitoring with any kind of error and interrupting hit" );

  Set_AD_Unlimited ();

  tot_errors = 0;
  
  _Pragma( "loopbound min 64 max 64" )
  do {
    if ( tot_errors == 4 ) check_current_errors = 5;

    if ( tot_errors == max_errors - 1 ) v_down_errors = 1;
    /* The V_DOWN error has a dramatic effect, so
       we save it for last.
    */

    _Pragma( "loopbound min 180 max 180" ) 
    for ( sec = 1; sec <= 180; sec ++ ) {
      max_adc_hits = HIT_BUDGET_DEFAULT;
      if ( sec > 2 * tot_errors ) ad_random_failures = 2;
      Monitor_Health ( Prob6e );
    }

    if ( telemetry_data.error_status != 0 ) {
      tot_errors ++;

      #if defined(TRACE_HARNESS)
      printf ( "Monitoring (6e) error %d, error status %x\n",
               tot_errors, telemetry_data.error_status );
      #endif
      Clear_Errors ();
    }
  } while ( tot_errors < max_errors );

  Check_No_Errors ();
}


void TM_Tests ( void )
/* Test of TM_InterruptService and telemetry functionality. */
{
  int octets;
  /* Number of octets sent and acknowledge by TM interrupt. */

  #if defined(TRACE_HARNESS)
  printf ( "\nTM_ISR_Tests\n" );
  #endif

  CASE( "One whole round of register TM" );

  Send_TC ( SEND_STATUS_REGISTER, 0 );

  Check_No_Errors ();
  Check ( TC_state == register_TM_e );

  Handle_TC ( Prob4a );

  Check ( TC_state == register_TM_e );

  _Pragma( "loopbound min 62 max 62" )
  for ( octets = 0; octets < sizeof ( telemetry_data_t ); octets += 2 ) {

    if ( telemetry_pointer != ( unsigned char * ) &telemetry_data.time ) {
      FOR_PROBLEM( Prob2a );
      TM_InterruptService ();
      END_PROBLEM;
    } else {
      FOR_PROBLEM( Prob2b );
      TM_InterruptService ();
      END_PROBLEM;
    }

    Check ( TC_state == register_TM_e );
  }

  CASE( "Partial register TM, stop by TC" );

  Send_TC ( SEND_STATUS_REGISTER, 22 );

  Check_No_Errors ();
  Check ( TC_state == register_TM_e );

  Handle_TC ( Prob4a );

  Check ( TC_state == register_TM_e );

  _Pragma( "loopbound min 20 max 20" )
  for ( octets = 0; octets < 40; octets += 2 ) {

    if ( telemetry_pointer != ( unsigned char * ) &telemetry_data.time ) {
      FOR_PROBLEM( Prob2a );
      TM_InterruptService ();
      END_PROBLEM;
    } else {
      FOR_PROBLEM( Prob2b );
      TM_InterruptService ();
      END_PROBLEM;
    }

    Check ( TC_state == register_TM_e );
  }

  Send_TC ( ERROR_STATUS_CLEAR, ERROR_STATUS_CLEAR );

  Check ( TC_state == TC_handling_e );

  Handle_TC ( Prob4a );

  Check_No_Errors ();

  CASE( "Science Data TM" );

  Send_TC ( SEND_SCIENCE_DATA_FILE, SEND_SCIENCE_DATA_FILE );

  Check_No_Errors ();
  Check ( TC_state == SC_TM_e );

  Handle_TC ( Prob4a );

  Check ( TC_state == SC_TM_e );

  /* Absorb TM until a TM_READY message is sent to the TC task: */

  octets = 0;

  _Pragma( "loopbound min 22 max 22" )
  while ( mail_count[ TCTM_MAILBOX ] == 0 ) {
    if ( telemetry_pointer < telemetry_end_pointer ) {
      FOR_PROBLEM( Prob2a );
      TM_InterruptService ();
      END_PROBLEM;
    } else {
      FOR_PROBLEM( Prob2c );
      TM_InterruptService ();
      END_PROBLEM;
    }

    octets += 2;
    Check ( TC_state == SC_TM_e );
  }

  #if defined(TRACE_HARNESS)
  printf ( "Science TM octets sent %d\n", octets );
  #endif

  /* Handle the TM_READY message: */

  Handle_TC ( Prob4a );

  Check ( TC_state == TC_handling_e );

  /* TM tests elsewhere:
     End of memory-dump TM : See Read_Data_Memory and TC_Task_Tests.
     Science Data TM (also): See Acquisition_Tests.
  */
}


static void Trigger_Hit ( int problem )
/* Invoke HandleHitTrigger.
   The problem parameter defines the analysis problem for this test.
*/
{
  Check ( mail_count[ ACQUISITION_MAILBOX ] == 0 );

  #if defined(TRACE_HARNESS)
  printf ( "Hit!\n" );
  #endif

  FOR_PROBLEM( problem );

  HandleHitTrigger ();

  END_PROBLEM;

  #if defined(TRACE_HARNESS)
  if ( mail_count[ ACQUISITION_MAILBOX ] == 0 )
    printf ( "- hit rejected\n" );
  else
    printf ( "- hit accepted\n" );
  #endif
}


static void Trigger_SU_Hit (
  sensor_index_t SU,
  int            problem )
/* Invoke HandleHitTrigger with the given SU in trigger_source_0/1.
   The problem parameter defines the analysis problem for this test.
*/
{
  Set_Trigger_SU ( SU );
  Trigger_Hit ( problem );
}


static void Acquire_Hit (
  int hit_problem,
  int acq_problem )
/* Invoke HandleHitTrigger followed by HandleAcquisition if the hit
   was accepted in the ISR. The problem parameters define the analysis
   problems for this test, separately for the Hit Trigger ISR and
   for the Acquisition task.
*/
{
  Trigger_Hit ( hit_problem );

  if ( mail_count[ ACQUISITION_MAILBOX ] > 0 ) {
    FOR_PROBLEM( acq_problem );
    HandleAcquisition ();
    END_PROBLEM;
  }
}


static void Hit_ISR_Tests ( void )
/* Test of HandleHitTrigger. */
{
  sensor_index_t su;

  #if defined(TRACE_HARNESS)
  printf ( "\nHit_ISR_Tests\n" );
  #endif

  /* Reset the historical record: */

  telemetry_data.hit_budget_exceedings = 0;

  /* Test: */

  CASE( "Hit Trigger, budget exhausted" );

  Check ( telemetry_data.hit_budget_exceedings == 0 );

  hit_budget_left = 0;

  /* Once: */

  Trigger_SU_Hit ( 0, Prob3a );

  Check ( mail_count[ ACQUISITION_MAILBOX ] == 0 );

  Check ( telemetry_data.hit_budget_exceedings == 1 );

  /* Once more: */

  Trigger_SU_Hit ( 1, Prob3a );

  Check ( mail_count[ ACQUISITION_MAILBOX ] == 0 );

  Check ( telemetry_data.hit_budget_exceedings == 2 );

  CASE( "Hit Trigger, budget exhausted for the 255th and 256th time" );

  telemetry_data.hit_budget_exceedings = 254;

  hit_budget_left = 0;

  /* 255th time: */

  Trigger_SU_Hit ( 3, Prob3a );

  Check ( mail_count[ ACQUISITION_MAILBOX ] == 0 );

  Check ( telemetry_data.hit_budget_exceedings == 255 );

  /* 256th time: */

  Trigger_SU_Hit ( 1, Prob3a );

  Check ( mail_count[ ACQUISITION_MAILBOX ] == 0 );

  Check ( telemetry_data.hit_budget_exceedings == 255 );

  CASE( "Hit Trigger, budget left, no A/D errors" );

  hit_budget_left = 15;

  Set_AD_Delay ( 2 );
  ad_conv_num = 0;
  
  _Pragma( "loopbound min 4 max 4" )
  for ( su = 0; su < NUM_SU; su ++ ) {
    Trigger_SU_Hit ( su, Prob3a );

    Check ( mail_count[ ACQUISITION_MAILBOX ] == 1 );

    Check ( mail_message[ ACQUISITION_MAILBOX ] == su + 1 );

    FlushMail ( ACQUISITION_MAILBOX );
  }

  Check ( hit_budget_left == 15 - NUM_SU );

  CASE( "Hit Trigger, budget left, no A/D errors, SU self test ok" );

  _Pragma( "loopbound min 4 max 4" )
  for ( su = 0; su < NUM_SU; su++ ) {
    /* Right self test pulse: */

    self_test_SU_number = su + 1;

    SU_state[ su ] = self_test_trigger_e;

    Trigger_SU_Hit ( su, Prob3a );

    Check ( mail_count[ ACQUISITION_MAILBOX ] == 1 );

    Check ( mail_message[ ACQUISITION_MAILBOX ] == self_test_SU_number );

    Check ( SU_state[ su ] == self_test_e );

    FlushMail ( ACQUISITION_MAILBOX );
  }

  CASE( "Hit Trigger, budget left, no A/D errors, SU self test wrong" );

  _Pragma( "loopbound min 4 max 4" )
  for ( su = 0; su < NUM_SU; su++ ) {
    /* Wrong self test pulse: */

    self_test_SU_number = su + 1;

    SU_state[ su ] = self_test_e;

    Trigger_SU_Hit ( su, Prob3a );

    Check ( mail_count[ ACQUISITION_MAILBOX ] == 1 );

    Check (
      mail_message[ ACQUISITION_MAILBOX ]
      == ( self_test_SU_number | HIT_SELF_TEST_RESET ) );

    Check ( SU_state[ su ] == self_test_e );

    FlushMail ( ACQUISITION_MAILBOX );
  }

  /* Reset the SU states: */

  self_test_SU_number = NO_SU;

  _Pragma( "loopbound min 4 max 4" )
  for ( su = 0; su < NUM_SU; su++ ) SU_state[ su ] = off_e;

  CASE( "Hit Trigger, budget left, all A/D delays at limit but ok" );

  hit_budget_left = 15;

  Set_AD_Delay ( ADC_MAX_TRIES );

  ad_conv_num = 0;

  _Pragma( "loopbound min 4 max 4" )
  for ( su = 0; su < NUM_SU; su ++ ) {
    Trigger_SU_Hit ( su, Prob3b );

    Check ( mail_count[ ACQUISITION_MAILBOX ] == 1 );

    Check (
      mail_message[ ACQUISITION_MAILBOX ]
      == ( ( su + 1 ) ) ); // | HIT_ADC_ERROR));

    FlushMail ( ACQUISITION_MAILBOX );
  }

  Check ( hit_budget_left == 15 - NUM_SU );

  CASE( "Hit Trigger, budget left, one A/D failure, others at limit" );

  hit_budget_left = 15;

  ad_conv_delay[ 0 ] = ADC_MAX_TRIES + 1;

  _Pragma( "loopbound min 4 max 4" )
  for ( su = 0; su < NUM_SU; su ++ ) {
    ad_conv_num = su;
    /* Offset starting index to make a different channel fail. */

    Trigger_SU_Hit ( su, Prob3b );

    Check ( mail_count[ ACQUISITION_MAILBOX ] == 1 );

    Check (
      mail_message[ ACQUISITION_MAILBOX ]
      == ( ( su + 1 ) | HIT_ADC_ERROR ) );

    FlushMail ( ACQUISITION_MAILBOX );
  }

  Check ( hit_budget_left == 15 - NUM_SU );

  CASE( "Hit Trigger, budget left, any number of A/D failures" );

  hit_budget_left = 80;

  su = NUM_SU - 1;

  _Pragma( "loopbound min 80 max 80" )
  while ( hit_budget_left > 0 ) {
    Random_AD_Delay ();

    Trigger_SU_Hit ( su, Prob3c );

    Check ( mail_count[ ACQUISITION_MAILBOX ] == 1 );

    Check (
      ( mail_message[ ACQUISITION_MAILBOX ] & SU_NUMBER_MASK )
      == su + 1 );

    FlushMail ( ACQUISITION_MAILBOX );

    if ( su > 0 ) su --;
    else        su = NUM_SU - 1;
  }

  /* More tests in SU_Self_Test_Tests. */
}


unsigned char switch_su_cmd[ NUM_SU ] = {
  SWITCH_SU_1,
  SWITCH_SU_2,
  SWITCH_SU_3,
  SWITCH_SU_4
};
/* The commands to switch Sensor Units ON or OFF. */


void Report_Event_Histo ( void )
/* Report the collected event counts per SU and class. */
{
  sensor_index_t sen;
  int class;

  #if defined(TRACE_HARNESS)
  _Pragma( "loopbound min 4 max 4" )
  for ( sen = 0; sen < NUM_SU; sen ++ )
    _Pragma( "loopbound min 10 max 10" )  
    for ( class = 0; class < NUM_CLASSES; class ++ ) {
      printf ( "Events from SU %d, class %d: %d\n",
               sen, class, science_data.event_counter[ sen ][ class ] );
    }
  #endif
}


static void Acquisition_Tests ( void )
/* Tests of AcquisitionTask. */
{
  sensor_index_t sen;
  unsigned int hits;
  int octets;

  /* These tests are constructed as a "nominal operation"
     scenario, which incidentally tests the AcquisitionTask
     and the Hit Trigger ISR, as well as other functions.
     The scenario is:

     - TC to switch sensors ON
       - Run Monitoring task to drive the SU state transition.
     - TC to START ACQUISITION
     - Particle hits until the Science Data is full
     - The same number of particle hits with the Science Data full
     - Science Data TM
       - some particle hits during science TM
     - TC to enter STANDBY mode
     - TC to switch sensors OFF.

  */

  CASE( "Turn Sensor Units ON" );

  /* Send the SWITCH ON commands: */

  _Pragma( "loopbound min 4 max 4" )
  for ( sen = 0; sen < NUM_SU; sen ++ ) {
    Check ( SU_state[ sen ] == off_e );

    Exec_TC ( switch_su_cmd[ sen ], ON_VALUE, Prob4a );

    Check_No_Errors ();
    Check ( SU_state[ sen ] == start_switching_e );
  }

  /* Prevent all errors in Monitoring: */

  Set_AD_Nominal ();
  max_adc_hits         = 0;
  ad_random_failures   = 0;
  check_current_errors = 0;
  v_down_errors        = 0;

  /* Run Health Monitoring to drive the SUs ON: */

  Monitor_Health ( Prob6a );

  _Pragma( "loopbound min 4 max 4" )
  for ( sen = 0; sen < NUM_SU; sen ++ )
    Check ( SU_state[ sen ] == switching_e );

  Monitor_Health ( Prob6a );

  _Pragma( "loopbound min 4 max 4" )
  for ( sen = 0; sen < NUM_SU; sen ++ )
    Check ( SU_state[ sen ] == on_e );

  CASE( "SWITCH_SU_ON when already ON, fail" );

  Exec_TC ( SWITCH_SU_2, ON_VALUE, Prob4a );

  Check ( telemetry_data.error_status == TC_ERROR );

  Check ( SU_state[ 1 ] == on_e );

  Clear_Errors ();

  CASE( "Start Acquisition" );

  Check ( ( telemetry_data.mode_status & MODE_BITS_MASK ) == STAND_BY );

  Exec_TC ( START_ACQUISITION, START_ACQUISITION, Prob4a );

  Check_No_Errors ();

  Check ( ( telemetry_data.mode_status & MODE_BITS_MASK ) == ACQUISITION );

  _Pragma( "loopbound min 4 max 4" )
  for ( sen = 0; sen < NUM_SU; sen ++ )
    Check ( SU_state[ sen ] == acquisition_e );

  CASE( "TC = SWITCH_SU in ACQUISITION, fail" );

  Exec_TC ( SWITCH_SU_1, ON_VALUE, Prob4a );

  Check ( telemetry_data.error_status == TC_ERROR );

  Clear_Errors ();

  CASE( "TC = START_ACQUISITION in ACQUISITION, fail" );

  Exec_TC ( START_ACQUISITION, START_ACQUISITION, Prob4a );

  Check ( telemetry_data.error_status == TC_ERROR );

  Check ( GetMode() == ACQUISITION );

  Clear_Errors ();

  CASE( "Hits with Science Data not full" );

  Set_AD_Delay ( 2 );

  hits = 0;

  _Pragma( "loopbound min 2625 max 2625" )
  while ( free_slot_index < max_events ) {
    hits ++;
    internal_time ++;
    hit_budget_left = 10;

    Random_Event ();

    Acquire_Hit ( Prob3a, Prob5a );
  }

  #if defined(TRACE_HARNESS)
  printf ( "Science Data filled with %d events after %d hits.\n",
           max_events, hits );
  #endif
  Report_Event_Histo ();

  CASE( "Hits with Science Data full" );

  _Pragma( "loopbound min 2625 max 2625" )
  while ( hits > 0 ) {
    hits --;
    internal_time ++;
    hit_budget_left = 10;

    Random_Event ();

    Acquire_Hit ( Prob3a, Prob5b );
  }

  Report_Event_Histo ();

  CASE( "Science Data TM, full Science Data, some hits during TM" );

  Send_TC ( SEND_SCIENCE_DATA_FILE, SEND_SCIENCE_DATA_FILE );

  Check_No_Errors ();
  Check ( TC_state == SC_TM_e );

  Handle_TC ( Prob4a );

  Check ( TC_state == SC_TM_e );

  /* Absorb TM until a TM_READY message is sent to the TC task,
     and simulate some particle hits at the same time:
  */

  hits = 0;
  /* We will make MAX_QUEUE_LENGTH + 4 hits. */

  hit_budget_left = MAX_QUEUE_LENGTH + 2;
  /* Ensure that some hits are rejected for budget exhaustion. */

  Check_Zero ( event_queue_length );

  octets = 0;

  _Pragma( "loopbound min 17676 max 17676" )
  while ( mail_count[ TCTM_MAILBOX ] == 0 ) {
    if ( telemetry_pointer < telemetry_end_pointer ) {
      FOR_PROBLEM( Prob2a );
      TM_InterruptService ();
      END_PROBLEM;
    } else {
      FOR_PROBLEM( Prob2c );
      TM_InterruptService ();
      END_PROBLEM;
    }

    octets += 2;
    Check ( TC_state == SC_TM_e );

    if ( hits < MAX_QUEUE_LENGTH + 4 ) {
      /* Hit during Science Data TM: */

      internal_time ++;
      Random_Event ();
      event_flag = ACCEPT_EVENT;

      Acquire_Hit ( Prob3a, Prob5b );

      hits ++;

      if ( hits <= MAX_QUEUE_LENGTH )
        Check ( event_queue_length == hits );
      else
        Check ( event_queue_length == MAX_QUEUE_LENGTH );
    }
  }

  #if defined(TRACE_HARNESS)
  printf ( "Science TM octets sent %d\n", octets );
  #endif

  Check_Zero ( hit_budget_left );

  Check ( hits == MAX_QUEUE_LENGTH + 4 );

  /* Handle the TM_READY message: */

  Handle_TC ( Prob4a );

  Check ( TC_state == TC_handling_e );

  /* Check that the queued events have been recorded: */

  Check_Zero ( event_queue_length );

  Check ( free_slot_index == MAX_QUEUE_LENGTH );

  CASE( "Switch to Self Test in Acquisition mode, fail" );

  Check ( GetMode() == ACQUISITION );

  Check_Zero ( telemetry_data.error_status & TC_ERROR );

  Exec_TC ( SWITCH_SU_2, SELF_TEST, Prob4a );

  Check_Nonzero ( telemetry_data.error_status & TC_ERROR );

  Exec_TC ( ERROR_STATUS_CLEAR, ERROR_STATUS_CLEAR, Prob4a );

  Check_Zero ( telemetry_data.error_status & TC_ERROR );

  CASE( "Stop acquisition" );

  Exec_TC ( STOP_ACQUISITION, STOP_ACQUISITION, Prob4a );

  Check_No_Errors ();

  Check ( ( telemetry_data.mode_status & MODE_BITS_MASK ) == STAND_BY );

  _Pragma( "loopbound min 4 max 4" )
  for ( sen = 0; sen < NUM_SU; sen ++ )
    Check ( SU_state[ sen ] == on_e );

  CASE( "Turn Sensor Units OFF" );

  /* Send the SWITCH OFF commands: */

  _Pragma( "loopbound min 4 max 4" )
  for ( sen = 0; sen < NUM_SU; sen ++ ) {
    Exec_TC ( switch_su_cmd[ sen ], OFF_VALUE, Prob4a );

    Check_No_Errors ();
    Check ( SU_state[ sen ] == off_e );
  }
}

void SU_Self_Test_Tests ( void )
/* Tests of the "SU Self Test" function. */
{
  sensor_index_t sen;

  sim_self_test = 1;

  CASE( "Turn Sensor Units ON for Self Test" );

  /* Send the SWITCH ON commands: */

  _Pragma( "loopbound min 4 max 4" )
  for ( sen = 0; sen < NUM_SU; sen ++ ) {
    Check ( SU_state[ sen ] == off_e );

    Exec_TC ( switch_su_cmd[ sen ], ON_VALUE, Prob4a );

    Check_No_Errors ();
    Check ( SU_state[ sen ] == start_switching_e );
  }

  /* Prevent all errors in Monitoring: */

  Set_AD_Nominal ();
  max_adc_hits         = 0;
  ad_random_failures   = 0;
  check_current_errors = 0;
  v_down_errors        = 0;

  /* Run Health Monitoring to drive the SUs ON: */

  Monitor_Health ( Prob6a );

  _Pragma( "loopbound min 4 max 4" )
  for ( sen = 0; sen < NUM_SU; sen ++ )
    Check ( SU_state[ sen ] == switching_e );

  Monitor_Health ( Prob6a );

  _Pragma( "loopbound min 4 max 4" )
  for ( sen = 0; sen < NUM_SU; sen ++ )
    Check ( SU_state[ sen ] == on_e );

  CASE( "Switch SU2 to Self Test in Standby mode" );

  Check ( GetMode() == STAND_BY );
  Check_No_Errors ();

  Exec_TC ( SWITCH_SU_2, SELF_TEST, Prob4a );

  Check_No_Errors ();

  Check ( self_test_SU_number == 2 );
  Check ( SU_state[ 1 ] == self_test_mon_e );

  CASE ( "Switch SU3 (also) to Self Test, fail" );

  Exec_TC ( SWITCH_SU_3, SELF_TEST, Prob4a );

  Check_Nonzero ( telemetry_data.error_status & TC_ERROR );

  Exec_TC ( ERROR_STATUS_CLEAR, ERROR_STATUS_CLEAR, Prob4a );

  Check_Zero ( telemetry_data.error_status & TC_ERROR );

  Check ( self_test_SU_number == 2 );

  Check_No_Errors ();

  CASE( "Run Self Test for SU2" );

  /* Run Monitoring up to but not including round_7_e: */

  _Pragma( "loopbound min 8 max 8" )
  while ( health_mon_round != round_7_e )
    Monitor_Health ( Prob6a );

  Check ( self_test_SU_number == 2 );
  Check ( SU_state[ 1 ] == self_test_mon_e );

  /* Run round_7_e of Monitoring to start Self Test: */

  Monitor_Health ( Prob6a );

  Check ( self_test_SU_number == 2 );
  Check ( SU_state[ 1 ] == self_test_e );

  /* Run round_6_e of Monitoring to execute Self Test: */

  Check ( health_mon_round == round_6_e );

  Monitor_Health ( Prob6a );

  Check ( self_test_SU_number == NO_SU );
  Check ( SU_state[ 1 ] == on_e );

  Check_No_Errors ();

  CASE( "Run Self Test for SU2, fail" );

  Check ( GetMode() == STAND_BY );
  Check_No_Errors ();

  Exec_TC ( SWITCH_SU_2, SELF_TEST, Prob4a );

  Check_No_Errors ();

  Check ( self_test_SU_number == 2 );
  Check ( SU_state[ 1 ] == self_test_mon_e );

  sim_self_test = 0;
  /* Force self-test to fail. */

  /* Run Monitoring up to but not including round_7_e: */

  _Pragma( "loopbound min 8 max 8" )
  while ( health_mon_round != round_7_e )
    Monitor_Health ( Prob6a );

  Check ( self_test_SU_number == 2 );
  Check ( SU_state[ 1 ] == self_test_mon_e );

  /* Run round_7_e of Monitoring to start Self Test: */

  Monitor_Health ( Prob6a );

  Check ( self_test_SU_number == 2 );
  Check ( SU_state[ 1 ] == self_test_e );

  /* Run round_6_e of Monitoring to execute Self Test: */

  Check ( health_mon_round == round_6_e );

  Monitor_Health ( Prob6a );

  Check ( self_test_SU_number == NO_SU );
  Check ( SU_state[ 1 ] == on_e );

  Check_Nonzero ( telemetry_data.SU_status[ 1 ] & SELF_TEST_ERROR );
  Check ( telemetry_data.error_status == 0x20 );

  Clear_Errors ();

  CASE( "Hit Trigger, SU Self Test, correct pulse" );

  hit_budget_left = 15;

  Set_AD_Delay ( 2 );
  ad_conv_num = 0;

  _Pragma( "loopbound min 4 max 4" )
  for ( sen = 0; sen < NUM_SU; sen ++ ) {
    self_test_SU_number = sen + SU1;

    SU_state[ sen ] = self_test_trigger_e;

    Trigger_SU_Hit ( sen, Prob3a );

    Check ( SU_state[ sen ] == self_test_e );

    Check ( mail_count[ ACQUISITION_MAILBOX ] == 1 );

    Check ( mail_message[ ACQUISITION_MAILBOX ] == sen + SU1 );

    FlushMail ( ACQUISITION_MAILBOX );
  }

  Check ( hit_budget_left == 15 - NUM_SU );

  CASE( "Hit Trigger, SU Self Test, incorrect pulse" );

  hit_budget_left = 15;

  Set_AD_Delay ( 2 );
  ad_conv_num = 0;

  _Pragma( "loopbound min 4 max 4" )
  for ( sen = 0; sen < NUM_SU; sen ++ ) {
    self_test_SU_number = sen + SU1;

    SU_state[ sen ] = self_test_e;

    Trigger_SU_Hit ( sen, Prob3a );

    Check ( SU_state[ sen ] == self_test_e );

    Check ( mail_count[ ACQUISITION_MAILBOX ] == 1 );

    Check ( mail_message[ ACQUISITION_MAILBOX ] ==
            ( ( sen + SU1 ) | HIT_SELF_TEST_RESET ) );

    FlushMail ( ACQUISITION_MAILBOX );
  }

  Check ( hit_budget_left == 15 - NUM_SU );

  CASE( "Hit Trigger, SU Self Test, other pulse" );

  hit_budget_left = 15;

  Set_AD_Delay ( 2 );
  ad_conv_num = 0;

  _Pragma( "loopbound min 4 max 4" )
  for ( sen = 0; sen < NUM_SU; sen ++ ) {
    self_test_SU_number = sen + SU1;

    SU_state[ sen ] = on_e;

    Trigger_SU_Hit ( sen, Prob3a );

    Check ( SU_state[ sen ] == on_e );

    Check ( mail_count[ ACQUISITION_MAILBOX ] == 1 );

    Check ( mail_message[ ACQUISITION_MAILBOX ] == sen + SU1 );

    FlushMail ( ACQUISITION_MAILBOX );
  }

  Check ( hit_budget_left == 15 - NUM_SU );

  Check_No_Errors ();

  CASE( "Turn Sensor Units OFF after Self Tests" );

  /* Send the SWITCH OFF commands: */

  _Pragma( "loopbound min 4 max 4" )
  for ( sen = 0; sen < NUM_SU; sen ++ ) {
    Exec_TC ( switch_su_cmd[ sen ], OFF_VALUE, Prob4a );

    Check_No_Errors ();
    Check ( SU_state[ sen] == off_e );
  }
}



/*   StartSystem () : test scenario    */


static unsigned int test_round = 0;
/* Counts test repetitions. */


void StartSystem( unsigned char task_number )
/*   ORIGINALLY:                                                             */
/* Purpose        : Starts the system.                                       */
/* Interface      : input  - none                                            */
/*                  output - none                                            */
/* Preconditions  : none                                                     */
/* Postconditions : First task is called and system started.                 */
/* Algorithm      : See below, self explanatory.                             */
/*   IN HARNESS:                                                             */
/* Executes the test scenario.                                               */
{
  #if defined(TRACE_HARNESS)
  printf ( "StartSystem %d\n", task_number );
  #endif

  /* Initialize the global data of the tasks: */

  InitHealthMonitoring ();

  /* Testing the ISRs and tasks: */

  _Pragma( "loopbound min 1 max 1" )
  do {
    test_round ++;

    #if defined(TRACE_HARNESS)
    printf ( "Test round %d\n", test_round );
    #endif

    TARGET_START_TEST;

    TC_ISR_Tests ();

    Report_Checks ();

    TC_Task_Tests ();

    Report_Checks ();

    Monitoring_Task_Tests ();

    Report_Checks ();

    TM_Tests ();

    Report_Checks ();

    Hit_ISR_Tests ();

    Report_Checks ();

    Acquisition_Tests ();

    Report_Checks ();

    SU_Self_Test_Tests ();

    Report_Checks ();

    /* Clear for next round: */

    checks = 0;
  } while ( TARGET_REPEAT_TEST );

  #if defined(TRACE_HARNESS)
  printf ( "Total mailbox overflows %d\n", mail_overflows );
  exit ( 0 );
  #endif

}
