/* 
 *	XGProcess.c
 *
 * This file does top-level processing of the incoming data.  Most of it is specific to 
 * the XRS GSE.  The function ProcessClump() is the top level entry point.
 * Other useful functions are NewMainWaves() and SetupMainWaves(), which handle making
 * the waves into which most incoming data goes.
 */

#include <stdio.h>

#include <IgorXOP.h>
#include <XOP.h>
#include <XOPSupport.h>

#include <fp.h>

#include "XGProcess.h"
#include "XGWaitReply.h"
#include "XGCommand.h"
#include "XGFilter.h"
#include "XGUtils.h"
#include "XOPUtils.h"
#include "XGDump.h"
#include "XGError.h"
#include "XGHKRates.h"
#include "handle_dt.h"
#include "XGMaster.h"
#include "XGCCSDS.h"
#include "XGCRC.h"
#include "XGCapTelem.h"
#include "XGNet.h"

long			macTimeDiff = 0;
ccHdrInfo_t		lastPacketHeader;

extern struct	crnt_strct * crnt_ar[];
extern long		scrLevel;
extern long		msgLevel[kNumMsgSelectors];
extern long		caughtUp;

const short	kPutHeaderLevel = 4;

const char	*cdpDFName = "cdp";
DataFolderHandle	dfCDP = nil;


/* Types of the main waves. */
const short typesMain[kNMainWaves] = 
{
	kMainTypeGT,
	kMainTypePix,
	kMainTypeRT,
	kMainTypeQual,
	kMainTypePH,
	kMainTypeTick,
	kMainTypeVern,
	kMainTypeSamp,
	kMainTypeFlags,
	kMainTypeUT
};


/* Names of the main waves */
char *namesMain[kNMainWaves] = 
{
	"SCTime",
	"Pixel",
	"Rt",
	"Qual",
	"Ph",
	"Tick",
	"Vern",
	"Samp",
	"Flags",
	"UTime"
};

/* Info about the memory dump waves */
static waveHndl	dpMemWave = nil;
static short	dpMemType = dpmdNone;

static const short	dpMemWaveType = NT_FP32;  // Don't change this without fixing HandleCDPMemDumpPacket
static const long	dpMemWaveLength = 65536;
static const char	*dpMemCDPAWaveName = "memDumpCDPA";
static const char	*dpMemCDPBWaveName = "memDumpCDPB";
static const char	*dpMemAcheWaveName = "memDumpAche";

const short	tcpFreeWaveType = NT_FP32;
const char	*tcpFreeWaveName = "TCPFree";

const short	tcpTimeWaveType = NT_FP64;
const char	*tcpTimeWaveName = "TCPTime";

long		tcpWaveInd;

extern long		updateInterval;
extern long		numPacketsLost;

long		curPntMain;
long		mainGrowPnts = 1000;
long		hasDTLengths = 1;

waveHndl	tcpFreeWave = nil;
waveHndl	tcpTimeWave = nil;
waveHndl	wavesMain[kNMainWaves] = {nil,nil,nil,nil,nil,nil,nil,nil,nil,nil};
static short	modifiedMain = 0;

static unsigned long	lastUpdate = 0;
static long				scTimeDiff = 0;
//static unsigned long	scTimeOffset = 0;
//static short			sampRate = 12288;
extern long				sampRate;

static const short			kMaxPHAs = 5000;
static struct pha_struct	*phaBuf = nil, *phaPtr, *phaBufEnd;


/*
 * Set all wave handles to nil, because the experiment is new,
 * so they all point to nowhere.
 */
void
NewMainWaves( void )
{
	short	i;
	
	for(i=0; i curPntMain )
				curPntMain = wPnts;
			
			if( WaveType( wavesMain[i] ) != typesMain[i] )
			{
				dimSizes[0] = wPnts;
				err = MDChangeWave( wavesMain[i], typesMain[i], dimSizes );
			}
		}
	}
	
	if( !err )
	{
		err = FetchOrMakeWaveInDF( nil, tcpFreeWaveName, tcpFreeWaveType, &tcpFreeWave,
		 0, &dfCDP );
		wPnts = WavePoints( tcpFreeWave );
		if( wPnts > tcpWaveInd )
			tcpWaveInd = wPnts;
	}

	if( !err )
	{
		err = FetchOrMakeWaveInDF( nil, tcpTimeWaveName, tcpTimeWaveType, &tcpTimeWave,
		 0, &dfCDP );
		wPnts = WavePoints( tcpTimeWave );
		if( wPnts > tcpWaveInd )
			tcpWaveInd = wPnts;
		
		if( !err )
			/* Make data units of time wave "dat", so it will display as time/date */
			SetWaveUnits( tcpTimeWave, "", "dat" );
	}

	/*
	 * We don't have to make all the waves the same length, since
	 * the data-loading routines always check the length.
	 */
	
	return err;
}


void
LockMainWaves( int states[kNMainWaves], void *ptrs[kNMainWaves] )
{
	int		types[kNMainWaves];
	int		newStates[kNMainWaves];
	long	lengths[kNMainWaves];
	short	i;
	
	GetWavesInfo( wavesMain, kNMainWaves, types, lengths, states, ptrs );
	for( i=0; itick_cntr >= 12 )
		nrolls = ( pp->scTime/4096 - scTimeDiff - 6 ) / 16;
	else if( pp->tick_cntr < 4 )
		nrolls = ( pp->scTime/4096 - scTimeDiff + 6 ) / 16;
	else
		nrolls = ( pp->scTime/4096 - scTimeDiff ) / 16;
	
	return pp->tick_cntr + nrolls * 16;
}



static long 
PHAParityOK( struct pha_struct *pp )
{
	unsigned char	*uc, accum;
	short			i, count;
	
	uc = (unsigned char *)pp;
	uc += sizeof(unsigned long);	/* Don't forget to skip scTime field */
	accum = 0;
	
	for( i=0; i<8; i++ )
		accum ^= *uc++;
	
	count = 0;
	for( i=0; i<8; i++ )
	{
		count ^= (accum & 1);
		accum >>= 1;
	}
	
	return !count;
}


static void
HandlePHA( struct pha_struct *pp )
{
	int			err = 0;
	DOUBLE		dval[kNMainWaves];
	short		i;
	
	if( !PHAParityOK( pp ) )
	{
		struct raw_struct *rs = (struct raw_struct *)pp;
		
		PutMsg( mtErr, msDT, 9, "%02hd*(%lu) PHA PARITY ERR: %04X %04X %04X %04X",
		 pp->pixel, pp->scTime, rs->word0, rs->word1, rs->word2, rs->word3 );
		return;		// EARLY RETURN!
	}
	
	dval[wmPH] = pp->pulse_height;
	if( pp->flag_baseline && ( dval[wmPH] > 16383 ) )
		dval[wmPH] -= 32768;
	
	dval[wmGT] = pp->scTime/4096.0;
	dval[wmPix] = pp->pixel;
	dval[wmRT] = pp->rise_time;
	dval[wmQual] = pp->pulse_quality;
	dval[wmTick] = pp->tick_cntr;
	dval[wmVern] = pp->time_vernier;
	dval[wmSamp] = pp->sample_count;
	dval[wmUT] = CalcRealTime( pp );
	dval[wmUT] += dval[wmSamp]/sampRate + dval[wmVern]/(sampRate*16.0);
	dval[wmFlags] = pp->flag_secondary	* (1<<0) +	// 1
					pp->flag_loRes		* (1<<1) +	// 2
					pp->flag_noRes		* (1<<2) +	// 4
					pp->flag_baseline	* (1<<3) +	// 8
					pp->flag_antiCo		* (1<<4) +	// 16
					pp->flag_clipped	* (1<<5);	// 32
	
	for( i=0; i= phaBufEnd )
	{
		DoCDPUpdate();
		if( phaPtr >= phaBufEnd )
		{
			numPacketsLost++;
			PutMsg( mtErr, msGSE, 9, "Lost a packet (too many PHAs)" );
			return;
		}
	}
	
	*phaPtr++ = *phs;
}


/*
 * Accumulate this channel's telemetry CRC, and increment its count
 */
static void
AccumTelemCRC( union dt_union *dup )
{
	struct crnt_strct	*crnt;
	short				pixel;
	
	pixel = dup->pha.pixel;
	crnt = crnt_ar[pixel];
	
	if( crnt->tcrc_valid )
	{
		crnt->telem_count += 1;
		crnt->telem_crc = crc_ary( crnt->telem_crc, 4, (unsigned short *)&(dup->raw.word0) );
	}
}


static void
DTMessage( short level, union dt_union *dup, const char *typeStr )
{
	PutMsg( mtInfo, msDT, level, "%02hd (%lu) %04X %04X %04X %04X %s",
	 dup->pha.pixel, dup->pha.scTime, dup->raw.word0,
	 dup->raw.word1, dup->raw.word2, dup->raw.word3, typeStr );
}


static void
HandleDT( union dt_union *dup )
{
	short	type;
	
	if( 0 == (type = dup->pha.type) )
	{
		/* PHA block */
		DTMessage( 2, dup, "(PHA)" );
		StorePHA( &(dup->pha));
		AccumTelemCRC( dup );
	}
	else if( 1 == type )
	{
		/* dump block */
		DTMessage( 1, dup, "(dump)" );
		HandleDump( dup );
		AccumTelemCRC( dup );
	}
	else if( 3 == type )
	{
		/* rates, HK, reply or error */
		if ( dup->rates.rates0.flag0 == 0 )
		{
			/* Rates */
			/* (Don't accumulate telemCRC)	*/
			DTMessage( 2, dup, "(Rates)" );
			HandleRates( dup );
		}
		else
		{
			/* HK, reply or error */
			if( dup->hk.hk0.blockcount == 3 )	
			{
				/* Reply or error */
				AccumTelemCRC( dup );
				switch( dup->replyorerror.reply.flagrore)
				{
				case 0: /* reply */
					DTMessage( 2, dup, "(reply)" );
					HandleReply( dup );
					break;
				
				case 1: /* error */
					DTMessage( 2, dup, "(error)" );
					HandleError( dup );
					break;
				}
			}
			else
			{
				/* HK 							*/
				/* (Don't accumulate telemCRC)	*/
				DTMessage( 2, dup, "(HK)" );
				HandleHK( dup );
			}
		}
	}
	else	
	{
		/* 2 == type */
		DTMessage( 7, dup,  "(unrecognized block type=2)" );
		AccumTelemCRC( dup );
	}
}


/*
 * Transfer our local information about modified waves to Igor
 */
static void
UpdateIgor( void )
{
	int		i;
	
	WaveHandleModified( tcpFreeWave );
	WaveHandleModified( tcpTimeWave );
	if( modifiedMain )
		for( i=0; i kPutHeaderLevel) || force )
	{
		PutMsg( mtInfo, msCCSDS, sLevel, "CCSDS: %04hX %04hX %04hX %04hX %04hX:"
		 " inst=%hX, subsys=%hx, type=%hX, len=%hu, seqNum=%hu, time=%lu(%f)",
		 sp[0], sp[1], sp[2], sp[3], sp[4], ch->inst>>5, ch->subSys>>2, ch->dataType>>9,
		 ch->dataLength, ch->seqNum, ch->time, ch->time/4096.0 );
	}
}


static unsigned short
BreakCCSDSHeader( unsigned char *p, ccHdrInfo_t *ch )
{
	unsigned short	w0, w1, inst, *sp;
	
	sp = (unsigned short *)p;
	
	w0 = *sp++;
	w1 = *sp++;
	
	inst = w0 & APID_instMask;
	ch->inst = inst;
	ch->subSys = w0 & APID_subSysMask;
	ch->dataType = w0 & APID_typeMask;
	ch->isTele = w0 & APID_teleMask;
	
	ch->seqType = w1 & seqFlagMask;
	ch->seqNum = w1 & seqCntMask;
	
	ch->dataLength = *sp++ - 3;
//	ch->time = *sp++ << 4;
//	ch->time += *sp>>12;
	ch->time = *sp++ * 65536.0;
	ch->time += *sp;
	
	lastPacketHeader = *ch;
	PutCCSDSHeader( p, ch, kPutHeaderLevel, true );
	
	return inst;
}


/*
 * Looks at sp to see if it's a valid CCSDS header.
 * If yes, returns the length of the data.
 * If no, returns 0.
 */
static unsigned short
HeaderValid( unsigned short *sp )
{
	unsigned short	w0, w1;
	unsigned short	subSys, dataType, isTele, dataLength;
	
	w0 = *sp++;
	w1 = *sp++;
	dataLength = *sp - 3;
	
	subSys = w0 & APID_subSysMask;
	dataType = w0 & APID_typeMask;
	isTele = w0 & APID_teleMask;
	
	/* Check the version, type and second header, plus instrument all at once */
	if( ( w0 & (firstBitsMask|APID_instMask) ) == (firstBitsVal | APID_instXRS) )
	{
		/* Okay, the instrument field is okay.  Check the subsys. */
		if( APID_subSysCAPA == subSys || APID_subSysCAPB == subSys )
		{
			/* Looks like CAP data.  Type must be HK (tele or auto) */
			if( APID_typeHK != dataType )
				return 0;
		}
		else if( APID_subSysCDPA == subSys || APID_subSysCDPB == subSys )
		{
			/* Looks like CDP data.  Type must be HK (tele or auto), sci (auto) or mem (auto) */
			if( APID_typeHK != dataType )
			{
				/* Not HK.  If tele is on or it's not either sci or mem, it's bad. */
				if( isTele || ( APID_typeMemDmp != dataType && APID_typeScience != dataType ) )
					return 0;
			}
		}
		else if( APID_subSysACHE == subSys )
		{
			/* ACHE.  Better be hk (tele or auto) */
			if( APID_typeHK != dataType )
				return 0;
		}
		else
		{
			/* Bad instrument. */
			return 0;
		}
		
		/*
		 * It's a valid instrument, subsystem and data type.
		 * Check sequence flag bits.
		 */
		if( (w1 & seqFlagMask) != seqFlagNoGroup )
			return 0;
		
		/* Make sure data length is even */
		if( dataLength % 2 )
			return 0;
			
		/* Okay, it's a good header.  */
		return dataLength;
	}
	
	return 0;
}



/*
 * Check whether we are at the start of a CCSDS packet.  This is only
 * called when we're trying to resync.
 *
 * Makes sure first 16 bits are a valid header, and checks for
 * another valid header after the length of this packet.
 * Returns ssUnknown if not good, ssResync if we have to wait to check.
 */
static short
PacketSync( unsigned char *p, long bytesLeft )
{
	unsigned short	dataLength, *sp;
	
	sp = (unsigned short*)p;
	dataLength = HeaderValid( sp );
	if( dataLength )
	{
		/* Look for another good packet after this one */
		if( dataLength < bytesLeft-20 )
		{
			dataLength = HeaderValid( sp + 5 + dataLength/2 );
			if( dataLength )
				return ssGood;
			else
				return ssUnknown;
		}
		else
		{
			/* This packet is incomplete.  Wait a while */
			return ssResync;
		}
	}
	
	return ssUnknown;
}


/*
 * Returns nonzero if it's an APID we care about (CDP or CAP)
 * In fact it returns ssCDP or ssCAP respectively.
 *
 * Now does some sanity checking, and tries to resync if we're lost.
 * skip tells the caller how many bytes to move ahead.  Normally it's
 * 10 for a good header.
 *
 * returns ssWait if we're still waiting for enough data to resync.
 *
 */
static long
ReadCCSDSHeader( unsigned char *p, long bytesLeft, long *skip, ccHdrInfo_t *ch,
 short state )
{
	unsigned short	inst, resyncState;
	unsigned long	timeout;
	static short	lostSync = false;
	static unsigned long	bytesLost;
	long			result = ssResync;
	
	if( state != ssResync )
	{
		*skip = 10;
		inst = BreakCCSDSHeader( p, ch );
		bytesLost = 0;
		
		if( APID_instXRS == inst )
		{
			/* ACHE packets */
			lostSync = false;
				
			if( ch->subSys == APID_subSysCDPA || ch->subSys == APID_subSysCDPB )
				result = ssCDP;
			else if( ch->subSys == APID_subSysCAPA || ch->subSys == APID_subSysCAPB )
				result = ssCAP;
			else if( ch->subSys == APID_subSysACHE )
			{
				short	i, n;
				char	buf[256], *bp;
				
				bp = buf;
				bp += sprintf( bp, "ACHE: (%lu) %hd bytes: ", ch->time, ch->dataLength );
				
				n = ch->dataLength;
				if( n > 24 )
					n = 24;
				
				for( i=0; idataLength )
					bp += sprintf( bp, "..." );
				
				PutMsg( mtInfo, msACHE, 3, buf );
				result = 0;
			}
			else
				result = ssUnknown;
				
			if( ssUnknown == result )
			{
				PutCCSDSHeader( p, ch, 9, false );
				PutMsg( mtErr, msGSE, 9, "  --> Unknown XRS subsystem %x", ch->subSys );
			}
/*			else
			{
				if( ch->dataLength > bytesLeft-10 )
				{
					*skip = 0;
					result = ssWait;
				}
			}
			return result;
*/			
		}
		else if( APID_instFill == inst && APID_typeFill == ch->dataType )
		{
			/* Accept but ignore fill packets. */
			result = 0;
		}
		else if( APID_instXIS == inst )
		{
			/* Accept but ignore XIS packets. */
			result = 0;
		}
		else if( APID_instDP == inst )
		{
			if( scrLevel <= 1 )
			{
				/* Accept DP packets. */
				char	buf[256], *bp = buf;
				short	i, j, n;
				
				bp += sprintf( bp, "DP: (%lu) %hd bytes:",ch->time, ch->dataLength );
				n = ch->dataLength + 10;
				if( n > bytesLeft )
					n = bytesLeft;
				//if( n > 30 )
				//	n = 30;
				
				PutMsg( mtInfo, msDP, 3, buf );
				
				for( i=0; idataLength > bytesLeft-10 )
			{
				*skip = 0;
				result = ssWait;
			}
			else
			{
				result = ssDP;
			}
*/			
			result = ssDP;
		}
		else if( APID_instHXD == inst )
		{
			/* Accept but ignore HXD packets. */
			return 0;
		}
		else
		{
			/* Not an APID we know about -- Better resync.  Start looking at next byte. */
			*skip = 1;
			p += 1;
			bytesLost = 1;
			bytesLeft -= 1;
			if( !lostSync )
			{
				PutCCSDSHeader( p, ch, 9, false );
				PutMsg( mtErr, msGSE, 9, "  -->Lost Packet Sync (unknown system %x)", inst );
				lostSync = true;
			}
		}
	}
	else
	{
		/* We were already trying to resync */
		*skip = 0;
		result = ssResync;
	}
	
	if( result != ssResync )
	{
		if( ch->dataLength > bytesLeft-10 )
		{
			*skip = 0;
			return ssWait;
		}
		else
		{
			return result;
		}
	}
	
	timeout = LMGetTicks() + 30;
	while( LMGetTicks() < timeout && bytesLeft > 0 )
	{
		resyncState = PacketSync( p, bytesLeft );
		if( ssResync == resyncState )
		{
			/* Not enough data to tell if it's a good packet */
			return ssResync;
		}
		else if( ssUnknown != resyncState )
		{
			*skip += 10;		/* skip over the good header */
			inst = BreakCCSDSHeader( p, ch );
			if( lostSync )
			{
				PutCCSDSHeader( p, ch, 9, false );
				PutMsg( mtErr, msGSE, 9, "Back in Sync.  Lost %lu bytes", bytesLost );
			}
			bytesLost = 0;
			
			lostSync = false;
			if( APID_instXRS == inst )
			{
				if( ch->subSys == APID_subSysCDPA || ch->subSys == APID_subSysCDPB )
					return ssCDP;
				if( ch->subSys == APID_subSysCAPA || ch->subSys == APID_subSysCAPB )
					return ssCAP;
				if( ch->subSys == APID_subSysACHE )
					return 0;	/* Accept but ignore ACHE packets */
			}
		}
		
		p += 1;
		bytesLost += 1;
		bytesLeft -= 1;
		*skip += 1;
	}
	
	/* Didn't manage to resync */
	return ssResync;
}


static const long	fileCreator	= 'hDmp';
static const long	fileType	= 'BINA';


static long
OpenUniqueFile( const char *base, FSSpec *fssp, short *refNumP )
{
	long	err = 0;
	short	suffix;
	char	fName[64];
	
	*refNumP = 0;
	suffix = 0;
	do
	{
		suffix += 1;
		sprintf( fName, "%.25s %hd", base, suffix );
		err = FSMakeFSSpec( 0, 0, CtoPstr( fName ), fssp );
	} while( !err && suffix < 100 );
	
	if( !err )
		err = -1;
	
	if( fnfErr == err )
		err = 0;
	
	if( !err )
	{
		err = FSpCreate( fssp, fileCreator, fileType, smSystemScript );
	}
	
	if( !err )
	{
		err = FSpOpenDF( fssp, fsWrPerm, refNumP );
	}
	
	return err;
}


/*
 * We found a bad packet.  Save it in the Igor application directory with
 * a name like "CCSDS 1".
 */
static void
SaveCCSDSPacket( unsigned char *p, ccHdrInfo_t *ch )
{
	long	err = 0;
	FSSpec	fss;
	short	refNum;
	long	count;
	
	err = OpenUniqueFile( "CCSDS ", &fss, &refNum );
	if( err )
	{
		refNum = 0;
		PutMsg( mtErr, msGSE, 9, "Couldn't open file to save bad packet (error %ld)", err );
	}
	else
	{
		count = sizeof( ccHdrInfo_t );
		err = FSWrite( refNum, &count, ch );
	}
	
	if( !err )
	{
		count = ch->dataLength;
		err = FSWrite( refNum, &count, p );
	}
	
	if( !err )
		PutMsg( mtWarn, msGSE, 9, "Saved bad packet to file \"%#s\"", fss.name );
	else
		PutMsg( mtErr, msGSE, 9, "Error %ld saving bad packet to file \"%#s\"", fss.name );
	
	if( refNum )
	{
		err = FSClose( refNum );
		refNum = 0;
		err = FlushVol( nil, fss.vRefNum );
	}
}


static void
HandleCAPPacket( unsigned char *p, ccHdrInfo_t *ch )
{
	short	side;
	
	if( APID_subSysCAPA == ch->subSys )
		side = 0;
	else
		side = 1;
	HandleCAPData( p, ch->dataLength, side, ch->time );
}


static void
HandleSciencePacket( unsigned char *p, ccHdrInfo_t *ch )
{
	union dt_union	du;
	short	more = true;
	short	blockBytes, length, toPrint, bytesLeft, i;
	unsigned char	*pp;
	char			*bp, buf[256];
	
	/* No echo byte, just a bunch of dt blocks, with lengths */
	
	/* If bad length, discard extra bytes */
	if( hasDTLengths )
		blockBytes = 9;
	else
		blockBytes = 8;
	
	bytesLeft = ch->dataLength;
	while( bytesLeft > 0 )
	{
		du.raw.scTime = ch->time;
		if( hasDTLengths )
		{
			length = *p++;
			bytesLeft--;
			PutMsg( mtInfo, msDT, 3, "-----DT length 0x%02hX = %hdd (%.1f blocks)",
			 length, length, length/8.0 );
			
			if( length % 8 )
			{
				PutMsg( mtErr, msDT, 9,
				 "DT contains %hu bytes (%hu left after %hu 8-byte blocks) at %hu bytes into",
				 length, length % 8, length / 8, ch->dataLength-bytesLeft );
				
				PutCCSDSHeader( p, ch, 9, false );
				
				toPrint = length;
				pp = p;
				while( toPrint > 0 )
				{
					bp = buf;
					for( i=0; i<32&&toPrint>0; i++,toPrint-- )
						bp += sprintf( bp, "%02hX ", *pp++ );
					
					bp += sprintf( bp, "\r" );
					PutMsg( mtErr, msDT, 5, buf );
				}
				
				/* Drop this DT, (and save the packet -- not implemented) */
				SaveCCSDSPacket( p, ch );
				bytesLeft = 0;
				length = 0;
			}
		}
		else
		{
			length = 8;
		}
		
		if( bytesLeft < length )
		{
			PutMsg( mtErr, msDT, 9, "DT length of %hd with %hd bytes remaining (total packet length = %hd)",
			 length, bytesLeft, ch->dataLength );
			break;
		}
		
		while( length > 0 )
		{
			du.raw.word0 = *((short *)p); p += sizeof(short);
			du.raw.word1 = *((short *)p); p += sizeof(short);
			du.raw.word2 = *((short *)p); p += sizeof(short);
			du.raw.word3 = *((short *)p); p += sizeof(short);
			HandleDT( &du );
			length -= 8;
			bytesLeft -= 8;
		}
	}
}


static void
HandleHKPacket( unsigned char *p, ccHdrInfo_t *ch )
{
	unsigned short	mData[mcBufLength+1];
	unsigned char	echo;
	long	len, bytesLeft, i;
	short	side;
	
	if( APID_subSysCDPA == ch->subSys )
		side = 0;
	else if ( APID_subSysCDPB == ch->subSys )
		side = 1;
	else
	{
		PutMsg( mtErr, msGSE, 9, "Oh crap!  HandleHKPacket got a wrong subsys packet (%hu)", ch->subSys );
		return;
	}
	
	bytesLeft = ch->dataLength;
	while( bytesLeft > 0 )
	{
		len = *p++; bytesLeft--;
		if( bytesLeft < len )
			PutMsg( mtErr, msGSE, 9, "%d bytes missing from HK packet", len-bytesLeft );
		
		echo = *p++;		/* Get the echo byte from the master */
		
		if( (echo & ECHO_TYPE_MASK) == ECHO_TYPE_MASTER )
		{
			short	copyWords = (len-1) / 2;
			if( copyWords > mcBufLength )
				copyWords = mcBufLength;
			
			for( i=0; itime, side, echo, mData, (len-1)/2, copyWords );
			bytesLeft -= len;
		}	/* if echo is a master command */
		else
		{
			/* echo is a channel or broadcast command */
			PutMsg( mtInfo, msDT, 3, "(%c) @ %lu: Channel command %02X", side?'A':'B', ch->time, echo );
			bytesLeft -= len;
			p += len-1;
		}
	}
}


static void
PrepareDPMemDump( const char *dfName, const char *waveName, short type )
{
	short	err;
	
	err = FetchOrMakeWaveInDF( dfName, waveName, dpMemWaveType, &dpMemWave,
	 dpMemWaveLength, nil );
	
	if( err )
	{
		dpMemWave = nil;
		dpMemType = dpmdNone;
	}
	else
	{
		dpMemType = type;
	}
}


static void
HandleDPPacket( unsigned char *p, ccHdrInfo_t *ch )
{
	unsigned char	c;
	
	/* we only handle hk */
	if( APID_typeHK == ch->dataType )
	{
		/* all we care about are the mem dump bits */
		c = p[dpHKMemDumpByte] & dpHKMemDumpBitMask;
		switch( c )
		{
		case dpHKMemDumpBitCDPA:
			PrepareDPMemDump( cdpDFName, dpMemCDPAWaveName, dpmdCDPA );
			break;
		
		case dpHKMemDumpBitCDPB:
			PrepareDPMemDump( cdpDFName, dpMemCDPBWaveName, dpmdCDPB );
			break;
		
		case dpHKMemDumpBitAche:
			PrepareDPMemDump( cdpDFName, dpMemAcheWaveName, dpmdAche );			
			break;
		
		default:
			dpMemWave = nil;
			dpMemType = dpmdNone;
			break;
		}
	}
}


/*
 * CDP memory dump (from the DP)
 *	Format:
 *	3 byte address (top byte always 0)
 *	2 byte length
 *	8 128-byte data blocks
 *
 * This puts it into a wave, in the correct place
 */
static void
HandleCDPMemDumpPacket( unsigned char *p, ccHdrInfo_t *ch )
{
	long		err = 0;
	short		i, hState;
	short		type;
	long		waveSize;
	long		dataOffset;
	float		*fp;
	unsigned short	addr, actLength, *dataP;
	const unsigned short	dpMemDumpBytes = 3 + 2 + 8 * 128;
	
	if( GetScrLevel(msDP) <= 3 )
	{
		char	buf[256], *bp;
		
		bp = buf;
		bp += sprintf( bp, "DP mem dump: " );
		for( i=0; i<30; i++ )
			bp += sprintf( bp, "%02hX", *(p+i) );
		
		PutMsg( mtInfo, msDP, 3, buf );
	}
	
	if( ch->dataLength != dpMemDumpBytes )
	{
		PutMsg( mtErr, msDP, 9, "Mem Dump packet is %hu bytes, should be %hu", ch->dataLength,
		 dpMemDumpBytes );
		return;
	}
	
//	err = FetchOrMakeWaveInDF( cdpDFName, dpMemWaveName, dpMemWaveType,
//	 &dpMemWave, dpMemWaveLength, nil );
	
	if( !dpMemWave )
	{
		PutMsg( mtErr, msDP, 9, "Got a DP mem dump packet while DP is not dumping." );
		return;
	}
	
	if( !err )
	{
		/*
		 * Put the data into the wave.
		 */
		type = WaveType( dpMemWave );
		if( type != dpMemWaveType )
		{
			PutMsg( mtErr, msDP, 9, "Mem Dump wave is the wrong type" );
			return;
		}
		
		waveSize = WavePoints( dpMemWave );
		if( waveSize != dpMemWaveLength )
		{
			PutMsg( mtErr, msDP, 9, "Mem Dump wave is not %hu points long", dpMemWaveLength );
			return;
		}
		
		err = MDAccessNumericWaveData( dpMemWave, kMDWaveAccessMode0, &dataOffset );
		if( err )
		{
			PutMsg( mtErr, msDP, 9, "Can't access Mem Dump wave" );
			IgorError( "Can't access Mem Dump wave", err );
			return;
		}
		
		addr =   p[1] * 256 + p[2];
		actLength = p[3] * 256 + p[4];
		if( actLength != ( 1024 ) )
		{
			PutMsg( mtErr, msDP, 9, "Mem Dump length is %hu, not %hu", actLength, dpMemDumpBytes );
			return;
		}
		
		if( (addr + actLength) > dpMemWaveLength )
		{
			PutMsg( mtErr, msDP, 9, "Mem Dump will overflow wave!!" );
			return;
		}
		
		hState = MoveLockHandle( dpMemWave );
		fp = (float*)((char*)(*dpMemWave) + dataOffset);
		fp += addr;
		dataP = (unsigned short*)( p + 3 + 2 );
		for( i=0; i 0 )
		{
			gotSome = true;
			if( ssCDP == state )
			{
				switch( ch.dataType )
				{
				case APID_typeScience:
					HandleSciencePacket( p, &ch );
					break;
					
				case APID_typeMemDmp:
					HandleCDPMemDumpPacket( p, &ch );
					break;
					
				case APID_typeHK:
					HandleHKPacket( p, &ch );
				}
			}
			else if( ssCAP == state )
			{
				HandleCAPPacket( p, &ch );
			}
			else if( ssDP == state )
			{
				HandleDPPacket( p, &ch );
			}
			else
			{
				PutMsg( mtErr, msCCSDS, 9, "ReadCCSDSHeader returned unknown subsystem %d", state );
			}
		}
		
		p += ch.dataLength;
		bytesLeft -= ch.dataLength;
		if( bytesLeft < 10 )
			more = false;
	}
	
	if( bytesLeft < 0 )
	{
		PutCCSDSHeader( packetStart, &ch, 9, false );
		PutMsg( mtErr, msCCSDS, 9, " -->Clump alignment error.  Left with %ld bytes of %d, after using %d",
		 bytesLeft, totalBytes, packetStart-buf );
		bytesLeft = 0;
	}
	
	GetDateTime( &now );
	if( now >= (lastUpdate + updateInterval) && gotSome )
	{
		/* 
		 * Time to actually deal with accumulated PHAs.
		 */
		DoCDPUpdate();
	}
	
	return bytesLeft;
}


/*
 * Return 1 if we're using wave w, 0 otherwise.
 */
int
CheckWavePHA( waveHndl w )
{
	short	i;
	
	for( i=0; i