//  TT8 specific includes
#include        <TT8.h>                 // Tattletale Model 8 Definitions
#include        <tat332.h>              // 68332 Tattletale (7,8) Hardware Definitions
#include        <sim332.h>              // 68332 System Integration Module Definitions
#include        <qsm332.h>              // 68332 Queued Serial Module Definitions
#include        <dio332.h>              // 68332 Digital I/O Port Pin Definitions
#include        <tt8lib.h>              // definitions and prototypes for Model 8 library

// general C includes
#include        <stdio.h>
#include        <stdlib.h>
#include		<string.h>
#include		<time.h>

//  includes for our particular program
#include		"assert.h"
#include		"memcheck.h"
#include		"ad7716.h"
#include		"power.h"
#include		"science.h"
#include		"logtime.h"
#include		"logdisk.h"
#include		"text_log.h"
#include		"misc.h"
#include		"MacTrimp.h"
#include        "power.h"
#include		"criterr.h"
#include		"logger.h"


//  includes for the disk routines
#include		<pcdisk.h>

static int disk_error = 0;

//  these routines and structures are intended for internal use only;
//  routines intended for general use are declared in the .h file
int sci_logger_buffer_init (int chan_n, int buffer_n, long size, int trigger);
void sci_logger_buffer_delete (void);
void sci_logger_buffer_empty (int buffer_n);
void sci_logger_add_point (int n, long value);
void sci_logger_trigger_on (short channel);
void sci_logger_trigger_off (short channel);
void sci_logger_power_on (void);
void sci_logger_power_off (void);
int sci_logger_compute_n (void);
void sci_logger_preview_ASCII (int n);
void sci_logger_preview_binary (int n);
int sci_logger_write_file (int n);
int sci_logger_write_buffer (int fd, int n_buff, long n_points);
void sci_logger_set_header_time (int n);
float sci_logger_value_to_voltage (short n, long value);
#ifdef DEBUG
void sci_logger_assert_file_size (char* fname, int buffer);
void sci_logger_dump_data (void);
#endif

//  Default values for short and long term averages
#define		DEFAULT_T_STA			0.50
#define		DEFAULT_T_LTA           1000.0
#define		DEFAULT_GAIN			2.50

/*  struct science_buffer
 *
 *  There is one of these structures allocated for each active science
 *  channel.  The data pointer is NULL if no space is allocated.  If it
 *  is NULL, then no other variables in this structure are valid or have
 *  any meaning.
*/ 
struct science_buffer  {
	struct MacTrimpi disk_header;
						//  header data for this particular science buffer
						//  this header data is updated whenever the logger
						//  triggers, or when it completes a write to disk
	#ifdef SHORT_SCIENCE_DATA
		short* data;	//  pointer to start of science data circular buffer
	#endif
	#ifdef LONG_SCIENCE_DATA	
		long* data;		//  pointer to start of science data circular buffer
	#endif	
	long n_data;		//  length of the data buffer (number of points)
	long newest_data;	//  index of the newest point in the data buffer

	float gain;			//  current gain of this channel
	short channel_num;	//  A/D channel number from which this data came.

	float t_pre_event;	//  Requested length of the pre-event data record in seconds.
						//  This number is passed to us by the user
	long n_pre_event;	//	Actual length of the pre-event data record in points.
						//  This number is calculated from the user's time and the
						//  current sample rate.  It may be reduced from the expected
						//  value if the number exceeds the length of the science buffer
	float t_post_event;	//  length of the post-event data record in seconds
	long n_post_event;	//	length of the post-event data record in poitns
	float t_STA;		//  length of short term average in seconds
	long n_STA;			//  number of points in the STA
	float t_LTA;		//  length of long term average in seconds
	long n_LTA;			//  number of points in the LTA

	float STA;			//  calculated value of short term average
	float LTA;			//  calculated value of long term average
	short trigger_chan;	//  non-zero if this particular channel should be
						//  allowed to trigger the system.
						//  Does not reflect at all on other channels
	short chans_to_trigger;	//  when this channel is triggered, it forces other channels
							//  to trigger as well; this specifies which other channels
							//  are to be triggered.
	float trigger_level;	//  ratio of STA/LTA at which we will trigger
	short trigger_state;	//  can take on values of WAITING, TRIGGERED, or POST_EVENT
	short trigger_request;	//  set non-zero whenever any channel requests that this
							//  channel trigger
	long n_post_event_left;		//  number of points remaining to collect in a
								//  post event sequence
	long n_waiting_to_write;	//  number of data point available to write to disk
								//  Note that the data may be non-contiguous if the
								//  buffer was wrapped around, as it often will be
	long total_to_write;		//  total number of points which will be written to
								//  disk as part of the current write cycle.  This data
								//  is used to interpolate to update the start times of
								//  files when multiple files are required to dump the
								//  current data.  This is equal to n_waiting_to_write
								//  when the write cycle start.
	long n_written;				//  number of data points already written to disk
								//  as part of the current file.
	long total_written;			//  total number of points written to disk (any file)
								//  during the current write cycle;
	//  REVIEW - we probably have unnecessary code in here in that we force
	//  next_to_write to be -1 if there are no data points waiting.  We could
	//  just let it float
    long next_to_write;			//  index of the next data point to write to disk.
                        		//  this is set to -1 if we are not writing
	double rate;				//  The calculated rate based on the above two parameters
								//  and the number of points waiting.
	time_tt start_time;			//  Time at which first point in buffer was collected
								//  This time is set when we trigger ON and updated when
								//  we write all data from the buffer to disk
	time_tt end_time;			//  Time at which the last point in the buffer was/
								//  collected.  This time is set when we actually go
								//  to write the data to disk.
	time_tt next_write_time;	//  Time at which the data pointed at by the index
								//  next_to_write was collected.
};






/*  struct science_logger
 *
 *  holds the data related to the overall science logging operation.  A
 *  separate structure holds the data refering to a particular channel
 *
*/
struct science_logger {
	struct science_buffer channel[N_SCI_CHANNELS];
	int logger_open_flag;		//  non-zero if the logger is opened
	int n_channels;				//  total number of channels being saved
	int current_buffer;			//  science_buffer index to use when storing
								//  the next piece of data to arrive
	int ASCII_preview_chan;		//  channel to dump in ASCII when previewing data
	int binary_preview_chan;	//  channel number to dump in binary when previewing data
	long disk_threshold;		//  maximum number of points to accumulate
								//  in the disk buffer before requesting a disk
								//  access
	long max_points_per_file;	//  maximum number of points to write to a data file
	unsigned long n_collected;	//  counts initial points collected
	int data_started;			//  non-zero if data is flowing
};
struct science_logger sci_logger;



//  possible values of trigger_flag
#define		WAITING		0
#define		TRIGGERED	1
#define		POST_EVENT	2
//  The next few subroutines are error checking routines used during debug only.
//  If the variable DEBUG is defined, they execute, otherwise they are defined
//  as NOOP.  These must be the first routines in this file to insure that the
//  'defines' work correctly.  These routines are not designed for error checking
//  variables that the user enters.  That code is spread throughout the file.
//  Rather, they are designed to insure that the internal variables
//  describing what the code should do are all self-consistent, and that they
//  won't crash the system when the code executes.  If an error is discovered,
//  the code will abort and print an error message.  The routine
//  sci_logger_assert_valid() should be callable at the start or finish of any
//  of these routines without printing an error message.
//
#ifdef DEBUG
void SCI_VALIDITY_FAILURE (void* exp, unsigned line1, unsigned line2);
void sci_logger_assert_valid(unsigned calling_line);
//  This is essentially a special version of ASSERT that allows us to print out
//  two line numbers.  It is used only here for the sci_logger_assert_valid routine.
//  It is useful because it allows us to print both the line number of the assert
//  which failed and the line number from which we called the assert_valid routine.
//  We don't bother to print the file name, because it is used only within this file
//
void SCI_VALIDITY_FAILURE (void* exp, unsigned line1, unsigned line2)  {
	fflush (stdout);
	fflush (stderr);
	printf ("\n\n***************************************\n");
	printf ("Science logger validity failure: %s @\n", (char*)exp);
	printf ("  line %d, called from line %d\n", line1, line2);
	printf ("***************************************\n");
	sci_logger_dump_data ();
	fflush (stdout);
    fflush (stderr);
	exit (-1);
}
#define TEST_VALID(exp) \
	    ( (exp) ? (void) 0 : SCI_VALIDITY_FAILURE (#exp, __LINE__, calling_line) )
//  sci_logger_assert_valid(void)
//  Internal use only
//  Debugging use only
//
//  This routine checks the internal consistency of the science logger
//  data structure in every way that I can think of.  It should be updated
//  any time the logger code is changed.
//  If the variable DEBUG is not defined, then this subroutine is defined
//	so as to be a NOOP.  See code immediately following the #else.
//
void sci_logger_assert_valid(unsigned calling_line)  {
	int n, buffer;

	TEST_VALID (sizeof (struct MacTrimpi) == 512);
	if (sci_logger.logger_open_flag == 0)  {
		//  not open.  make sure no data is allocated
		for (n = 0;  n < N_SCI_CHANNELS;  n++)
			TEST_VALID (sci_logger.channel[n].data == NULL);
		return;
	}
	//  a value other than 0 or 1 would imply that a pointer went awry
	TEST_VALID (sci_logger.logger_open_flag == 1);

	//  if we get here, then we are open.  start by testing the variables
	//  in the general science logger struct.  Most of these should be
	//  obvious
	TEST_VALID (sci_logger.n_channels >= 0);
	if (sci_logger.n_channels > 0)
		TEST_VALID (sci_logger.current_buffer < sci_logger.n_channels);
	else
		TEST_VALID (sci_logger.current_buffer == 0);	
	TEST_VALID (sci_logger.n_channels <= N_SCI_CHANNELS);
	TEST_VALID (sci_logger.current_buffer >= 0);
	TEST_VALID (sci_logger.max_points_per_file > 1024);
	//  The max is choosen arbitrarily.  This is about 136 minutes at
	//  128 samples/second.  Increase it if you want.
	TEST_VALID (sci_logger.max_points_per_file <= 1048576L);
//	TEST_VALID (sci_logger.conversion_rate >= -1.0);
	//  current limit, but no reason not to extend it higher
//	TEST_VALID (sci_logger.conversion_rate < 258);
	TEST_VALID (sci_logger.binary_preview_chan >= -1);
	#ifdef REVA
	TEST_VALID (sci_logger.binary_preview_chan <= N_SCI_CHANNELS);
	TEST_VALID (sci_logger.binary_preview_chan != 0);
	#endif
	#ifdef REVB
	TEST_VALID (sci_logger.binary_preview_chan < N_SCI_CHANNELS);
	#endif
	
	TEST_VALID (sci_logger.ASCII_preview_chan >= -1);
	#ifdef REVA
	TEST_VALID (sci_logger.ASCII_preview_chan <= N_SCI_CHANNELS);
	TEST_VALID (sci_logger.ASCII_preview_chan != 0);
	#endif
	#ifdef REVB
	TEST_VALID (sci_logger.ASCII_preview_chan < N_SCI_CHANNELS);
	#endif	

	//  then test the variables in the individual science buffer structs
	for (buffer = 0;  buffer < N_SCI_CHANNELS;  buffer++)  {
		if (buffer >= sci_logger.n_channels)  {
			//  If the channel is not in use, data should be NULL, and all
			//  other variables are undefined and irrelavent
			TEST_VALID (sci_logger.channel[buffer].data == NULL);
			continue;
		}

		//  in use channels should have data allocated.  We could narrow this further
		//  for use on the TT8 in that the pointer should point into RAM, which has
		//  a limited and known range.
		TEST_VALID (sci_logger.channel[buffer].data != NULL);
		//  strictly, n_data must be > 0;  we arbitrarily up this to 512
		TEST_VALID (sci_logger.channel[buffer].n_data >= 512);
		TEST_VALID (sci_logger.channel[buffer].newest_data >= -1);
		TEST_VALID (sci_logger.channel[buffer].newest_data < sci_logger.channel[buffer].n_data);
		TEST_VALID (sci_logger.channel[buffer].n_waiting_to_write >= 0);
		TEST_VALID (sci_logger.channel[buffer].n_waiting_to_write < sci_logger.channel[buffer].n_data);

		if (sci_logger.channel[buffer].n_waiting_to_write > 0)
			TEST_VALID (sci_logger.channel[buffer].next_to_write >= 0);
		else
			TEST_VALID (sci_logger.channel[buffer].next_to_write == -1);
		TEST_VALID (sci_logger.channel[buffer].next_to_write < sci_logger.channel[buffer].n_data);
		TEST_VALID (sci_logger.channel[buffer].channel_num < N_SCI_CHANNELS);
		TEST_VALID (sci_logger.channel[buffer].channel_num >= 0);
		
		TEST_VALID (sci_logger.channel[buffer].t_pre_event >= 0);
		TEST_VALID (sci_logger.channel[buffer].n_pre_event < sci_logger.channel[buffer].n_data);
		TEST_VALID (sci_logger.channel[buffer].t_post_event >= 0);
		TEST_VALID (sci_logger.channel[buffer].t_STA >= 0);
		TEST_VALID (sci_logger.channel[buffer].t_LTA >= 0);
		TEST_VALID (sci_logger.channel[buffer].STA >= 0);
		TEST_VALID (sci_logger.channel[buffer].LTA >= 0);

		//  can change this next one when we extend the logger to more channels
		TEST_VALID ((sci_logger.channel[buffer].chans_to_trigger&0xFFF0) == 0);
		TEST_VALID ((sci_logger.channel[buffer].trigger_state == POST_EVENT) ||
					(sci_logger.channel[buffer].trigger_state == TRIGGERED ) ||
					(sci_logger.channel[buffer].trigger_state == WAITING   )   );
		//  not intrinsically important, but it may indicate the failure
		//  of a pointer which led to an overwrite.
		TEST_VALID ((sci_logger.channel[buffer].trigger_request == 0) ||
					(sci_logger.channel[buffer].trigger_request == 1)   );
	}
}
#else
	#define sci_logger_assert_valid(exp) ((void)0)
#endif




//  sci_logger_power_on_init ()
//  User callable
//
//	Inits variables to a known state when the logger is first powered
//  on.  Note that 'power on' refers to the main computer, NOT to the
//  science logger A/D converters.  We should therefore be sure that the
//  outputs connected to converter digital lines are held low so that we
//  don't accidently power the A/D or associated chips through the logic
//  lines.
//
void sci_logger_power_on_init (void)  {
	int n;

	#ifndef SHORT_SCIENCE_DATA
		#ifndef LONG_SCIENCE_DATA
			printf ("You must define either SHORT_SCIENCE_DATA or LONG_SCIENCE_DATA\n");
			printf ("at compile time.\n");
			ResetToMon ();
		#endif
	#endif		
	#ifdef SHORT_SCIENCE_DATA
		#ifdef LONG_SCIENCE_DATA
			printf ("You must define only one of SHORT_SCIENCE_DATA or LONG_SCIENCE_DATA\n");
			printf ("at compile time.\n");
			ResetToMon ();
		#endif
	#endif		
	
	sci_logger.logger_open_flag = 0;
	sci_logger.data_started = 0;
	for (n = 0;  n < N_SCI_CHANNELS;  n++)
		sci_logger.channel[n].data = NULL;
	sci_logger_assert_valid(__LINE__);
	
	//  make sure the gain hardware is inited
	PConfOutp (E, 3);
	PConfOutp (E, 5);
	PConfOutp (E, 6);
	PConfOutp (E, 7);
	PClear (E, 3);
	PClear (E, 5);
	PClear (E, 6);
	PClear (E, 7);
}

		
//  sci_logger_open ()
//  User callable
//
//	This routine must be called before any other of the science logger routines
//  are called.  It performs various memory allocations and initializations.
//
//	Returns: 0 if OK, negative otherwise.
//  Failures: can fail only if memory allocations in the AD7716 init
//		routine fails.
//
int sci_logger_open (void)  {
	sci_logger_assert_valid(__LINE__);
    ASSERT (sci_logger.logger_open_flag == 0);

	//  first set the variable which tells other routines that the
	//  logger is opened.  Without this, most of them will not respond
	sci_logger.logger_open_flag = 1;
	sci_logger.data_started = 0;
	
	sci_logger.n_channels = 0;
	sci_logger.current_buffer = 0;
	sci_logger.ASCII_preview_chan = -1;
	sci_logger.binary_preview_chan = -1;
	sci_logger.n_collected = 0;
	
	//  Arbitrarily choosen file length.  Change it if you want, but for maximum disk
	//  efficiency, make sure it remains an integer multiple of the length of a disk
	//  allocation unit.  The standard disk allocation unit for big disks under the
	//  FAT16 file system is 32k.  The standard disk allocation unit for FAT32 if
	//  formatted by Windows is 4k.  However, if formatted using the routines that
	//  come with the data logger it will be 32k.  This is done because I assume we
	//  are always writting relatively large files, and because the file system will
	//  be somewhat more efficient if we allocate disk in relatively large chunks.
	#ifdef SHORT_SCIENCE_DATA
		sci_logger.max_points_per_file = (SCIENCE_FILE_LENGTH-sizeof(struct MacTrimpi))/sizeof (short);
	#endif
	#ifdef LONG_SCIENCE_DATA	
		sci_logger.max_points_per_file = (SCIENCE_FILE_LENGTH-sizeof(struct MacTrimpi))/sizeof (long);
	#endif	

	//  REVIEW - should we have a default conversion rate???
	
	//  turns power on and configures the port pins which control the
	//  channel gain
	sci_logger_power_on ();

	//  Initialize the AD7716 interface, which sets the buffer length
    //  A buffer length of 24576 points stores approximately 63 seconds
    //	of data at 128 Hz if we are saving three channels.  The required
    //  length is set by the time which we may go without collecting
    //  data from the buffer.  In normal operation, this will be the time
    //  required to write the full buffer to disk.  63 seconds appears
    //  to be considerable overkill
//	if (AD7716_open (4608L) < 0)  {
	if (AD7716_open (AD7716_BUFFER_LENGTH) < 0)  {
		sci_logger_power_off ();
		sci_logger.logger_open_flag = 0;
		return -1;
	}
	sci_logger_assert_valid(__LINE__);
	return 0;
}


	
	
	
//  sci_logger_close
//  User callable
//
//  This routine is called when the user is done with the science logger.
//  It closes the low level interface to the AD, and returns all memory to
//  the system.  The only science logger routine which can be called after
//  this is the open routine.
//
//	NOTE: we do not flush the buffers to disk before closing the logger.
//  If the user wants any data saved, call the disk_write routine first
//
void sci_logger_close (void)  {
	sci_logger_assert_valid(__LINE__);
	//  NOOP if already closed
	if (sci_logger.logger_open_flag == 0)
		return;

	//  try to delete buffers in the reverse of the allocation order
	sci_logger_buffer_delete ();

	AD7716_close ();
	
	//  turns power off and configures the port pins which control the
	//  channel gain as outputs low so that we can avoid powering
	//  the analog section through these outputs
	sci_logger_power_off ();
	sci_logger.logger_open_flag = 0;
	
	sci_logger_assert_valid(__LINE__);
}




//  User callable
int sci_logger_is_open (void)  {
	ASSERT ((sci_logger.logger_open_flag == 0) || (sci_logger.logger_open_flag == 1));
	
	return sci_logger.logger_open_flag;
}	





//  sci_logger_activate_chans
//  User callable
//
//  Sets which channels are recorded.  Each recorded channel is also triggered
//  under the current code.  This is a user callable routine.
//
//  Arguments: bit pattern representing which channels are to be active.
//	Returns: 0 if OK, negative on failure
//	Failures: only on memory allocation errors in the channel_setup subroutine
//
//	NOTE that we do not restart the data stream at the end of this routine
//
int sci_logger_activate_chans (int record_chans)  {
	int trigger_chans, channel_bit;
	int n, chan, n_chans;
	long buffer_size;
	sci_logger_assert_valid(__LINE__);

	//  don't respond if the logger isn't open
	if (sci_logger.logger_open_flag == 0)
		return 0;
	
	//  Before setting new channels we have to turn off the data stream and
	//  clear the buffers so that we can avoid mismatches when we change
	//  which channels are included in the data stream
	sci_logger_stop_data ();
	sci_logger_buffer_delete ();

	//  now tell the 7716 which channels to pass on to us
	AD7716_set_channels (record_chans);
	
	//  for now every recorded channel is a trigger channel
	trigger_chans = record_chans;

	//  count the number of channels so we can decide how much memory to
	//  allocate to each.  It is important that other big users of memory
	//  already have allocated what they need.  In particular, make sure
	//  the AD7716 has grabbed its buffer already
	n_chans = 0;
	for (n = 0;  n < N_SCI_CHANNELS;  n++)  {
		channel_bit = 0x01 << n;
		if (record_chans & channel_bit)
			n_chans++;
	}
	buffer_size = mem_find_max_malloc ();
	//  don't use the last of the memory, as other routines may want some
	//  small amount.  Hold back 3 k.
	buffer_size -= 3*1024;

	printf ("Total memory available for science buffers is %ld\n", buffer_size);
	if (n_chans > 0)
		buffer_size /= n_chans;
	#ifdef SHORT_SCIENCE_DATA
		buffer_size /= sizeof (short);
	#endif
	#ifdef LONG_SCIENCE_DATA	
		buffer_size /= sizeof (long);
	#endif
	//  Buffer size must be a multiple of 4 only because of the special
	//  malloc routines which we use during debugging.  These routines
	//  add space at each end of the buffer to use for error checking
	//  so as to catch memory overuns.
	if ((buffer_size%4) != 0)
		buffer_size -= (buffer_size%4);
	printf ("Each active science channel has a buffer which is %.1f seconds long\n",
								(float)(buffer_size/sci_logger_get_conversion_rate ()));
	printf ("at the current conversion rate.\n");

	//  n_channels is zeroed by the bufer_delete routine
	ASSERT (sci_logger.n_channels == 0);
	for (chan = 0;  chan < N_SCI_CHANNELS;  chan++)  {
		channel_bit = 0x01 << chan;
		if (record_chans & channel_bit)  {
			//  REVIEW - kinda a kluge which we do so that a trigger on any channel
			//  will trigger all other channels.  This info not available at the
			//  lower level of "buffer_init"
			sci_logger.channel[sci_logger.n_channels].chans_to_trigger = record_chans;

			//  this channel is active.  Init a buffer for the incoming data
			if (sci_logger_buffer_init (chan, sci_logger.n_channels++,
											 buffer_size, trigger_chans&channel_bit) != 0)  {
				//  if we failed, delete all previous buffers
				sci_logger_buffer_delete ();
				return -1;
			}
			printf ("Science channel %d initialized\n", chan);
		}  else  {
			//  trigger bit shouldn't be set unless channel bit is
			ASSERT ((trigger_chans&channel_bit) == 0);
			;
		}	
	}

	sci_logger.current_buffer = 0;      	//  1st data point goes to buffer 0
	sci_logger_set_binary_preview (-1);		//  all data preview turned off
	sci_logger_set_ASCII_preview (-1);
	
	//  REVIEW - where should the threshold be set - this algorithm allows 5 seconds
	sci_logger.disk_threshold = buffer_size - 5*sci_logger_get_conversion_rate ();
	if (sci_logger.disk_threshold < 0)
		sci_logger.disk_threshold = buffer_size/2;

	sci_logger_assert_valid(__LINE__);
	return 0;
}




//  sci_logger_set_ASCII_preview (int channel)
//  User callable
//
//	This is a user callable routine.  It specifies which data channel is to
//  be returned for previewing in ASCII format.
//
void sci_logger_set_ASCII_preview (int channel)  {
	sci_logger_assert_valid(__LINE__);

	//  don't respond if the logger isn't open
	if (sci_logger.logger_open_flag == 0)
		return;
	
	//  negative channel number turns off preview
	if (channel < 0)  {
		sci_logger.ASCII_preview_chan = -1;
		return;
	}

	//  REVIEW - not yet enabled for 8 channel hardware; somewhat complicated
	//  because we will need to include the A/D address bits
	if (channel >= 4)
		return;
	sci_logger.ASCII_preview_chan = channel;
	sci_logger_assert_valid(__LINE__);
	return;
}	




//  sci_logger_set_ASCII_preview (int channel)
//  User callable
//
//	This is a user callable routine.  It specifies which data channel is to
//  be returned for previewing in binary format.
//
void sci_logger_set_binary_preview (int channel)  {
	sci_logger_assert_valid(__LINE__);

	//  don't respond if the logger isn't open
	if (sci_logger.logger_open_flag == 0)
		return;
	
	if (channel < 0)  {
		sci_logger.binary_preview_chan = -1;
		return;
	}

	//  REVIEW - not yet enabled for 8 channel hardware; somewhat complicated
	//  because we will need to include the A/D address bits
	if (channel >= 4)
		return;
	sci_logger.binary_preview_chan = channel;
	sci_logger_assert_valid(__LINE__);
	return;
}	




//  sci_logger_start_data (void)
//  User callable
//
//  This routine inits the A/D converter and starts the data flow.
//  Make sure that sci_logger_open() was called first.  This routine
//  is user callable
//
//	Returns: 0 if all is OK; negative on failure
//  Failures: Only if we cannot init the A/D.
//
void sci_logger_start_data (void)  {
	sci_logger_assert_valid(__LINE__);
	//  don't respond if the logger isn't open
	if (sci_logger.logger_open_flag == 0)
		return;

	//  verify that power is on and QSPI set up
	ASSERT (1);
	
	//  REVIEW
	//  delete this when debugging is complete
//	AD7716_critical_data_dump ();

	//  reset will empty the buffers and reset the actual A/D chip
	AD7716_start_data ();
	sci_logger.data_started = 1;
	sci_logger_assert_valid(__LINE__);
}




//  sci_logger_stop_data ()
//  User callable
//
//  Stops the data collection routines; resets the buffers; the next
//  start will not produce contiguous data with the current data.  The A/D
//  hardware does not actually stop; it can't.  Rather, we just stop listening
//  to it.  User callable.
//
void sci_logger_stop_data (void)  {
	int n;
	sci_logger_assert_valid(__LINE__);

	//  don't respond if the logger isn't open
	if (sci_logger.logger_open_flag == 0)
		return;

	//  first stop the low-level routines
	AD7716_stop_data ();
	
	//  now empty the local buffers for the science logger
//printf ("Stopdata empties %d buffers.\n", sci_logger.n_channels);	
	for (n = 0;  n < sci_logger.n_channels;  n++)
		sci_logger_buffer_empty (n);
//printf ("Done.\n");		
	
	sci_logger.data_started = 0;
	sci_logger_assert_valid(__LINE__);
	return;
}



//  sci_logger_pause_data ()
//  User callable
//
//  Used to temporarily pause the collection of data from the science A/D
//  when we expect to resume shortly.  The user should call sci_logger_resume()
//  as quickly as possible in order to insure that no data is lost.  This
//  routine is called when the user needs to usurp the QSPI interface for
//  use with other peripherals.  User callable.
//
void sci_logger_pause_data (void)  {
	sci_logger_assert_valid(__LINE__);

	//  REVIEW - suppose the logger is open, but the data isn't flowing

	//  don't respond if the logger isn't open
	if (sci_logger.logger_open_flag != 0)
		AD7716_pause_data ();
}




//  sci_logger_resume_data ()
//  User callable
//
//  Resumes data collection where it left off when sci_logger_pause was
//  called.  Does not check to insure that no data was lost.
//
void sci_logger_resume_data (void)  {
	//  Don't call the assert_valid routine at the start because we are
	//  in a hurry to restore the interrupt handler.
	//  Don't respond if the logger isn't open
	if (sci_logger.logger_open_flag != 0)
		AD7716_resume_data ();
	sci_logger_assert_valid(__LINE__);
}

			       


//  sci_logger_buffer_init (int chan_n, int buffer_n, long size, int trigger)
//  Internal use only
//
//  Initializes a buffer for a particular one of the available
//  science channels.  This includes allocating memory for the data
//  buffer and initing variables such as the STA and LTA.
//
//  Arguments: chan_n = the channel number to init; range >= 0 to < N_SCI_CHANNELS
//			   buffer_n = the buffer number to use for this channel
//	           size = the number of points to store in the buffer
//			   trigger: non-zero if we should calculate triggering for
//						this channel
//  Returns: 0 if OK, negative on failure
//  Failures: can fail only if the allocation of space fails
// 
int sci_logger_buffer_init (int chan_n, int buffer_n, long size, int trigger)  {
	ASSERT ((buffer_n >= 0) && (buffer_n < N_SCI_CHANNELS));
	ASSERT (sci_logger.channel[buffer_n].data == NULL);
	ASSERT (sci_logger.logger_open_flag == 1);
	ASSERT (chan_n >= 0);
	ASSERT (chan_n < N_SCI_CHANNELS);
	//  the assert valid routine will not pass here because we are in the
	//  middle of allocating the data structures, and the variables are not
	//  yet self-consistent

	#ifdef DEBUG
	printf ("channel %d turned on; buffer %d used\n", chan_n, buffer_n);
	#endif

	//  allocate space for the circular buffer
	#ifdef SHORT_SCIENCE_DATA
		sci_logger.channel[buffer_n].data = (short *)malloc (sizeof(short)*size);
	#endif
	#ifdef LONG_SCIENCE_DATA	
		sci_logger.channel[buffer_n].data = (long *)malloc (sizeof(long)*size);
	#endif	

//	printf ("sci_logger: just malloc'd 0x%lX\n", (long)sci_logger.channel[buffer_n].data);	
	if (sci_logger.channel[buffer_n].data == NULL)  {
		printf ("Apparent programming error: could not allocate memory.\n");
		log_printf ("sci_logger_buffer_init: ");
		log_printf ("Failed to allocate space for science channel %d\n", chan_n);
		return -1;
	}
	sci_logger.channel[buffer_n].n_data = size;
	//  REVIEW
	//  why do we have this half-assed init of some variables to default
	//  values.  if we keep, shouldn't we do this using the standard
	//  routines?
	sci_logger.channel[buffer_n].t_STA = DEFAULT_T_STA;
	sci_logger.channel[buffer_n].t_LTA = DEFAULT_T_LTA;
	sci_logger.channel[buffer_n].t_pre_event = 0;
	sci_logger.channel[buffer_n].t_post_event = 0;
	sci_logger_compute_n ();
	sci_logger.channel[buffer_n].channel_num = chan_n;	

	//  non-zero if we are triggering on this channel
	sci_logger.channel[buffer_n].trigger_chan = trigger;
	#ifdef DEBUG
	printf ("Channel %d is %striggered\n", chan_n, ((trigger != 0) ? "":"not "));
	#endif

//if (trigger == 0)
//	printf ("channel %d on buffer %d is NOT triggered.\n", chan_n, buffer_n);	
//else
//	printf ("triggering will be calculated for channel %d on buffer %d.\n", chan_n, buffer_n);	

	//  mark the buffer as empty and set default gain
	sci_logger_buffer_empty (buffer_n);
	sci_logger_set_gain (chan_n, DEFAULT_GAIN);
	
	//  The maximum number of points to write is choosen so that the total
	//  file length, including the 512 byte header, will equal 128 blocks of
	//  512 bytes each.  For a 2 GigByte disk, this is the minimum allocation
	//  unit.  This therefore should also be the minimum file length.

	//  REVIEW - we could probably change this to force the logger to save
	//  data in complete disk allocation units (clusters).
	return 0;
}


//  sci_logger_buffer_empty
//  Internal use only
//
//  Marks a buffer as being empty of data
//
void sci_logger_buffer_empty (int buffer_n)  {
	long n;

	//  REVIEW - are the following necessary?
	ASSERT (sci_logger.channel[buffer_n].data != NULL);
	ASSERT (sci_logger.logger_open_flag == 1);
	if (sci_logger.channel[buffer_n].data == NULL)
		return;
	if (sci_logger.logger_open_flag == 0)
		return;	

	sci_logger.channel[buffer_n].newest_data = -1;
	sci_logger.channel[buffer_n].STA = 0.0;
	sci_logger.channel[buffer_n].LTA = 0.0;
	sci_logger.n_collected = 0;

	sci_logger.channel[buffer_n].next_to_write = -1;
	sci_logger.channel[buffer_n].n_waiting_to_write = 0;
	sci_logger.channel[buffer_n].n_written = -1;
	
	sci_logger.channel[buffer_n].trigger_state = WAITING;
	sci_logger.channel[buffer_n].trigger_request = 0;

	//  Fill the buffer with the starting value
	for (n = 0;  n < sci_logger.channel[buffer_n].n_data; n++)
		sci_logger.channel[buffer_n].data[n] = 0x0;
}	




//  sci_logger_buffer_delete (int buffer_n)
//  Internal use only
//
//	Deletes all memory associated with all of the science buffers.
//  In debug mode, sets various variables to illegal values in the
//  hopes that we will catch the error if we later try to use this
//  memory without re-allocating it.  Internal use only.
//
void sci_logger_buffer_delete (void)  {
	int buffer_n;
	ASSERT (sci_logger.logger_open_flag == 1);

	//  We are careful to free memory in the reverse of the allocation
	//  order because of a possible bug in the Aztec memory routines
	for (buffer_n = N_SCI_CHANNELS-1;  buffer_n >= 0;  buffer_n--)  {
		if (sci_logger.channel[buffer_n].data == NULL)
			continue;
		#ifdef DEBUG
			sci_logger.channel[buffer_n].n_data = -2;
			sci_logger.channel[buffer_n].newest_data = -2;
			sci_logger.channel[buffer_n].STA = -1.0;
			sci_logger.channel[buffer_n].LTA = -1.0;
			sci_logger.channel[buffer_n].n_STA = -1;
			sci_logger.channel[buffer_n].n_LTA = -1;
			sci_logger.channel[buffer_n].channel_num = 0xCC;
			sci_logger.channel[buffer_n].trigger_chan = 0xCC;
			sci_logger.channel[buffer_n].next_to_write = -2;
			sci_logger.channel[buffer_n].n_waiting_to_write = -2;
			sci_logger.channel[buffer_n].n_written = -2;
		#endif
//		printf ("sci_logger: about to free 0x%lX\n", (long)sci_logger.channel[buffer_n].data);	
		ASSERT (sci_logger.channel[buffer_n].data != NULL);
		free (sci_logger.channel[buffer_n].data);
		sci_logger.channel[buffer_n].data = NULL;
	}
	sci_logger.n_channels = 0;
	sci_logger.current_buffer = 0;
	return;
}
			



/*  int sci_logger_collect (int max_to_collect)
 *  User callable
 *
 *  Transfers data out of the AD7716's SPI buffer and into the science
 *  data buffers.  This includes conversion of the data to a long and
 *	the calculation of the STA and LTA appropriate for this particular
 *  channel.  User callable
 *
 *	Arguments: max_to_collect is the maximum number of points to pull
 *				from the buffer before returning.  The routine may return
 *				after pulling fewer if there are not enough in the
 *				buffer. This allows for a sort of cooperative timesharing to
 *				prevent this routine from taking too much time in a single
 *				block.
 *	Returns: number of points read
 *  Failures: none
 *
*/
int sci_logger_collect (int max_to_collect)  {
	int n, buff;
	long value;
//	int trigger;

	sci_logger_assert_valid(__LINE__);
	//  don't respond if the logger isn't open
	if (sci_logger.logger_open_flag == 0)
		return 0;

	//  try to always collect data in sets as they were collected
	//  from the A/D converter
	max_to_collect *= sci_logger.n_channels;
	for (n = 0;  n < max_to_collect;  n++)  {
		//  returns 0 if data was available
		if (AD7716_get_data_point (&value) != 0)
			break;

		//  If we got a point, then add it to the appropriate buffer
		sci_logger_add_point (sci_logger.current_buffer, value);
		
		//  update the current buffer number
		sci_logger.current_buffer++;
		if (sci_logger.current_buffer < sci_logger.n_channels)
			continue;
			
		//  We ONLY get to this point each time we have been through all the
		//  active science channels once.
		//  Reset the current buffer counter, so additional points go to
		//  the correct buffers
		sci_logger.current_buffer = 0;

		//  It is difficult to properly calculate triggering when we are
		//  first turned on, because the algorithm we use for the STA and
		//  LTA give spurious results until we have at least a few times
		//  n_STA and n_LTA worth of point accumulated.  This doesn't
		//  really matter, except that it makes logger demonstrations look bad
		//  when the instrument shows false triggers.  So this code is really 
		//  here more for demos than real use.  It checks to see if we have
		//  accumulated more than a few seconds of data, and if so, it arbitrarily
		//  sets the long term average to the short term average.  This too can
		//  give spurious results, but it is the easiest solution, and the flaws
		//  in the approach aren't really important
		if (sci_logger.n_collected < 6*sci_logger.channel[0].n_STA)  {
			sci_logger.n_collected++;
			continue;
		}	

		if (sci_logger.n_collected == 6*sci_logger.channel[0].n_STA)  {
			sci_logger.n_collected++;
			for (buff = 0;  buff < sci_logger.n_channels;  buff++)  {
				sci_logger.channel[buff].LTA = sci_logger.channel[buff].STA;
				sci_logger.channel[buff].trigger_request = 0;
			}	
			continue;
		}		

		//  VERY IMPORTANT
		//  REVIEW
		//  When we complete the data collection on a particular channel, we must
		//  immediately stop data collection and return so that the main routines 
		//  can call the disk write routines and dump the data.  If we don't do this,
		//  then the next few points might retrigger the logger and overwrite the
		//	header data which may not have yet been flushed to disk

		//  now go through each active channel and see if any channel requested
		//  that it be triggered.  Adjust the data stream state as appropriate
		for (buff = 0;  buff < sci_logger.n_channels;  buff++)  {
			switch (sci_logger.channel[buff].trigger_state)  {
				case TRIGGERED:
					if (sci_logger.channel[buff].trigger_request == 0)
						sci_logger_trigger_off (sci_logger.channel[buff].channel_num);
					break;
				
				case WAITING:
					if (sci_logger.channel[buff].trigger_request)
						sci_logger_trigger_on (sci_logger.channel[buff].channel_num);
					break;	
			
				case POST_EVENT:
					if (sci_logger.channel[buff].trigger_request)
						sci_logger_trigger_on (sci_logger.channel[buff].channel_num);
					else  {
						if (sci_logger.channel[buff].n_post_event_left <= 0)  {
							#ifdef DEBUG
								printf ("Switched from post-event to waiting.\n");					
							#endif
							sci_logger.channel[buff].trigger_state = WAITING;
						}	
					}
					break;	
					
				default:
					#ifdef DEBUG
						printf ("Unknown value for trigger flag.\n");
						ASSERT (0);
					#endif
					sci_logger.channel[buff].trigger_state = WAITING;
			}
			//  reset the trigger request flag for the next loop through the buffer
			sci_logger.channel[buff].trigger_request = 0;
		}	
	}
	
	#ifdef DEBUG
	sci_logger_assert_valid(__LINE__);
	AD7716_assert_valid (-1);
	#endif	

	//  return the number of points processed		
	return n;
}




/*  sci_logger_add_point (int n, long value)
 *  Internal use only
 *
 *	Adds a single point to the requested science buffer, including the
 *	calculation of LTA and STA as apppropriate.  Internal use only
 *
 *  Arguments: n = the channel number to add this point to
 *			   value = the value to add to this channel
 *	Returns: none
 *  Failures: none
 *
*/
void sci_logger_add_point (int n, long value)  {
	int m, k;
	ASSERT (sci_logger.channel[n].data != NULL);
	ASSERT (sci_logger.logger_open_flag == 1);

	#ifdef DEBUG
//	if (sci_logger.channel[n].channel_num != AD7716_get_channel_num (value))  {
//		printf ("Impending assertion failure.\n");
//		printf ("'newest_data' = %ld\n", sci_logger.channel[n].newest_data);
//		printf ("channel buffer index = %d\n", n);
//		printf ("offending value = 0x%08lX\n", value);
//	}	
	#endif					

//	printf ("Latest value = 0x%08lX\n", value);
//	printf ("channel_num = 0x%04X\n", sci_logger.channel[n].channel_num);
//	printf ("value = 0x%08lX\n", value);
//	for (nn = 0;  nn < 8;  nn++)
//		printf ("SPIRCV[%d] = 0x%04X\n", nn, shit[nn]);
//	ASSERT (n == 0);
//	ASSERT (sci_logger.channel[0].channel_num == 0);
	
	//  verify that the channel number from the A/D matches the channel
	//  number of the software buffer.
	#ifdef DEBUG	
	if (sci_logger.channel[n].channel_num != AD7716_get_channel_num (value))  {
		printf ("buffer %d\n", n);
		printf ("data 0x%08lX\n", value);
		printf ("newest_data = %ld\n", sci_logger.channel[n].newest_data);
	} 
	ASSERT (sci_logger.channel[n].channel_num == AD7716_get_channel_num (value));
    #endif

	//  Compute the index of the next newest point.
	//  Wrap around when we reach the end of the buffer.
	sci_logger.channel[n].newest_data += 1;
	if (sci_logger.channel[n].newest_data >= sci_logger.channel[n].n_data)
		sci_logger.channel[n].newest_data = 0;
	#ifdef SHORT_SCIENCE_DATA
		//  data passed to this routine is 24 bit data in a 32 bit word
		value = (value >> 8);
		sci_logger.channel[n].data[sci_logger.channel[n].newest_data] = (short)value;
	#endif
	#ifdef LONG_SCIENCE_DATA	
		sci_logger.channel[n].data[sci_logger.channel[n].newest_data] = value;
	#endif	

	//  This allows us to call a subroutine which dumps data from a particular
	//  channel for the user to check out.
	if (sci_logger.channel[n].channel_num == sci_logger.ASCII_preview_chan)
		sci_logger_preview_ASCII (n);
	if (sci_logger.channel[n].channel_num == sci_logger.binary_preview_chan)  {
		//  In binary preview mode, always push a 16 bit number to the user, cause preview
		//  certainly doesn't need high resolution.  Treat long and short science data
		//  differently so that we get only the most signficant bits
		//
		//  Don't call a subroutine because speed is important
		#ifdef SHORT_SCIENCE_DATA
			SerPutByte ((char)((value >> 8)&0xFF));
			SerPutByte ((char)(value & 0xFF));
		#endif
		#ifdef LONG_SCIENCE_DATA
			SerPutByte ((char)((value >> 16)&0xFF));
			SerPutByte ((char)((value >> 8)&0xFF));
		#endif
//		sci_logger_preview_binary (n);
	}	

	//  if this channel is triggered, then we need to update the number of points
	//  waiting
	if (sci_logger.channel[n].trigger_state != WAITING)  {
		//  REVIEW - what is this????
		//  Not waiting, yet n_waiting... == 0?
		if (sci_logger.channel[n].n_waiting_to_write++ == 0)
			sci_logger.channel[n].next_to_write = sci_logger.channel[n].newest_data;
		
		//  if this channel is in the post event phase, then we decrement the
		//  number of points left to collect in post event
		if (sci_logger.channel[n].trigger_state == POST_EVENT)
			sci_logger.channel[n].n_post_event_left--;
	}	
	
	if (sci_logger.channel[n].trigger_level == 0)  {
		//  Should actually mark all other active channels as triggered
		//  as well, but lets modify the way it works in the interest
		//  of speed.  
		sci_logger.channel[n].trigger_request = 1;
	}  else  {	
		//  if this channel is one on which we are triggering, then calculate the STA and LTA
		if (sci_logger.channel[n].trigger_chan)  {
			sci_logger.channel[n].STA += (labs (value) - sci_logger.channel[n].STA) / 
															((float)sci_logger.channel[n].n_STA);
		
			sci_logger.channel[n].LTA += (labs (value) - sci_logger.channel[n].LTA) /
															((float)sci_logger.channel[n].n_LTA);
			ASSERT (sci_logger.channel[n].LTA >= 0.0);
			ASSERT (sci_logger.channel[n].STA >= 0.0);
			if (sci_logger.channel[n].STA >
				(sci_logger.channel[n].trigger_level*sci_logger.channel[n].LTA))  {
				//  this channel is showing a trigger.  Loop through all channels
				//  which it triggers, and mark them as requesting a trigger.  Later
				//  after we have been through the data from all the channels once,
				//  we will actually go through the channels and untrigger those which
				//  are not requesting a trigger.  Although this is complex, it allows
				//  triggering from multiple sources.
//				printf ("Trigger request on %d\n", n);			
				for (m = 0;  m < N_SCI_CHANNELS;  m++)  {
					if ((0x01 << m) & sci_logger.channel[n].chans_to_trigger)  {
						for (k = 0;  k < sci_logger.n_channels;  k++)  {
							if (sci_logger.channel[k].channel_num == m)  {
//								printf ("t %d\n", k);						
								sci_logger.channel[k].trigger_request = 1;
							}							
						}
					}
				}
			}
		}	
	}

	sci_logger_assert_valid(__LINE__);
	return;
}




//  sci_logger_preview_ASCII
//  Internal use only
//
//	Prints out an occasional value so the user can verify the operation
//  of the data logger.  Internal use only
//
void sci_logger_preview_ASCII (int n)  {
	static long preview_count = 0;
	long value;
	ASSERT (sci_logger.logger_open_flag == 1);
	
	if ( ((preview_count++)%100) == 0)  {
//		printf ("%ld: 0x%08lX\n", AD7716_get_icount(), value);
//		printf ("%ld: %ld\n", AD7716_get_icount(), value);
		value = sci_logger.channel[n].data[sci_logger.channel[n].newest_data];
		printf ("%ld: %f", AD7716_get_icount(), sci_logger_value_to_voltage (n, value));
		printf ("    STA: %f    LTA: %f\n",
					sci_logger_value_to_voltage (n, sci_logger.channel[n].STA),
						sci_logger_value_to_voltage (n, sci_logger.channel[n].LTA));
	}	
	return;
}		




//  sci_logger_preview_binary
//  Internal use only
//
//	Prints out data in binary for graphing by the user program.  This
//  obviously will not work with a standard terminal program.
//
void sci_logger_preview_binary (int n)  {
	long value;
	ASSERT (sci_logger.logger_open_flag == 1);

	value = sci_logger.channel[n].data[sci_logger.channel[n].newest_data];
	printf ("%c%c", (char)((value&0xFF00) >> 8), (char)(value&0xFF));
	fflush (stdout);
	return;
}		



float sci_logger_value_to_voltage (short n, long value)  {
	#ifdef SHORT_SCIENCE_DATA
		return (2.5F/sci_logger.channel[n].gain)*value/(float)0x8000;
	#endif
	#ifdef LONG_SCIENCE_DATA
		return (2.5F/sci_logger.channel[n].gain)*value/(float)0x800000;
	#endif
}		



//  sci_logger_trigger_on ()
//  Internal use only
//
//  This routine is called to force the data logger to begin recording data when
//  the trigger calculation routines determine that an interesting event has occured
//
//  The three phases are waiting for an event, triggered (saving data), and post event
//	(not triggered, but still saving additional data for a fixed length of time).
//
//	This routine should not be called if the logger is already triggered.
//  If this routine is called during the "post event" phase, then the new data will
//  	be added to the old as a single record.
//	If this routine is called during the waiting phase, then a block of data from the
//  	buffer is marked for saving, and additional data will be saved until the
//		sci_logger_trigger_off () routine is called.
//
void sci_logger_trigger_on (short channel)  {
	extern const float SOFTWARE_VERSION;
	double delta;
	int n;
	long temp;
	long delta_secs, delta_ticks;
	#ifdef DEBUG
	time_tt time_temp;
	#endif

	ASSERT (sci_logger.logger_open_flag == 1);
	sci_logger_assert_valid(__LINE__);

	if (sci_logger.logger_open_flag == 0)
		return;
		
	//  figure out which buffer corresponds to this channel number
	for (n = 0;  n < sci_logger.n_channels;  n++)  {
		if (sci_logger.channel[n].channel_num == channel)
			break;
	}
	#ifdef DEBUG
	printf ("Trigger_on (chan %d, buffer %d) called\n", channel, n);
	#endif
	ASSERT (n < sci_logger.n_channels);
	//  impossible event
//	if (n >= sci_logger.n_channels)
//		return;
	
//	printf ("buffer %d; STA = %f;  LTA = %f;  trigger_level = %f\n",
//			n, sci_logger.channel[n].STA, sci_logger.channel[n].LTA, sci_logger.channel[n].trigger_level);
//	printf ("newest data = %ld\n", sci_logger.channel[n].newest_data);
	
	//  don't call this routine if we are already triggered
	ASSERT (sci_logger.channel[n].trigger_state != TRIGGERED);
	//  impossible event
	if (sci_logger.channel[n].trigger_state == TRIGGERED)
		return;
	
	if (sci_logger.channel[n].trigger_state == POST_EVENT)  {
		//  we are in the post event collection phase, and have re-triggered
		//  Everything is still set up; we need only reset the trigger flag
		sci_logger.channel[n].trigger_state = TRIGGERED;
		return;
	}
	
	//  If we aren't triggered, and we aren't in the post event, then we must
	//  be waiting.  Each channel is set up individually for saving.
	ASSERT (sci_logger.channel[n].trigger_state == WAITING);
	sci_logger.channel[n].trigger_state = TRIGGERED;

	//  start by determining which data point in the buffer is the first one
	//  we should write.
//	printf ("n_pre_event for buffer %d is %ld\n", n, sci_logger.channel[n].n_pre_event);
	//  NOTE - if we trigger before we have accululated at least
	//  n_pre_event points in the buffer, then some of the data will be
	//  whatever was in the buffer when the space was allocated, or whatever
	//  we wrote into the buffer in the buffer_empty routine.
	temp = sci_logger.channel[n].newest_data - sci_logger.channel[n].n_pre_event;
	if (temp < 0)
		temp += sci_logger.channel[n].n_data;
	#ifdef DEBUG
	printf ("Newest data is %ld\n", sci_logger.channel[n].newest_data);
	printf ("n_pre_event is %ld\n", sci_logger.channel[n].n_pre_event);
	printf ("next_to_write is %ld\n", sci_logger.channel[n].next_to_write);
	#endif

	sci_logger.channel[n].next_to_write = temp;
	ASSERT (temp >= 0);
	ASSERT (temp < sci_logger.channel[n].n_data);
		
	sci_logger.channel[n].n_waiting_to_write = 1 + sci_logger.channel[n].n_pre_event;
	sci_logger.channel[n].n_written = 0;

	//  Compute the time of the first data point in the buffer.  Start by getting
	//  the time of the newest point in the data buffer
	//  Don't execute this sequence until the variable next_to_write is set
	sci_logger.channel[n].start_time = AD7716_get_data_point_time ();
	#ifdef DEBUG
	time_temp = logger_time_get_time ();
	printf ("Current time: %s", logger_time_get_string (&time_temp));
	printf ("Last data point time: %s", logger_time_get_string (&sci_logger.channel[n].start_time));
	#endif
	
//	printf ("sci_...header_time: start at %s",
//			logger_time_get_string (&sci_logger.channel[n].disk_header.start_time));

	delta = sci_logger.channel[n].newest_data - sci_logger.channel[n].next_to_write;
	if (delta < 0)
		delta += sci_logger.channel[n].n_data;
	#ifdef DEBUG
	printf ("Number of points waiting is %ld\n", (long)delta);		
	#endif	
	delta /= AD7716_get_conversion_rate ();
	delta_secs = (long)delta;
	delta_ticks = (long)(0.5F + ((delta-delta_secs) * GetTickRate ()));
	#ifdef DEBUG
	printf ("Delta time is %lf\n", delta);	
	printf ("Delta secs is %ld\n", delta_secs);	
	printf ("Delta ticks is %ld (= 0.%ld sec)\n", delta_ticks, delta_ticks*25);	
//	printf ("delta_secs = %ld\n", delta_secs);
//	printf ("delta_ticks = %ld\n", delta_ticks);	
	#endif
	sci_logger.channel[n].start_time.secs -= delta_secs;
	sci_logger.channel[n].start_time.ticks -= delta_ticks;
	if (sci_logger.channel[n].start_time.ticks < 0)  {
		sci_logger.channel[n].start_time.ticks += GetTickRate ();
		ASSERT (sci_logger.channel[n].start_time.ticks >= 0);
		sci_logger.channel[n].start_time.secs -= 1;
	}
	sci_logger.channel[n].next_write_time = sci_logger.channel[n].start_time;
	#ifdef DEBUG
	printf ("Next write time: %s", logger_time_get_string (&sci_logger.channel[n].next_write_time));
	#endif
	
//	printf ("sci_...header_time: %s",
//				logger_time_get_string (&sci_logger.channel[n].disk_header.start_time));

	//  zero the header
	memset (&sci_logger.channel[n].disk_header, 0,
			sizeof (sci_logger.channel[n].disk_header));
	strcpy (sci_logger.channel[n].disk_header.magicstring, "DATA");
	sprintf (sci_logger.channel[n].disk_header.totalhdrs, "1");
	sprintf (sci_logger.channel[n].disk_header.abbrev, "GEOSens");
	sprintf (sci_logger.channel[n].disk_header.title, "GEOSense Science Channel %d",
														sci_logger.channel[n].channel_num);
	//  The sampling period is now calculated and set just before we write
	//  this channel to disk and is based on the actual average sampling rate over
	//  the entire time that the data was being added to the buffer
//	sprintf (sci_logger.channel[n].disk_header.sampling_period, "%.8f",
//									(float)(1.0F/sci_logger_get_conversion_rate()));
//	ASSERT (sci_logger.channel[n].disk_header.sampling_period[sizeof(sci_logger.channel[n].disk_header.sampling_period)-1] == '\0');
	#ifdef SHORT_SCIENCE_DATA
		sprintf (sci_logger.channel[n].disk_header.samplebits, "16");
		sprintf (sci_logger.channel[n].disk_header.wordsize, "2");
    #endif
    #ifdef LONG_SCIENCE_DATA
		sprintf (sci_logger.channel[n].disk_header.samplebits, "24");
		sprintf (sci_logger.channel[n].disk_header.wordsize, "4");
	#endif	
	sci_logger.channel[n].disk_header.typemark = AMPLITUDE;
	sci_logger.channel[n].disk_header.swapping = UNSWAPPED;
	sci_logger.channel[n].disk_header.signing = SIGNED;
	sci_logger.channel[n].disk_header.caltype = CALIBRATED;
	sprintf (sci_logger.channel[n].disk_header.calmin, "%.8f",
								(-2.5/sci_logger.channel[n].gain));
	sprintf (sci_logger.channel[n].disk_header.calmax, "%.8f",
								(2.5/sci_logger.channel[n].gain));
	sprintf (sci_logger.channel[n].disk_header.calunits, "Volts");
	sprintf (sci_logger.channel[n].disk_header.recordsize, "512");
	sprintf (sci_logger.channel[n].disk_header.sourcevers, "%.4f", SOFTWARE_VERSION);
	
	//  Finally, convert the time which is stored as a time_tt varible into
	//  the MacTrimpi header format
	sci_logger_set_header_time (n);

	sci_logger_assert_valid(__LINE__);
}




//  sci_logger_set_header_time
//
//	Uses the time stored in the "next_write_time" variable to set
//  the MacTrimpi header variables which specify the time.
//
//	As noted in the routine AD7716_get_data_point_time (), this routine
//  introduces some inaccuracies caused by the uncertainty in the real
//  conversion rate.  Those inaccuracies increase as the length of
//  the pre-event length increases.
//
void sci_logger_set_header_time (int n)  {
	long delta_secs, delta_ticks, msec;
	double delta;
	time_tt p1_time;
	struct tm* tm;
	ASSERT (n >= 0);
	ASSERT (n <= N_SCI_CHANNELS);
		
	//  Now compute the time of the first point.  Note that our
	//  internal clock has 25 uSec resolution, but the header clock
	//  has only mSec resolution.  If necessary, round up to the
	//  next second.
	p1_time = sci_logger.channel[n].next_write_time;	
	msec = 0.5 + (1000L * p1_time.ticks / GetTickRate ());
	if (msec >= 1000)  {
		msec = 0;
		p1_time.ticks = 0;
		p1_time.secs += 1;
	}	

	//  convert time to the tm structure time
	tm = localtime (&(p1_time.secs));
	if (tm->tm_year < 70)
		tm->tm_year += 2000;
	else
		tm->tm_year += 1900;

	sprintf (sci_logger.channel[n].disk_header.year, "%d", tm->tm_year);
	sprintf (sci_logger.channel[n].disk_header.month, "%02d", 1+tm->tm_mon);
	sprintf (sci_logger.channel[n].disk_header.day, "%02d", tm->tm_mday);
	sprintf (sci_logger.channel[n].disk_header.year, "%d", tm->tm_year);
	sprintf (sci_logger.channel[n].disk_header.hours, "%02d", tm->tm_hour);
	sprintf (sci_logger.channel[n].disk_header.minutes, "%02d", tm->tm_min);
	sprintf (sci_logger.channel[n].disk_header.seconds, "%02d", tm->tm_sec);
	//  need to convert ticks to milliseconds
	sprintf (sci_logger.channel[n].disk_header.msec, "%03ld", msec);
	return;
}

	



//  sci_logger_trigger_off ()
//  Internal use only
//
//	See the description of the trigger algorithms at the trigger_on() routine.
//  When we turn the trigger off we first set it to POST_EVENT, and then
//	set a counter to the length of the desired post event data block.
//  This counter will be decremented each time we add a new point 
//  with the add_point() routine.  When the counter reaches zero, we mark
//  the trigger as off.
//
void sci_logger_trigger_off (short channel)  {
	int n;
	sci_logger_assert_valid(__LINE__);
	ASSERT (sci_logger.logger_open_flag == 1);
	//  impossible event
	if (sci_logger.logger_open_flag == 0)
		return;

	//  figure out which buffer corresponds to this channel number
	for (n = 0;  n < sci_logger.n_channels;  n++)  {
		if (sci_logger.channel[n].channel_num == channel)
			break;
	}
	ASSERT (n < sci_logger.n_channels);
	//  impossible event
	if (n >= sci_logger.n_channels)
		return;
	
//	printf ("Trigger_off (chan %d) called\n", channel);
//	printf ("buffer %d; STA = %f;  LTA = %f;  trigger_level = %f\n",
//		n, sci_logger.channel[n].STA, sci_logger.channel[n].LTA, sci_logger.channel[n].trigger_level);
	
	sci_logger.channel[n].n_post_event_left = sci_logger.channel[n].n_post_event;
//	printf ("  post event length is %ld points\n", sci_logger.channel[n].n_post_event_left);
	if (sci_logger.channel[n].n_post_event_left > 0)
		sci_logger.channel[n].trigger_state = POST_EVENT;
	else
		sci_logger.channel[n].trigger_state = WAITING;
	sci_logger_assert_valid(__LINE__);
}





//  Internal use only
void sci_logger_power_on (void)  {
	ASSERT (sci_logger.logger_open_flag == 1);
	sci_logger_assert_valid(__LINE__);
	if (sci_logger.logger_open_flag == 0)
		return;

	//  make sure all the I/O lines are configured so that we are not
	//  powering the ADC through any of the logic lines.  This is redundant
	//  because it was done at power up, but do it again to be sure
	#ifdef REVB
	PConfInp (F,2);     //  1211 data ready
	#endif
	PConfInp (F,4);		//  7716 data ready
	PConfOutp(E,0);		//  AD7716 TFS on the ADC module
	PClear(E,0);
	PConfOutp(E,1);		//  ADC Reset line on the ADC module
	PClear(E,1);

    //  Then apply power to the circuits
	AnalogPowerON ();
	SensorPowerON ();
	
	//  Then reconfigure the gain bits to the default state (low gain)
	PConfOutp (E, 3);
	PConfOutp (E, 5);
	PConfOutp (E, 6);
	PConfOutp (E, 7);
	PSet (E, 3);
	PSet (E, 5);
	PSet (E, 6);
	PSet (E, 7);
}



//  Internal use only
void sci_logger_power_off (void)  {
	ASSERT (sci_logger.logger_open_flag == 1);
	sci_logger_assert_valid(__LINE__);

	if (sci_logger.logger_open_flag == 0)
		return;

	//  make sure the pins connected to digital control lines on the science
	//  logger are pulled low so that we don't power this section through
	//  these pins.  The configuration as an output is redundant, but can't hurt.
	PConfOutp (E, 3);
	PConfOutp (E, 5);
	PConfOutp (E, 6);
	PConfOutp (E, 7);
	PClear (E, 3);
	PClear (E, 5);
	PClear (E, 6);
	PClear (E, 7);

	SensorPowerOFF ();
	AnalogPowerOFF ();
}




//  sci_logger_compute_n ()
//  Internal use only
//
//	Computes and stores all paramaters which depend on the conversion
//  rate.  Because of the way this routine is called it sometimes does
//  more computation then necessary, but it is only used when the user
//  is doing setup, so we don't really care.  Internal use only.
//
//	Errors: none returned, but...
//		We don't allow the pre-event length to get too large because we
//		don't want the internal buffer to overflow before we can write
//		the data to disk.  If it gets to long, we truncate the number,
//		but not the requested time.  This way, if the user later reduces
//		the conversion rate we can recompute the number and perhaps give
//		the requested length.  It is up to the user to call the self-test
//		routine before quitting to insure that all is copesetic.
//
int sci_logger_compute_n ()  {
	long max_n_pre_event;
	int n, ret;
	float DISK_DELAY;
	DISK_DELAY = 15.0;
	ASSERT (sci_logger.logger_open_flag == 1);
	
	ret = 0;				
	for (n = 0; n < sci_logger.n_channels; n++)  {
		//  REVIEW - rename the 15 second magic number
		//  compute a maximum allowed length of the pre_event data which by subtracting
		//  15 seconds
		max_n_pre_event = sci_logger.channel[n].n_data -
				DISK_DELAY*sci_logger_get_conversion_rate ();
		sci_logger.channel[n].n_pre_event =
			sci_logger.channel[n].t_pre_event * sci_logger_get_conversion_rate ();
		if (sci_logger.channel[n].n_pre_event > max_n_pre_event)  {
			sci_logger.channel[n].n_pre_event = max_n_pre_event;
			ret = -1;
		}
		//  post-event length can be as long as we want, since we don't store
		//  it in advance
		sci_logger.channel[n].n_post_event =
			sci_logger.channel[n].t_post_event * sci_logger_get_conversion_rate ();
		sci_logger.channel[n].n_STA =
			sci_logger.channel[n].t_STA * sci_logger_get_conversion_rate ();
		sci_logger.channel[n].n_LTA =
			sci_logger.channel[n].t_LTA  * sci_logger_get_conversion_rate ();
#ifdef DEBUG
//		printf ("Channel %d: n_data = %ld\n", n, sci_logger.channel[n].n_data);
//		printf (" max_n_pre_event = %ld\n", max_n_pre_event);
//		printf (" t_pre_event = %f -> n_pre_event = %ld\n",
//					sci_logger.channel[n].t_pre_event, sci_logger.channel[n].n_pre_event);
//		printf (" t_post_event = %f -> n_post_event = %ld\n",
//					sci_logger.channel[n].t_post_event, sci_logger.channel[n].n_post_event);
//		printf (" t_STA = %f -> n_STA = %ld\n",
//					sci_logger.channel[n].t_STA, sci_logger.channel[n].n_STA);
//		printf (" t_LTA = %f -> n_LTA = %ld\n",
//					sci_logger.channel[n].t_LTA, sci_logger.channel[n].n_LTA);
#endif										
	}

	return ret;
}	



	
//  sci_logger_set_pre_event (float t)
//  User callable
//
//  Sets the pre-event length in seconds.  The pre-event length 
//  in samples must be reset if we change the conversion rate.
//  User callable.
//
//	Arguments:
//		t = pre-event length in seconds
//  Returns: negative on failure
//	
int sci_logger_set_pre_event (float t)  {
	int n;
	ASSERT (t >= 0);
	sci_logger_assert_valid(__LINE__);

	if (sci_logger.logger_open_flag == 0)
		return 0;
	if (t < 0)
		return -1;
		
	for (n = 0; n < sci_logger.n_channels; n++)  {
		sci_logger.channel[n].t_pre_event = t;
	}
	//  the sci_logger_compute_n() routine recalculates all parameters
	//  which depend on both a time and the conversion rate, such as the
	//  n_pre_event which we changed here.
	sci_logger_assert_valid(__LINE__);
	return sci_logger_compute_n ();
}



//  sci_logger_set_post_event (float t)
//  User callable
//
//	Sets post event data logging length as we did pre-event; can return
//  non-zero only if a previous set_pre_event failed.
//
int sci_logger_set_post_event (float t)  {
	int n;
	ASSERT (t >= 0);
	sci_logger_assert_valid(__LINE__);

	if (sci_logger.logger_open_flag == 0)
		return 0;
	
	for (n = 0; n < sci_logger.n_channels; n++)  {
		sci_logger.channel[n].t_post_event = t;
	}
	sci_logger_assert_valid(__LINE__);
	return (sci_logger_compute_n ());
}



//  User callable
int sci_logger_set_STA_time (float t)  {
	int n;
	ASSERT (t >= 0);
	if (sci_logger.logger_open_flag == 0)
		return 0;
	if (t < 0)
		return -1;
	
	for (n = 0; n < sci_logger.n_channels; n++)  {
		sci_logger.channel[n].t_STA = t;
	}
	sci_logger_assert_valid(__LINE__);
	return sci_logger_compute_n ();
}



//  User callable
int sci_logger_set_LTA_time (float t)  {
	int n;
	ASSERT (t >= 0);
	sci_logger_assert_valid(__LINE__);

	if (sci_logger.logger_open_flag == 0)
		return 0;
	if (t < 0)
		return -1;
	
	for (n = 0; n < sci_logger.n_channels; n++)  {
		sci_logger.channel[n].t_LTA = t;
	}
	sci_logger_assert_valid(__LINE__);
	return sci_logger_compute_n ();
}





//  sci_logger_set_conversion_rate ()
//  User callable
//
//  Calls the AD7716 subroutines to set the basic conversion
//  rate used by the software.  This routine can be called even
//  when the logger is closed so that we can use the low level
//  routines to verify the validity of the requested rate
//
//	Argument: floating point number which is the requested rate
//	Returns: floating point number which is the conversion rate
//		closest to the requested rate
//	Failures: if the requested rate is not within 1% of a known rate
double sci_logger_set_conversion_rate (float rate)  {
	short restart;
	double true_rate;
	sci_logger_assert_valid(__LINE__);

	//  stop the data flow and empty buffers when we change rates.
	restart = sci_logger.data_started;
	sci_logger_stop_data ();

	//  Start by verifying that the conversion rate is legal and setting
	//  the conversion rate using the low-level routines
	true_rate = AD7716_set_conversion_rate (rate);
	
//	printf ("AD7716_set convert returns %f\n", true_rate);
	if (true_rate > 0)  {
		if (sci_logger.logger_open_flag != 0)  {
			//  many variables are passed from the user as times, but used internally
			//  as a number of points.  These variables must be adjusted whenever the
			//  conversion rate changes.
			sci_logger_compute_n ();
		}
	}	
	if (restart)
		sci_logger_start_data ();
	sci_logger_assert_valid(__LINE__);
	//  choose new clock rate to accomodate new data rate, if needed
	logger_set_clock_rate (-1);
	return true_rate;	
}




short sci_logger_set_averaging (short n_average)  {
	short restart;
	short result;

	//  stop the data flow and empty buffers when we change the
	//  effective conversion rate
	restart = sci_logger.data_started;
	sci_logger_stop_data ();

	//  try to change the averaging
	result = AD7716_set_averaging (n_average);

	//  choose new clock rate to accomodate new data rate, if needed
	logger_set_clock_rate (-1);
	
	//  restart the science data if appropriate
	if (restart)
		sci_logger_start_data ();
	sci_logger_assert_valid(__LINE__);
	return result;
}





//  sci_logger_get_conversion_rate ()
//  User callable
//
//	Returns the current conversion rate in samples/second.
//	
double sci_logger_get_conversion_rate (void)  {
	if (sci_logger.logger_open_flag == 0)
		return 0;

	return AD7716_get_conversion_rate ();
}




//  sci_logger_set_gain
//  User callable
//
//	Sets the gain applied to a particular channel.  Allowed values
//  are 2.5 and 25; the user must be within 1% of these values for
//  this routine to recognize them
//
int sci_logger_set_gain (int channel, float gain)  {
	float normal_gain;
	int gain_bit;
	int n;
	sci_logger_assert_valid(__LINE__);
	ASSERT (channel >= 0);
	ASSERT (channel < N_SCI_CHANNELS);
	
	//  make sure the requested gain is a known legal value
	normal_gain = gain / 2.50F;
	if ((normal_gain > 0.99)	&&
		(normal_gain < 1.01)   )
		gain_bit = 1;
	else  {
		if ((normal_gain > 9.9 )	&&
			(normal_gain < 10.1)   )
			gain_bit = 0;
		else
			//  unknown gain requested
			return -1;
	}

	//  if gain is OK, enter it into the setup table
	for (n = 0;  n < sci_logger.n_channels;  n++)  {
		if (sci_logger.channel[n].channel_num == channel)  {
			//  this buffer has the channel number of the channel whose
			//  gain we are setting
			sci_logger.channel[n].gain = gain;
		}
	}
	
	//  now that we have verified that the gain is legal, check to see
	//  if the logger is open.  Don't twiddle the hardware bits unless
	//  the logger is open.  When the logger is closed the power is off,
	//  and we want the output bits to be set low so that we don't accidently
	//  power circuitry through these logic lines
	if (sci_logger.logger_open_flag == 0)  {
		sci_logger_assert_valid(__LINE__);
		return 0;
	}	

	//  if logger is open, adjust the gain control lines immediately
	switch (channel)  {
		case 3:
			//  set the output port pin which controls the gain for this channel
			//  to reflect the value of the gain bit
			PConfOutp (E, 7);
			if (gain_bit)
				PSet (E, 7);
			else
				PClear (E, 7);
			break;

		case 2:
			PConfOutp (E, 6);
			if (gain_bit)
				PSet (E, 6);
			else
				PClear (E, 6);
			break;

		case 1:
			PConfOutp (E, 5);
			if (gain_bit)
				PSet (E, 5);
			else
				PClear (E, 5);
			break;

 		case 0:
			PConfOutp (E, 3);
			if (gain_bit)
				PSet (E, 3);
			else
				PClear (E, 3);
			break;

		default:
			//  don't pass bad channel numbers to this routine
			printf ("Error: Requested gain change on channel %d\n", channel);
			ASSERT (0);
			return -1;
	}
	
	sci_logger_assert_valid(__LINE__);
	return 0;
}


//  User callable
void sci_logger_set_trigger_level (short channel, float level)  {
	int n;
	sci_logger_assert_valid(__LINE__);

	if (sci_logger.logger_open_flag == 0)
		return;

	if (channel < 0)  {
		for (n = 0;  n < sci_logger.n_channels;  n++)
			sci_logger.channel[n].trigger_level = level;
	}  else  {
		for (n = 0;  n < sci_logger.n_channels;  n++)  {
			if (sci_logger.channel[n].channel_num == channel)
				sci_logger.channel[n].trigger_level = level;
		}		
	}			
	sci_logger_assert_valid(__LINE__);
}		





//  sci_logger_disk_request ()
//  User callable
//
//  Checks each data buffer to see if there is more than a certain amount of data
//  waiting to be dumped to the disk.  If any channel exceeds the threshold, returns
//  non-zero to request a disk access.
//
int sci_logger_disk_request (void)  {
	int n;
	sci_logger_assert_valid(__LINE__);
	
	if (sci_logger.logger_open_flag == 0)
		return 0;

	for (n = 0;  n < sci_logger.n_channels;  n++)  {
		//  if any one channel has lots of data waiting then we must request
		//  the disk be turned on
		if (sci_logger.channel[n].n_waiting_to_write > sci_logger.disk_threshold)  {
//			printf ("  sci logger has enough data waiting.\n");			
			return 1;
		}	

		//  if any channel is no longer triggered but has data waiting we must
		//  request a disk access immediately.  If we fail to do this, then we
		//  run the risk of overwriting the header information if this channel
		//  were to re-trigger before the data was written.  This may result
		//  in many quick disk spin-ups (and the associated power penalty) if
		//  a number of channels with different trigger levels or post-event
		//  lengths complete at unrelated times.
		if (sci_logger.channel[n].trigger_state == WAITING)  {
			if (sci_logger.channel[n].n_waiting_to_write > 0)  {
//				printf ("  sci logger waiting with data present\n");
				return 1;
			}	
		}
	}
	
	return 0;
}





//  sci_logger_write_disk ()
//  Uuser callable
//
//  This routine is called each time the disk is spun up so that the
//  science logger can have a chance to dump all available data to the
//  disk.  This routine may be called even when there is no data waiting.
//
int sci_logger_write_disk (void)  {
	int n;
	time_tt ltime;
	long dt;
	double period;
	
	sci_logger_assert_valid(__LINE__);
	#ifdef DEBUG
	printf ("sci_logger_write_disk(): starting\n");
	#endif
	
	if (sci_logger.logger_open_flag == 0)
		return 0;

	//  Start by noting the time of the most recently acquired data point
	//  in the buffers.  All buffers will have the same time.
	ltime = AD7716_get_data_point_time ();
	#ifdef DEBUG
	printf ("Most recent data point time: %s", logger_time_get_string (&ltime));	
	#endif
	
	for (n = 0;  n < sci_logger.n_channels;  n++)  {
		//  For each buffer, start by setting the end time if there is
		//  data waiting in this buffer.  Then calculate the actual period
		//  as based on all of the data waiting in the buffer.
		//  REVIEW
		//  This could result in an inaccurate period if we don't have enough
		//  data points in the buffer.  This could happen if some other system
		//  has triggered the disk write or if we had independently triggered
		//  science channels with differing numbers of points in them
		if (sci_logger.channel[n].n_waiting_to_write > 0)  {
			sci_logger.channel[n].end_time = ltime;
			
			//  calculate how many clock ticks elapsed from the start to the
			//  end of the data.
			dt = ttmcmp (sci_logger.channel[n].end_time, sci_logger.channel[n].start_time);
			sci_logger.channel[n].total_to_write = sci_logger.channel[n].newest_data -
													 sci_logger.channel[n].next_to_write + 1;
			if (sci_logger.channel[n].total_to_write < 0)
				sci_logger.channel[n].total_to_write += sci_logger.channel[n].n_data;
			ASSERT (sci_logger.channel[n].total_to_write == sci_logger.channel[n].n_waiting_to_write);	
			sci_logger.channel[n].total_written = 0;
			//  then the period as measured in clock ticks
			period = (double)dt / ((double)(sci_logger.channel[n].total_to_write - 1));
			//  and finally the period in seconds.			
			period /= GetTickRate ();
			sprintf (sci_logger.channel[n].disk_header.sampling_period, "%.11lf", period);

			#ifdef DEBUG
			printf ("\n\nBuffer %d has data.\n", n);		
			printf ("Start time: %s", logger_time_get_string (&sci_logger.channel[n].start_time));	
			printf ("Total elapsed ticks is %ld\n", (long)dt);			
			printf ("Total points to write is %ld\n", sci_logger.channel[n].total_to_write);
			printf ("Total buffer length is %ld\n", sci_logger.channel[n].n_data);
			printf ("period is %lf\n", period);			
			printf ("Sampling period is %s\n", sci_logger.channel[n].disk_header.sampling_period);
			#endif
		}								
	
		//  Then write out the data from the buffer.  The write_file
		//  routine can only write a single file.  If the data is too
		//  long to fit within a single file, then we must call it
		//  multiple times in order to dump all of the data.
		while (sci_logger.channel[n].n_waiting_to_write > 0)  {
			if (sci_logger_write_file (n) != 0)  {
				//  REVIEW
				//  Error writing to disk - how do we handle this????
				return -1;
			}
		}		
	}		
	//  Don't check for overflow here, but rather check in main.  By checking in
	//  main we can be sure that all the disk accesses are complete.  We can also
	//  read a few points before checking for the overflow (which is latched) thus
	//  insuring that the overflow doesn't occur between the time we check and the
	//  time we can next read points
//	sci_logger_overflow_check ();
	
	//  make sure we haven't screwed up the structures
	sci_logger_assert_valid(__LINE__);
	return 0;
}




void sci_logger_overflow_check (void)  {
	int n;
	time_tt ltime;
	long value;

	if (AD7716_check_for_overflow ())  {
		//  we took too long writing the data, and the AD7716 routines internal
		//  buffer overflowed, resulting in a loss of data.  The start time for
		//  each buffer point is therefore incorrect.  We must fix this.
		log_printf ("sci_logger_overflow_check: AD7716 buffer overflow.\n  data lost @");
		ltime = logger_time_get_time ();
		log_printf ("%s", logger_time_get_string (&ltime));
		printf ("The input data buffer overflowed and science data was lost.\n");
		ASSERT (sci_logger.current_buffer == 0);
		
		//  empty out the current buffer
		for (n = 0;  n < sci_logger.n_channels;  n++)  {
			#ifdef DEBUG
				printf ("Buffer %d had %ld points waiting.\n", n, sci_logger.channel[n].n_waiting_to_write);
			#endif			
			sci_logger_buffer_empty (n);
		}	
		
		for (n = 0;  n < sci_logger.n_channels;  n++)  {
			//  returns 0 if data was available
			//  This ASSERT is from an old version of the code in which this routine
			//  was called only after all the data had been written to disk.  In the
			//  current version we can call this at any time, and in fact we call it
			//  only after first reading a few points from the 7716.  By reading a few
			//  points first we insure that the overflow doesn't occur between the end
			//  of this routine and the next call to the read routines.  In this version
			//  we explicitly flush the buffer above.
			ASSERT (sci_logger.channel[n].n_waiting_to_write == 0);
			while (AD7716_get_data_point (&value) != 0)
				{;}
				
			//  Once we get a point, add it to the appropriate buffer
			sci_logger_add_point (sci_logger.current_buffer, value);
		
			//  update the current buffer number
			sci_logger.current_buffer++;
		}

		sci_logger.current_buffer = 0;
		ltime = AD7716_get_data_point_time ();
		for (n = 0;  n < sci_logger.n_channels;  n++)  {
			sci_logger.channel[n].start_time = ltime;
			sci_logger.channel[n].next_write_time = ltime;
			sci_logger.channel[n].n_written = 0;
			//  now that the next_write_time variable is correct, call the routine
			//  which updates the header variables.
			sci_logger_set_header_time (n);
		}	
		AD7716_clear_overflow ();
	}
}





char sci_logger_month_name[12][4] = {
	"JAN",
	"FEB",
	"MAR",
	"APR",
	"MAY",
	"JUN",
	"JUL",
	"AUG",
	"SEP",
	"OCT",
	"NOV",
	"DEC",
};	


void sci_logger_make_dir_name (char* buff, int channel, int year, int month, int day)  {
	ASSERT (month >= 1);
	ASSERT (month <= 12);
	ASSERT (day >= 1);
	ASSERT (day <= 31);
	ASSERT (year >= 1970);
	sprintf (buff, "\\S%d_%4d\\%s%d\\", channel, year, sci_logger_month_name[month-1], day);
	#ifdef DEBUG
		printf ("sci_logger_make_dir_name: dir name = %s\n", buff);	
	#endif	
	return;
}



//  sci_logger_write_file (int buffer)
//
//  Writes one file to disk from the specified buffer.  The maximum length
//  of a file is specifed by sci_logger.max_points_per_file, which as initialized
//  at startup.  If there are more points then this waiting to be written, then
//  this routine will not write all of the points to disk.  This routine should be
//  called repeatedly until the number of points waiting is 0
//
int sci_logger_write_file (int buffer)  {
	int fd, result, len;
	long n_to_write, max_to_write, last_point;
	long total_ticks, delta_ticks, delta_secs;
	char filename[64];
	int year, month, day, hour, min;
	double dtemp;

	#ifdef DEBUG
	printf ("\nsci_logger_write_file: Writing file from buffer %d\n", buffer);
	#endif
	
	//  Start by making file name, which is based on the date and time
	//  months are numbered from 0, so add one before making name
	year = atoi (sci_logger.channel[buffer].disk_header.year);
	month = atoi (sci_logger.channel[buffer].disk_header.month);
	day = atoi (sci_logger.channel[buffer].disk_header.day);
	hour = atoi (sci_logger.channel[buffer].disk_header.hours);
	min = atoi (sci_logger.channel[buffer].disk_header.minutes);
//	Original file naming convention:
//	sprintf (filename, "\\SCIENCE\\CHANNEL%d\\%4d\\%02d%02d%02d%02d.SCI", 
//					sci_logger.channel[buffer].channel_num,
//					year, month, day, hour, min);
//
//  New convention with improved tree structure which minimizes the number of
//  directories that the software must read and therefore maximizes the speed:
//	sprintf (filename, "\\S%d_%4d\\%s%d\\S%d_%02d%02d.SCI", 
//					sci_logger.channel[buffer].channel_num, year,
//					sci_logger_month_name[month-1], day,
//					sci_logger.channel[buffer].channel_num, hour, min);
//
//  Another change to separate the directory name from the file name so that we
//  can conveniently pre-create all necessary directories  
	sci_logger_make_dir_name (filename, sci_logger.channel[buffer].channel_num, year, month, day);
	len = strlen (filename);
	sprintf (&filename[len], "S%d_%02d%02d.SCI", sci_logger.channel[buffer].channel_num, hour, min);
	
	#ifdef DEBUG
	printf ("Filename %s\n", filename);
	#endif

	//  now open the file for writing
	//  REVIEW - we really ought to check to make sure the file doesn't
	//  yet exist.  (if the header needs writing).  Note that if the file
	//  does exist this code will tack the new data on to the end of the
	//  file, preventing data loss, but producing a screwed up format.
	//  REVIEW - we should verify that the disk power is on
	fd = logger_disk_open (filename, (PO_BINARY|PO_CREAT|PO_WRONLY), PS_IWRITE);
	if (fd < 0)  {
		log_printf ("sci_logger_write_file: file ('%s') open failure.\n", filename);
		log_printf ("  Error %d => %s.\n", get_errno (), get_errno_string(-1));
		return -1;
	}
	//  seek to the end of the file so we don't overwrite any previous data
	critical_error_reset ();
	if (po_lseek (fd, 0L, PSEEK_END) < 0)  {
		log_printf ("sci_logger_write_file: file ('%s') lseek failure.\n", filename);
		log_printf ("  Error %d => %s.\n", get_errno (), get_errno_string(-1));
		critical_error_reset ();
		po_close (fd);
		return -1;
	}
			
	if (sci_logger.channel[buffer].n_written == 0)  {
		//  if no points have yet been written to this file then
		//  write a header.
		#ifdef DEBUG
		printf ("Writing header to file.\n");		
		#endif
		ASSERT (sizeof (sci_logger.channel[buffer].disk_header) == 512);
		critical_error_reset ();
		result = po_write (fd, (byte*)&sci_logger.channel[buffer].disk_header, 
									sizeof (sci_logger.channel[buffer].disk_header));
		if (result != sizeof (sci_logger.channel[buffer].disk_header))  {
			//  error writing
			log_printf ("sci_logger_write_file: file ('%s') header write failure.\n", filename);
			log_printf ("  Tried to write %d, got %d\n",
					sizeof (sci_logger.channel[buffer].disk_header), result);
			log_printf ("  Error %d => %s.\n", get_errno (), get_errno_string(-1));
			critical_error_reset ();
			po_close (fd);
			return -1;
		}
	}
	critical_error_reset ();
	po_flush (fd);
		
//printf ("The newest point in the buffer is %ld\n", sci_logger.channel[buffer].newest_data);
//printf ("The buffer size is %ld\n", sci_logger.channel[buffer].n_data);
//printf ("The first point to write is %ld\n", sci_logger.channel[buffer].next_to_write);
//printf ("The number of points waiting is %ld\n", sci_logger.channel[buffer].n_waiting_to_write);
//printf ("The last point to write is therefore %ld\n", last_point);
//fflush (stdout);	

	for (;;)  {
		//  if no points to write, we're done.  Update time to indicate at next
		//  point
		if (sci_logger.channel[buffer].n_waiting_to_write <= 0)  {
			critical_error_reset ();
			po_flush (fd);
			critical_error_reset ();
			po_close   (fd);
			#ifdef DEBUG
			printf ("Wrote %ld points total to '%s'.\n",
					sci_logger.channel[buffer].n_written, filename);
	        #endif
			sci_logger.channel[buffer].next_write_time = sci_logger.channel[buffer].end_time;
			dtemp = ttmcmp (sci_logger.channel[buffer].end_time,
									sci_logger.channel[buffer].start_time);

			dtemp /= (double)(sci_logger.channel[buffer].total_to_write - 1);
			dtemp += 0.5;
			sci_logger.channel[buffer].next_write_time.ticks += (long)dtemp;
			if (sci_logger.channel[buffer].next_write_time.ticks >= GetTickRate ())  {
				sci_logger.channel[buffer].next_write_time.ticks -= GetTickRate ();
				sci_logger.channel[buffer].next_write_time.secs += 1;
			}
			sci_logger.channel[buffer].start_time = sci_logger.channel[buffer].next_write_time;
			#ifdef DEBUG
			printf ("New start time: %s", logger_time_get_string (&sci_logger.channel[buffer].next_write_time));
			printf ("\n");
			#endif
			return 0;
		}	
	
		//  start by calculating how many points we should write.  Start with
		//  the largest possible number, and reduce it as necessary
		n_to_write = sci_logger.channel[buffer].n_waiting_to_write;
		
		//  don't write more than the maximum number of points per file to this file
		max_to_write = sci_logger.max_points_per_file - sci_logger.channel[buffer].n_written;
		ASSERT (max_to_write > 0);
		if (n_to_write > max_to_write)
			n_to_write = max_to_write;
		
		//  Data to write must be contiguous.  Don't try to write so much data
		//  that we wrap around the end of the buffer
		last_point = sci_logger.channel[buffer].next_to_write + n_to_write;
		if (last_point >= sci_logger.channel[buffer].n_data)
			n_to_write = sci_logger.channel[buffer].n_data - sci_logger.channel[buffer].next_to_write;

		//  The number of bytes written must fit into an unsigned 16 bit integer.
		//  The maximum numbers listed here conveniently correspond to a 64k file
		//  length with a 512 byte header, as well as fitting into the unsigned
		//  16 bit integer size.
		#ifdef SHORT_SCIENCE_DATA
			if (n_to_write > 32512)
				n_to_write = 32512;
    	#endif
		#ifdef LONG_SCIENCE_DATA
			if (n_to_write > 16256)
				n_to_write = 16128;
    	#endif
    	#ifdef DEBUG
		printf ("About to write %ld point block to fd %d\n", n_to_write, fd);    	
		#endif

		if (sci_logger_write_buffer (fd, buffer, n_to_write) != 0)  {
			//  REVIEW - should think about error handling
			log_printf ("sci_logger_write_file: file ('%s') buffer write failure.\n", filename);
			log_printf ("  Tried to write %ld to fd %d, got %d\n", n_to_write, fd, result);
			log_printf ("  Error %d => %s.\n", get_errno (), get_errno_string(-1));
			critical_error_reset ();
			po_close (fd);
			return -1;
		}
		#ifdef DEBUG
			sci_logger_assert_file_size (filename, buffer);
		#endif	
			
		if (sci_logger.channel[buffer].n_written >= sci_logger.max_points_per_file)  {
			#ifdef DEBUG
			printf ("Wrote %ld points total to '%s'.\n",
					sci_logger.channel[buffer].n_written, filename);
			printf ("File length maxed out; updating time\n");			
	        #endif
			critical_error_reset ();
			po_close (fd);
			sci_logger.channel[buffer].n_written = 0;
			//  update time of next_to_write and the header time
			total_ticks = ttmcmp (sci_logger.channel[buffer].end_time,
									sci_logger.channel[buffer].start_time);

			dtemp = (double)sci_logger.channel[buffer].total_written;
			dtemp /= (double)(sci_logger.channel[buffer].total_to_write - 1);
			dtemp *= total_ticks;
			delta_ticks = (long)(0.5 + dtemp);
			
			delta_secs = delta_ticks/GetTickRate ();
			#ifdef DEBUG
			printf ("Total delta ticks (start to end of buffer) ticks %ld\n", total_ticks);									
			printf ("Total delta ticks (start to end of file) %ld\n", delta_ticks);											
			printf ("Delta secs %ld\n", delta_secs);			
			#endif
			delta_ticks -= (delta_secs*GetTickRate());
			ASSERT (delta_ticks >= 0);
			ASSERT (delta_ticks < GetTickRate ());
			sci_logger.channel[buffer].next_write_time = sci_logger.channel[buffer].start_time;
			#ifdef DEBUG
			printf ("Delta ticks is %ld (= 0.%ld sec)\n", delta_ticks, delta_ticks*25);	
			printf ("Orig start time: %s", logger_time_get_string (&sci_logger.channel[buffer].next_write_time));
			#endif
			sci_logger.channel[buffer].next_write_time.secs += delta_secs;
			sci_logger.channel[buffer].next_write_time.ticks += delta_ticks;
			if (sci_logger.channel[buffer].next_write_time.ticks >= GetTickRate ())  {
				sci_logger.channel[buffer].next_write_time.ticks -= GetTickRate ();
				sci_logger.channel[buffer].next_write_time.secs += 1;
			}
			#ifdef DEBUG
			printf ("New start time: %s", logger_time_get_string (&sci_logger.channel[buffer].next_write_time));
			printf ("\n");
			#endif
			//  now that the next_write_time variable is correct, call the routine
			//  which updates the header variables.
			sci_logger_set_header_time (buffer);
			return 0;
		}
	}	
	return 0;
}




int sci_logger_write_buffer (int fd, int n_buff, long n_points)  {
	long first_point;
	unsigned int n_bytes, result;

	ASSERT (sci_logger.logger_open_flag == 1);
	ASSERT (fd >= 0);
	ASSERT ((n_buff >= 0) && (n_buff < N_SCI_CHANNELS));
	
	//  for convenience only
	first_point = sci_logger.channel[n_buff].next_to_write;
	#ifdef DEBUG
	printf ("sci_logger_write_buffer: Writing %ld points starting from %ld.\n", n_points, first_point);	
	#endif
	ASSERT ((first_point >= 0) && (first_point < sci_logger.channel[n_buff].n_data));
	ASSERT ((first_point+n_points) <= sci_logger.channel[n_buff].n_data);

//  printf ("Writing %ld point block of data starting from %ld\n", n_to_write, first_point);
	#ifdef SHORT_SCIENCE_DATA
		ASSERT (n_points < 32767);
		n_bytes = n_points * sizeof (short);
   	#endif
	#ifdef LONG_SCIENCE_DATA
		ASSERT (n_points < 16383);
		n_bytes = n_points * sizeof (long);
   	#endif

	critical_error_reset ();
	result = po_write (fd, (byte*)&sci_logger.channel[n_buff].data[first_point], n_bytes);
	if (result != n_bytes)  {
		log_printf ("sci_logger_write_buffer: Error writing data to disk.\n");
		log_printf ("  Error: %s\n", get_errno_string(-1));
		log_printf ("  Tried %u, got %u\n", n_bytes, result);
		#ifdef DEBUG
		printf ("First point is %ld; address is 0x%08lX, n_bytes is 0x%04X\n",
						first_point,
						(long)&sci_logger.channel[n_buff].data[first_point],
						n_bytes);
		printf ("Start of buffer is at 0x%08lX\n", 
						(long)&sci_logger.channel[n_buff].data[0]);
		#endif
		critical_error_reset ();
		po_flush (fd);
		return -1;
	}
	critical_error_reset ();
	po_flush (fd);

	sci_logger.channel[n_buff].next_to_write += n_points;
	ASSERT (sci_logger.channel[n_buff].next_to_write <= sci_logger.channel[n_buff].n_data);
	if (sci_logger.channel[n_buff].next_to_write >= sci_logger.channel[n_buff].n_data)
		sci_logger.channel[n_buff].next_to_write = 0;
	sci_logger.channel[n_buff].n_written += n_points;
	sci_logger.channel[n_buff].total_written += n_points;
	sci_logger.channel[n_buff].n_waiting_to_write -= n_points;
	#ifdef DEBUG
	printf ("Updated next_to_write is %ld\n", sci_logger.channel[n_buff].next_to_write);
	printf ("n_written %ld\n", sci_logger.channel[n_buff].n_written);
	printf ("total_written %ld\n", sci_logger.channel[n_buff].total_written);
	printf ("n_waiting_to_write %ld\n", sci_logger.channel[n_buff].n_waiting_to_write);
	#endif

	ASSERT (sci_logger.channel[n_buff].n_waiting_to_write >= 0);
	//  REVIEW - why is this here?
	//  how does it line up with our need to track the next to write as we
	//  empty out the buffer on a continuously triggered channel?
	if (sci_logger.channel[n_buff].n_waiting_to_write <= 0)
		sci_logger.channel[n_buff].next_to_write = -1;
	return 0;
}



//  sci_logger_setup_partition (void)
//
//  This subroutine creates the directories that the science logger expects
//  to need to store data in on the current partition.  It is not strictly
//  necessary to call this routine, but it may improve performance by putting
//  the directory names at the start of the file structure.
//
void sci_logger_setup_partition (void)  {
	dword blocks_total, blocks_free;
	long blocks_per_day, n_days, n;
	struct tm* tp;
	time_tt now;
	int chan;
	char dirname[128];

	logger_disk_power_disk_on ();
	critical_error_reset ();
	pc_free(logger_disk_get_partition(), &blocks_total, &blocks_free);
	
	#ifdef SHORT_SCIENCE_DATA
		//               bytes/conversion * seconds/day * conversions/second divided by bytes/block
		blocks_per_day = 2.0 * 86400L * sci_logger_get_conversion_rate () * sci_logger_get_n_channels () / 512.0;
	#endif	
	#ifdef LONG_SCIENCE_DATA
		blocks_per_day = 4.0 * 86400L * sci_logger_get_conversion_rate() * sci_logger_get_n_channels () / 512.0;
	#endif
	
	if (blocks_per_day == 0)  {
		printf ("sci_logger_setup_partition: Unexpected failure to calculate disk requirements\n");
		logger_disk_power_disk_off ();
		return;
	}		
	
	n_days = blocks_free / blocks_per_day;
	n_days += 2;
	printf ("There is room on this disk for approximately %ld days data\n", n_days);
	
	now = logger_time_get_time ();
	//  anything greater than 1000 just not reasonable
	ASSERT (n_days < 1000);
	for (n = 0;  n < n_days;  n++)  {
//		printf ("day %ld\n", n);		
		for (chan = 0;  chan < sci_logger_get_n_channels();  chan++)  {
//			printf ("  chan %d\n", chan);		
			tp = localtime (&now.secs);
			if (tp->tm_year < 70)
				tp->tm_year += 2000;
			else
				tp->tm_year += 1900;
			sci_logger_make_dir_name (dirname, sci_logger.channel[chan].channel_num,
												 tp->tm_year, 1+tp->tm_mon, tp->tm_mday);
			critical_error_reset ();
			if (pc_isdir(dirname) == FALSE)  {
				printf ("Making directory '%s'\n", dirname);			
				strcat (dirname, "fname");
				logger_disk_make_dir (dirname);
			}	
		}
		//  add one day to the current time and create next batch of directories
		now.secs += 86400L;
	}
	printf ("All necessary directories created\n");
}




#ifdef DEBUG
void sci_logger_assert_file_size (char* fname, int buffer)  {
	long temp;
	STAT pstat;
	#ifdef SHORT_SCIENCE_DATA
		const short word_length = sizeof (short);
	#endif
	#ifdef LONG_SCIENCE_DATA
		const short word_length = sizeof (long);
	#endif
	
	critical_error_reset ();
	if (pc_stat(fname, &pstat) != 0)  {
		printf ("Couldn't stat '%s': %s\n", fname, get_errno_string (-1));
		return;
	}
	temp = pstat.st_size - sizeof (sci_logger.channel[buffer].disk_header);
	if ((temp%word_length) != 0)  {
		printf ("File '%s':\n", fname);
		printf ("  Length is %ld (not a multiple of data length!)\n", pstat.st_size);
		printf ("  Number of points written is %ld\n",
							sci_logger.channel[buffer].n_written);
	}
	temp /= word_length;
	if (temp != sci_logger.channel[buffer].n_written)  {
		printf ("Critical error!\n");
		printf ("File '%s':\n", fname);
		printf ("  Length of file is %ld\n", pstat.st_size);
		printf ("  But number of points written is %ld\n",
							sci_logger.channel[buffer].n_written);
	}						
}
#endif		



//  sci_logger_print_file
//
//  prints out a science file in an ASCII format
//
int sci_logger_print_file (int fd)  {
	return MacTrimpi_print_file (fd);
}	




//  sci_logger_self_test (void)
//
//  Tests as much as possible of the data logger, including the
//  following:
//		1) should perform all internal consistency checks of the
//				assert_valid routine, but should produce an error
//				message instead of a program abort in the event of
//				a failure.
//		2) should verify that the specified gain is matched by the
//				hardware setup
//		3) should verify that the AD7716 is responding - i.e., that it
//				interrupts and gives us data which at least has the
//				channel numbers correct
//
//
int sci_logger_self_test (void)  {
	int n;
	long temp;
	
	//  make sure the logger is open
	if (sci_logger.logger_open_flag == 0)  {
		printf ("The science logger is suspended.\n");
		return -1;
//		return SCI_LOGGER_NOT_OPEN;
	}
		

	//  if we get here, then we are open.  start by testing the variables
	//  in the general science logger struct
	if (sci_logger.n_channels <= 0)  {
		printf ("There are no active channels.\n");
  		return -2;
//		return SCI_LOGGER_NO_ACTIVE_CHANS;
	}

	//  then test the variables in the individual science buffer structs
	for (n = 0;  n < sci_logger.n_channels;  n++)  {
		temp = sci_logger.channel[n].t_pre_event * sci_logger_get_conversion_rate ();
		if (sci_logger.channel[n].n_pre_event != temp)  {
			printf ("The pre-event length is too long to fit the buffer.\n");
			return -3;
//			return SCI_LOGGER_PRE_EVENT;
		}
	}
	return 0;
}		



int sci_logger_get_n_channels (void)  {
	return sci_logger.n_channels;
}



void sci_logger_show_status (void)  {
	int n;

	if (sci_logger_is_open() == 0)
		return;
		
	printf ("There are %ld points waiting.\n", AD7716_get_n_waiting ());
	printf ("The conversion rate is");
	fflush (stdout);
	printf (" %.9lf\n", sci_logger_get_conversion_rate ());
	printf ("Each channel has a buffer length of %ld points.\n", sci_logger.channel[0].n_data); 
	for (n = 0;  n < sci_logger.n_channels;  n++)  {
		printf ("Channel %d has %ld points waiting for storage to disk\n",
										n, sci_logger.channel[n].n_waiting_to_write);
	}
}	

	


#ifdef DEBUG
void sci_logger_dump_data (void)  {	
	int n;

	printf ("\nsci_logger.n_channels = %d\n", sci_logger.n_channels);
	printf ("sci_logger.current_buffer = %d\n", sci_logger.current_buffer);
	printf ("sci_logger.ASCII_preview_chan = %d\n", sci_logger.ASCII_preview_chan);
	printf ("sci_logger.binary_preview_chan = %d\n", sci_logger.binary_preview_chan);
	printf ("sci_logger.disk_threshold = %ld\n", sci_logger.disk_threshold);
	printf ("sci_logger.max_points_per_file = %ld\n", sci_logger.max_points_per_file);
	printf ("sci_logger.logger_open_flag = %d\n", sci_logger.logger_open_flag);
	printf ("sci_logger.n_collected = %ld\n", sci_logger.n_collected);

	for (n = 0;  n < N_SCI_CHANNELS;  n++)  {
		if (sci_logger.channel[n].data == NULL)  {
			printf ("Buffer %d is not in use.\n", n);
			continue;
		}
		
		printf ("Buffer %d is active\n", n);
		printf ("  n_data = %ld\n", sci_logger.channel[n].n_data);
		printf ("  newest_data = %ld\n", sci_logger.channel[n].newest_data);
		printf ("  gain = %f\n", sci_logger.channel[n].gain);
		printf ("  channel_num = %d\n", sci_logger.channel[n].channel_num);
		printf ("  t_pre_event = %f\n", sci_logger.channel[n].t_pre_event);
		printf ("  n_pre_event = %ld\n", sci_logger.channel[n].n_pre_event);
		printf ("  t_post_event = %f\n", sci_logger.channel[n].t_post_event);
		printf ("  n_post_event = %ld\n", sci_logger.channel[n].n_post_event);
		printf ("  t_STA = %f\n", sci_logger.channel[n].t_STA);
		printf ("  n_STA = %ld\n", sci_logger.channel[n].n_STA);
		printf ("  t_LTA = %f\n", sci_logger.channel[n].t_LTA);
		printf ("  n_LTA = %ld\n", sci_logger.channel[n].n_LTA);
		printf ("  STA = %f\n", sci_logger.channel[n].STA);
		printf ("  LTA = %f\n", sci_logger.channel[n].LTA);
		printf ("  trigger_chan = 0x%04X\n", sci_logger.channel[n].trigger_chan);
		printf ("  chans_to_trigger = 0x%04X\n", sci_logger.channel[n].chans_to_trigger);
		printf ("  trigger_level = %f\n", sci_logger.channel[n].trigger_level);
		printf ("  trigger_state = %d\n", sci_logger.channel[n].trigger_state);
		printf ("  trigger_request = %d\n", sci_logger.channel[n].trigger_request);
		printf ("  n_post_event_left = %ld\n", sci_logger.channel[n].n_post_event_left);
		printf ("  n_waiting_to_write = %ld\n", sci_logger.channel[n].n_waiting_to_write);
		printf ("  n_written = %ld\n", sci_logger.channel[n].n_written);
		printf ("  next_to_write = %ld\n", sci_logger.channel[n].next_to_write);
	}
	return;
}	
#endif
