/* tm3get11.c   -- TASS Mark 3 data collection program version 1.1 */



/* Collect tonight's images from a TASS Mark III camera.
 *
 * This program and its user's guide are Copyright (C) 1996 EcoSystematica inc.
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 *
 * You will find a copy of the GNU General Public License at the end of
 * the user's guide (tm3get11.htm).
 *
 * TASS is The Amateur Sky Survey project started by Tom Droege, who also
 * wrote the first TASS software in Qbasic.
 * The TASS Mark III camera is Tom's design and working nicely, thank you!
 * The TASS mailing list is at tass@wwa.com, and to join in the fun, just
 * send a message at tass-request@wwa.com with the single word "subscribe"
 * as the subject line.  Easy, no?
 * The WEB site is maintained by Michael "Stupendous Man" Richmond at his site
 * http://www.astro.princeton.edu/~richmond/tass.html
 * 
 *      Norman W. Molhant   21/08/1996   (molhant@ere.umontreal.ca)
 *
 * This program is supposed to be ANSI-C, except where indicated otherwise. :-)
 *
 * Please send feedback (bug reports, features wanted, etc.) to Norman Molhant
 * at  molhant@ere.umontreal.ca  or post your feedback to the TASS mail list
 * at  tass@wwa.com  so we will all rejoice in its reading.
 */



#include <errno.h>
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <ctype.h>
#include <time.h>
#include <math.h>
#include <mem.h>
#include <signal.h>



/* for DOS uncomment the next line - comment it for LINUX */
#undef _LINUX_
/* for LINUX uncomment the next line - comment if for DOS */
/* #define _LINUX_ 1     */



/* hardware constants -- TASS control card ports offsets */

#define PathSetup       0
#define DataIn          0
#define AuxLoad         1
#define ReadyIn         1
#define DACsetup        2
#define AuxIn           2
#define DACload         3
#define SelData         3

/* hardware constants -- TASS control card DAC chip */

#define ClrAddr         0
#define DAChiTEC        0x070
#define DACloTEC        0x048
#define DAChiVCO        0x0B3
#define DACloVCO        0x08B

/* hardware constants -- TASS control card commands */

#define ResetReady      1     /* 0 lobyte + 1 resetReady */
#define GetLoByte       3     /* 0 lobyte + 3 setReady */
#define ShiftStart      4     /* 4 hibyte + 0 rowshift&startADC */
#define StartOnly       6     /* 4 hibyte + 2 startADC */

/* hardware constants -- ADC input multiplexer */

#define TEC             0x000
#define Therm2          0x008
#define Therm1          0x010
#define Minus5          0x018
#define CCD1            0x020
#define Plus5           0x028
#define CCD0            0x030
#define CCD2            0x038

/* hardware constants -- TASS control card status */

#define TestBit         128

/* FITS format constants: 2880 bytes = 36 records of 80 chars */

#define FITSBLOC        2880
#define CARDLEN         80

/* command table constants */

#define SETui   1
#define SETr    2
#define SETs    3
#define SETc    4
#define SETp    5
#define SETv    6
#define SETl    7


/* pseudo-type definitions to keep LINT quiet about useless conversions */

#define sint     signed short   /* ASSUMING a short is a 16 bits integer */
#define uint   unsigned short   /* ASSUMING a short is a 16 bits integer */
#define byte   unsigned char    /* ASSUMING a char is an  8 bits integer */


/* actual type definitions */

typedef uint histogram[8192];           /* histogram for 13 bit values */
typedef byte FitsBuff[FITSBLOC];        /* buffer for FITS file */
typedef FitsBuff *FitsBufP;             /* pointer thereto */
typedef byte BitLine[100];              /* a line into a bit plane */
typedef char str24[24];                 /* up to 23 characters */
typedef char str80[80];                 /* up to 79 characters */
typedef long vector[9];                 /* a vector of 9 longs */
typedef vector matrix[5];               /* a matrix of 5 vectors */
typedef char *texte;                    /* a C string */

typedef uint uPixels[800];      /* buffer for 768 pixels + shim */
typedef uPixels *uPiP;          /* pointer to said buffer */
typedef long lPixels[768];      /* area for 768 long pixels */
typedef sint sPixels[768];      /* buffer for 768 signed pixels */
typedef sPixels *sPiP;          /* pointer to said buffer */

typedef struct tm structime;    /* formatted date & time record */
typedef structime *timeptr;     /* points to a formatted date & time record */

typedef struct { uint cur;      /* how many pixel rows loaded */
                 uint don;      /* how many pixel rows processed */
                 sPiP row[5];   /* up to 5 rows of signed pixels */
               } history;

typedef struct { texte name;    /* config variable name */
                 uint how;      /* what to do with it */
                 void *here;    /* where to do so */
               } commands;

typedef struct { texte name;    /* FITS header line name */
                 uint how;      /* what to do with it */
                 void *here;    /* where to do so */
                 texte remark;  /* and a facultative comment */
               } FITScmd;

typedef struct { uint ourset;   /* file set number - 0 when not in use */
                 uint lines;    /* number of CCD lines written */
                 FILE *fil[3];     /* one file per CCD */
                 uint bufndx[3];       /* one index into each FITS buffer */
                 FitsBufP buffer[3];   /* one FITS file buffer per CCD */
               } fileset;

typedef struct { uint  n;       /* number of lines */
                 str80 s[7];    /* up to 7 lines */
               } CCDtext;



#ifdef _LINUX_
#else
#include "tass_dos.1"
#endif



/* global tables, DOS & LINUX */

static byte CCD[3] = { CCD0, CCD1, CCD2 };      /* addresses for CCDs */

static texte direction[5] = {   /* the 5 directions in a bottom PSF */
  "   leftwards  ",
  "   down left  ",
  "   vertically ",
  "   down right ",
  "   rightwards "
};

/* global variables */

static uint OffLine[3];         /* first display line for each CCD */
static uint LinShown[3];        /* # lines displayed for each CCD */
static uint PixLine[3];         /* current displayed line for each CCD */

static FitsBuff bufferh;        /* byte buffer for FITS header */
static uint bufndxh;            /* index into bufferh */

static histogram *histo;        /* histogram large enough for 13 bit values */
static lPixels *sums;           /* smoothed sky background */
static uPiP pixlin[3];          /* one line of pixels per CCD */

/* static FILE *prn;               -- DEBUG - printing */
#ifdef __WATCOMC__
static int WATbug;              /* to avoid a bug of the WATCOM compiler */
#endif
static FILE *LOG;               /* log file */
static fileset Fset[2];         /* two sets of files */
static uint curset;             /* current rank of file sets */
static uint oldset;             /* previous rank of file sets */

static uint totline;            /* current pixel line */
static uint Num8;               /* number of displayed pixel bytes */

static uint temp1;              /* coolant temperature */
static uint temp2;              /* CCD temperature */
static uint icool;              /* TEC cooling current */
static uint pvolt;              /* positive supply voltage */
static uint nvolt;              /* negative supply voltage */

static uint median;             /* approximate median pixel value */
static uint lomost;             /* lowest pixel value divided by 8 */

static char todate[12];         /* today DD-MM-YYYY */
static char totime[10];         /* now HH:MM:SS */
static time_t now;              /* current UT in seconds */
static structime local;         /* formatted local time */
static structime utime;         /* formatted universal time */
static double lst;              /* local sidereal time */
static double lstinc;           /* increment of local sidereal time per line */

static sint DACflag;            /* 1 when DACs must be updated, 0 otherwise */
static sint status;             /* program status */
static sint Dying;              /* Dying > 0 means Die () has been called */
static sint TVBvar;             /* interaction flag: 0 TEC, 1 VCO */
static uint uivar;              /* interaction variable */
static char Display;            /* A, E, M, W */
static char Focussing;          /* 1 when focussing, 0 otherwise */
static history PixNow;          /* last 5 rows of signed pixels */
static matrix autocorr;         /* autocorrelation matrix */
static matrix accucorr;         /* accumulated autocorrelation matrix */

static uint currow;             /* current character row */
static uint curlin;             /* top line in current character row */
static uint curcol;             /* current character column */

static str24 title;             /* what is going on right now */
static str24 fname;             /* current file name */

/* constants from config file */

static uint linesperCCD;        /* total # lines to be collected per CCD */
static uint linesperfil;        /* # of lines per raw file */
static uint linesoverlap;       /* # of lines overlapping 2 files */
static uint VCOsetting;         /* VCO value to get the right scan speed */
static uint TECsetting;         /* TEC value to get the right temperature */
static uint FirstShown;         /* first pixel to be shown: 0..ChipPixels-1 */
static uint TotalShown;         /* number of pixels shown: 0..ChipPixels */
static uint ChipLines;          /* # of lines per CCD chip */
static uint ChipPixels;         /* # of pixels per CCD line */
static uint MaxPixels;          /* by definition > ChipPixels + 5 */
static uint TASSports;          /* IO address of TASS Control Card ports */
static uint Reversed;           /* 1 for negative CCD images, otherwise 0 */
static uint DontStop;           /* 1 for keep displaying, 0 to return to DOS */
static double TECresistor;      /* TEC current measuring resistor in ohms */
static double lineTime;         /* CCD line shift period in seconds */
static double exposure;         /* total exposure duration in seconds */
static double latitude;         /* latitude of camera site in degrees */
static double longitude;        /* longitude of camera site in degrees */
static double tLiqMax;          /* maximum temperature for liquid coolant */
static str24 telescope;         /* telescope identification */
static sint CCDexists[3];       /* for each CCD, 1 if it exists, 0 otherwise */
static double CCDdeclin[3];     /* for each CCD, its declination in degrees */
static double CCDoffRA[3];      /* for each CCD, its right ascension offset */
                                /*               in degrees */
static CCDtext CCDfits[3];      /* for each CCD, up to 7 FITS header lines */


/* config file interpretation table */

static commands choice[35] =            /* all config variables */
{
  {"SHIFTIME", SETr,  & lineTime},      /* line shift period in secs */
  {"EXPOSURE", SETr,  & exposure},      /* exposure duration in secs */
  {"TELESCOP", SETs,  telescope},       /* telescope identification */
  {"TOTLINES", SETui, & linesperCCD},   /* # lines to be collected per CCD */
  {"FILLINES", SETui, & linesperfil},   /* # lines per raw file */
  {"OVRLINES", SETui, & linesoverlap},  /* # lines overlapping 2 files */
  {"VCOVALUE", SETui, & VCOsetting},    /* VCO value for correct scan speed */
  {"TECVALUE", SETui, & TECsetting},    /* TEC value for correct temperature */
  {"TECRESIS", SETr, & TECresistor},    /* TEC current resistor in ohms */
  {"1STSHOWN", SETui, & FirstShown},    /* 1st pixel to be shown */
  {"TOTSHOWN", SETui, & TotalShown},    /* # pixels shown */
  {"CCDLINES", SETui, & ChipLines},     /* # lines per CCD chip */
  {"CCDPIXEL", SETui, & ChipPixels},    /* # pixels per CCD line */
  {"MAXPIXEL", SETui, & MaxPixels},     /* must be > ChipPixels + 5 */
  {"TASSPORT", SETui, & TASSports},     /* IO address of TASS Control Card */
  {"LATITUDE", SETr,  & latitude},      /* latitude of camera site in deg. */
  {"LONGITUD", SETr,  & longitude},     /* longitude of camera site in deg. */
  {"COOLMAXT", SETr,  & tLiqMax},       /* coolant max. temp. in Celsius */
  {"REVERSED", SETui, & Reversed},      /* 1 for negative CCD images, else 0 */
  {"CCD0HERE", SETui, & CCDexists[0]},  /* 1 if CCD0 exists, 0 otherwise */
  {"CCD0DECL", SETr,  & CCDdeclin[0]},  /* declination of CCD0 */
  {"CCD0RAOF", SETr,  & CCDoffRA[0]},   /* right ascension offset of CCD0 */
  {"CCD0TEXT", SETl,  & CCDfits[0]},    /* FITS header lines for CCD0 */
  {"CCD1HERE", SETui, & CCDexists[1]},  /* 1 if CCD1 exists, 0 otherwise */
  {"CCD1DECL", SETr,  & CCDdeclin[1]},  /* declination of CCD1 */
  {"CCD1RAOF", SETr,  & CCDoffRA[1]},   /* right ascension offset of CCD1 */
  {"CCD1TEXT", SETl,  & CCDfits[1]},    /* FITS header lines for CCD1 */
  {"CCD2HERE", SETui, & CCDexists[2]},  /* 1 if CCD2 exists, 0 otherwise */
  {"CCD2DECL", SETr,  & CCDdeclin[2]},  /* declination of CCD2 */
  {"CCD2RAOF", SETr,  & CCDoffRA[2]},   /* right ascension offset of CCD2 */
  {"CCD2TEXT", SETl,  & CCDfits[2]},    /* FITS header lines for CCD2 */
  {"KEEPSHOW", SETui, & DontStop},      /* end-of-run behaviour */
#ifdef _LINUX_
  {"SETCOLOR", SETp, NULL},             /* one of 16 colors on the monitor */
  {"SETVIDEO", SETv, NULL},             /* video mode & specs */
#else
  {"SETCOLOR", SETp,  & G16Pal[17]},    /* one of 16 colors on the monitor */
  {"SETVIDEO", SETv,  & Video},         /* video mode & specs */
#endif
  {0, 0, 0}
};


/* FITS header creation table */

static FITScmd header[29] =               /* all FITS header lines */
{
  {"SIMPLE",   SETc,  "T",            0}, /* 00: SIMPLE must be T */
  {"BITPIX",   SETc,  "16",           0}, /* 01: 16 bits per pixel */
  {"NAXIS",    SETc,  "2",            0}, /* 02: 2 axes */
  {"NAXIS1",   SETui, & MaxPixels,    "pixels per raw line"},
  {"NAXIS2",   SETui, & linesperfil,  "lines per raw file"},
  {"CTYPE1",   SETs,  "DEC",          "declination"},
  {"CRPIX1",   SETc,  "1.0",          0}, /* 06: 1st pixel is reference */
  {"CRVAL1",   SETr,  & CCDdeclin[0], "start DEC in degrees"},
  {"CDELT1",   SETc,  "0.0038197",    "DEC per pixel in degrees"},
  {"CROTA1",   SETc,  "0.0",          "no rotation"},
  {"CTYPE2",   SETs,  "RA",           "right ascension"},
  {"CRPIX2",   SETc,  "1.0",          0}, /* 11: 1st line is reference */
  {"CRVAL2",   SETr,  & CCDoffRA[0],  "start RA in degrees"},
  {"CDELT2",   SETc,  "0.0038197",    "RA per pixel in degrees"},
  {"CROTA2",   SETc,  "0.0",          "no rotation"},
  {"BSCALE",   SETc,  "1.0",          "true_pixel = fits_pixel * BSCALE"},
  {"BZERO",    SETc,  "32768.0",      "           + BZERO"},
  {"DATE-OBS", SETs,  todate,         "UT start date \'DD-MM-YYYY\'"},
  {"TIME",     SETs,  totime,         "UT start time \'hh:mm:ss\'"},
  {"TELESCOP", SETs,  telescope,      0}, /* 19: telescope id */
  {"INSTRUME", SETs,  "CCD0",         0}, /* 20: camera id */
  {"LATITUDE", SETr,  & latitude,     "positive degrees are north"},
  {"LONGITUD", SETr,  & longitude,    "positive degrees are west"},
  {"EXPOSURE", SETr,  & exposure,     "exposure in seconds"},    /* 23 */
  {"SHIFTIME", SETr,  & lineTime,     "line shift period in seconds"},
  {"COMMENT",  SETl,  "pixels 0-9 dark 10-13 blind 14-781 image 782-791 dark", 0},
  {"COMMENT",  SETl,  "pixels 792-793 blind 794-799 other (see tm3get11.htm)", 0},
  {"HISTORY",  SETl,  "data collected by tm3get11 (DOS) from a Kodak KAF400", 0},
  {0, 0, 0, 0} /* 28 */
};

#define XIS2line        04      /* header line for number of CCD lines */
#define RASCline        12      /* header line for right ascension degrees */
#define DATEline        17      /* header line for date DD-MM-YYYY */
#define TIMEline        18      /* header line for time hh:mm:ss */
#define INSTline        20      /* header line for instrument name 'CCDi' */
#define ENDFline        28      /* header line for END statement */


/* prototypes */

void setDisplayArea (char which);       /* specify where to display what */
void Die (texte s1, texte s2);          /* let's die a violent death */
void ProcessKey (sint Key, sint CRok);  /* process a key */
void setDACs (void);                    /* set the DACs */



#ifdef _LINUX_
#else
#include "tass_dos.2"
#endif



/* all the following should be ANSI C */

void SayString (texte s)         /* display a string */
{
  while (*s) SayChar (*(s++));
}


void SayNumber (long n)          /* display a number */
{
  str24 buf;

  sprintf (buf, "%ld", n);
  SayString (buf);
}


void SayLong (long n)            /* display a number in pseudo-float format */
{
  sint j, s;
  str24 buf;

  if (n < 0) { s = 1; n = -n; } else s = 0;
  sprintf (buf, "  %3ld", n);
  if (s)
    {
      j = 4;
      while (buf[j] != ' ') j--;
      buf[j] = '-';
    }
  SayString (buf);
}


void SayReal (double d, sint s)  /* display a real number */
{
  str24 buf;

  sprintf (buf, "%.*f", s, d);
  SayString (buf);
}


void SayHour (double h)          /* display hour & minutes */
{
  str24 buf;
  sint ih, im, is;

  ih = (sint) h;
  h -= (double) ih;
  h *= 60.0;
  im = (sint) h;
  h -= (double) im;
  h *= 60.0;
  is = (sint) h;
  sprintf (buf, "%02d:%02d:%02d", ih, im, is);
  SayString (buf);
}


void setDisplayArea (char which)        /* specify where to display what */
{
  uint height;
  uint first;
  uint index;
  
Redo:
  first = 48;           /* space for 3 lines of text @ 16 rows per line */
  Focussing = 0;        /* stop focussing when changing screens */
  PixNow.cur = 0;       /* empty history */
  PixNow.don = 0;
  Display = which;

  for (index = first; index < Video.verlin; index++) ClearVideoLine (index, 0);

  if (which == 'A')     /* all 3 cameras together */
    {
      fname[1] = 'x';
      height = (Video.verlin - first) / 3;

      for (index = 0; index < 3; index++)   /* for all 3 cameras */
        {
          OffLine[index] = first;           /* first display line */
          LinShown[index] = height;         /* display height in lines */
          PixLine[index] = 0;               /* start with display line 0 */
          first += height;                  /* for next camera */
        }
    }
  else                  /* only one camera */
    {
      for (index = 0; index < 3; index++)   /* for all 3 cameras */
        {
          OffLine[index] = Video.verlin;    /* first display line */
          LinShown[index] = 0;              /* display 0 lines */
          PixLine[index] = 0;               /* start with display line 0 */
        }

      height = Video.verlin - first;

      if (which == 'E')      index = 2;     /* CCD2 is Eastern */
      else if (which == 'M') index = 1;     /* CCD1 is Middle  */
      else if (which == 'W') index = 0;     /* CCD0 is Western */
      else return;

      if (!CCDexists[index])
        {
          which = 'A';
          goto Redo;
        }

      fname[1] = '0' + index;

      OffLine[index] = first;       /* first display line */
      LinShown[index] = height;     /* display height in lines */
    }
}


void ToggleFocus (void) /* set focussing ON or OFF */
{
  uint i, j;
  uint skip;            /* area set aside for the autocorrelation matrix */
  uint index;           /* which CCD are we speaking about? */
  
  if (Display == 'E')      index = 2;  /* CCD2 is Eastern */
  else if (Display == 'M') index = 1;  /* CCD1 is Middle  */
  else if (Display == 'W') index = 0;  /* CCD0 is Western */
  else return;
  
  skip = 80;            /* 5 lines of text @ 16 pixel rows per line */

  if (Focussing)
    {
      Focussing = 0;
      OffLine[index] -= skip;       /* first display line */
      LinShown[index] += skip;      /* display height in lines */
      PixLine[index] = 0;           /* restart with display line 0 */
    }
  else
    {
      Focussing = 1;
      j = OffLine [index];          /* first line to clear */
      for (i = 0; i < skip; i++) ClearVideoLine (i + j, 0);
      OffLine[index] += skip;       /* first display line */
      LinShown[index] -= skip;      /* display height in lines */
      PixLine[index] = 0;           /* restart with display line 0 */
    }

  PixNow.cur = 0;               /* empty history */
  PixNow.don = 0;
  for (j = 0; j < 5; j++)       /* clean autocorrelation matrix */
    for (i = 0; i < 9; i++)
      accucorr[j][i] = 0;
}


void TimeStamp (texte t)
{
  timeptr tim;

  now = time (NULL);
  tim = localtime (& now);
  memcpy (& local, tim, sizeof (structime));
  local.tm_year += 1900;
  local.tm_mon += 1;            /* localtime () returns month in 0..11 */
  tim = gmtime (& now);
  memcpy (& utime, tim, sizeof (structime));
  utime.tm_year += 1900;
  utime.tm_mon += 1;            /* gmtime () returns month in 0..11 */
  if (LOG)
    fprintf (LOG, "%s  LT: %04d-%02d-%02d %02d:%02d:%02d  UT: %04d-%02d-%02d %02d:%02d:%02d\n",
                  t,
                  local.tm_year, local.tm_mon, local.tm_mday,
                  local.tm_hour, local.tm_min, local.tm_sec,
                  utime.tm_year, utime.tm_mon, utime.tm_mday,
                  utime.tm_hour, utime.tm_min, utime.tm_sec);
/* fprintf (prn, "TimeStamp %s\n", t);  -- DEBUG */
}


void initialize (void)  /* initialize global variables */
{
  uint i;
  double v;
  str24 buf;

  linesperCCD = 0;      /* don't collect no image lines */
  linesperfil = 0;      /* so each file will be empty */
  linesoverlap = 0;     /* and they won't overlap */
  VCOsetting = 10250;   /* about right for 0.916 secs per line */
  TECsetting = 1050;    /* just a guess for temperature control */
  ChipLines = 1;        /* at least a linear array */
  ChipPixels = 794;     /* Kodak KAF400 has 794 pixels par line */
  MaxPixels = 800;      /* at least ChipPixels + 5 */
  TASSports = 0x0300;   /* default TASS Control Card base address */
  FirstShown = 15;      /* show pixels starting with #15 */
  TotalShown = 640;     /* show only 640 pixels */
  Reversed = 0;         /* 1 for negative CCD images, otherwise 0 */
  DontStop = 0;         /* 1 to keep displaying, 0 to return to DOS */
  TECresistor = 0.12;   /* TEC current measuring resistor in ohms */
  lineTime = 0.916;     /* CCD line shift period in seconds at the equator */
  exposure = 469.0;     /* total exposure duration in seconds */
  latitude = 40.0;      /* latitude of camera site in degrees, south < 0 */
  longitude = 80.0;     /* longitude of camera site in degrees, east < 0 */
  tLiqMax = 37.0;       /* maximum temperature for liquid coolant */
  lst = 0.0;            /* local sidereal time */
  lstinc = 0.0;         /* increment in sidereal time per line */
  strcpy (fname, "(x) no file");                /* current output file */
  strcpy (telescope, "TASS Mark III #0");       /* telescope identification */
  strcpy (todate, "DD-MM-YYYY");                /* today's date */
  strcpy (totime, "hh:mm:ss");                  /* now's hour */
  CCDexists[0] = 0;             /* default: no eastern CCD chip */
  CCDexists[1] = 1;             /* default: central CCD chip only */
  CCDexists[2] = 0;             /* default: no western CCD chip */
  CCDdeclin[0] = -1.466765;     /* point 1.466765 degrees south of equator */
  CCDdeclin[1] = -1.466765;     /* point 1.466765 degrees south of equator */
  CCDdeclin[2] = -1.466765;     /* point 1.466765 degrees south of equator */
  CCDoffRA[0] = -14.0222;       /* point 14.0222 degrees east of due south */
  CCDoffRA[1] =   0.9778;       /* point  0.9778 degrees west of due south */
  CCDoffRA[2] =  15.9778;       /* point 15.9778 degrees west of due south */
  CCDfits[0].n = 0;             /* no supplementary FITS header lines */
  CCDfits[1].n = 0;             /* no supplementary FITS header lines */
  CCDfits[2].n = 0;             /* no supplementary FITS header lines */

  Fset[0].ourset = 0;   /* file set 1 is unused */
  Fset[1].ourset = 0;   /* and so is set 2 */
  bufndxh = 0;          /* bufferh is empty */
  status = 0;           /* program status */
  Dying = 0;            /* Dying > 0 when Die () has been called */
  DACflag = 0;          /* no need to setup DACs */
  TVBvar = 0;           /* interaction flag: choice is TEC */
  uivar = 0;            /* interaction variable: value 0 */

  currow = 0;           /* current character row */
  curlin = 0;           /* top line in current character row */
  curcol = 0;           /* current character column */
  Focussing = 0;        /* not yet in focussing mode */
  PixNow.cur = 0;       /* empty history */
  PixNow.don = 0;
  for (i = 0; i < 3; i++) Fset[0].buffer[i] = (FitsBufP) malloc (sizeof (FitsBuff));
  for (i = 0; i < 3; i++) Fset[1].buffer[i] = (FitsBufP) malloc (sizeof (FitsBuff));
  for (i = 0; i < 3; i++) pixlin[i] = (uPiP) malloc (sizeof (uPixels));
  for (i = 0; i < 5; i++) PixNow.row[i] = (sPiP) malloc (sizeof (sPixels));
  histo = (histogram *) malloc (sizeof (histogram));
  sums = (lPixels *) malloc (sizeof (lPixels));

#ifdef _LINUX_
#else
  afont = 0;            /* address of our 8 by 16 font */
  initVideoMode (0x12, 640, 480);
  CRTC = 0x03D4;
#endif

  setDisplayArea ('A'); /* display mode = all 3 CCDs together */

  LOG = NULL;
  tzset ();             /* set date & time */
  TimeStamp ("initial");
  sprintf (buf, "%04d%02d%02d.tm3",             /* build a file name */
                local.tm_year,
                local.tm_mon,
                local.tm_mday);
/* prn = fopen ("prn", "w"); -- DEBUG */
#ifdef __WATCOMC__
  WATbug = 1;                                   /* to avoid a WATCOM bug */
#endif
  LOG = fopen (buf, "at");                      /* open text file for append */
  v = ((double) now) - 813196800.0;             /* seconds since JD 2450000.5 */
  v /= 86400.0;                                 /* days since JD 2450000.5 */
  v += 2450000.5;                               /* Julian Date */
  fprintf (LOG, "run \"%s\"  JD: %.3f\n",
                buf,
                v);

  while (KeyPressed ()) (void) GetKey ();       /* clear keyboard */
}


void AuLoup (uint n, fileset *that)
{
/* fprintf (prn, "AuLoup %u CCDexists %d %d %d\n", n, CCDexists[0], CCDexists[1], CCDexists[2]); -- DEBUG */
  if (! LOG) return;
  fprintf (LOG, "---- AuLoup %u, that %p, curset %u, oldset %u ----\n",
                n,
                that,
                curset,
                oldset);
  fprintf (LOG, " CCDexists %d %d %d\n",
                CCDexists[0],
                CCDexists[1],
                CCDexists[2]);
  fprintf (LOG, " Fset [0] @ %p, ourset %u, lines %u\n",
                &(Fset[0].ourset),
                Fset[0].ourset,
                Fset[0].lines);
  fprintf (LOG, " fil %p, %p, %p\n",
                Fset[0].fil[0],
                Fset[0].fil[1],
                Fset[0].fil[2]);
  fprintf (LOG, " bufndx %u, %u, %u\n",
                Fset[0].bufndx[0],
                Fset[0].bufndx[1],
                Fset[0].bufndx[2]);
  fprintf (LOG, " buffer %p, %p, %p\n",
                Fset[0].buffer[0],
                Fset[0].buffer[1],
                Fset[0].buffer[2]);
  fprintf (LOG, " Fset [1] @ %p, ourset %u, lines %u\n",
                &(Fset[1].ourset),
                Fset[1].ourset,
                Fset[1].lines);
  fprintf (LOG, " fil %p, %p, %p\n",
                Fset[1].fil[0],
                Fset[1].fil[1],
                Fset[1].fil[2]);
  fprintf (LOG, " bufndx %u, %u, %u\n",
                Fset[1].bufndx[0],
                Fset[1].bufndx[1],
                Fset[1].bufndx[2]);
  fprintf (LOG, " buffer %p, %p, %p\n",
                Fset[1].buffer[0],
                Fset[1].buffer[1],
                Fset[1].buffer[2]);
  if (that)
    {
      fprintf (LOG, " that %p, ourset %u, lines %u\n",
                that,
                that->ourset,
                that->lines);
      fprintf (LOG, " fil %p, %p, %p\n",
                that->fil[0],
                that->fil[1],
                that->fil[2]);
      fprintf (LOG, " bufndx %u, %u, %u\n",
                that->bufndx[0],
                that->bufndx[1],
                that->bufndx[2]);
      fprintf (LOG, " buffer %p, %p, %p\n",
                that->buffer[0],
                that->buffer[1],
                that->buffer[2]);
    }
  fprintf (LOG, "\n");
}


void setDateTime (void)         /* set date & time in the header */
{                               /* now is the value returned by time () */
  sint j;
  str24 r;
  byte *t;
  double d, u, v;

  /* JD 2450000.5 is midnight on october 9th, 1995 */

  v = ((double) now) - 813196800.0;     /* seconds since JD 2450000.5 */
  v /= 86400.0;                         /* days since JD 2450000.5 */
  v += 2450000.5;                       /* Julian Date */
  d = floor (v + 0.5) - 0.5;
  u = (d - 2451545.0) / 36525.0;        /* number of centuries till 2000 */

  lst = 6.697374558 + ((2400.051336 + (0.000025862 * u)) * u);

  while (lst > 24.0) lst -= 24.0;
  while (lst < 0.0) lst += 24.0;

  lst += (v - d) * 24.0 * 1.002737909;  /* add fractional part of date */

  while (lst > 24.0) lst -= 24.0;
  while (lst < 0.0) lst += 24.0;

  lst *= 15;                            /* Greenwich sideral time in degrees */
  lst -= longitude;                     /* longitude in degrees, west > 0 */ 

  while (lst > 360.0) lst -= 360.0;
  while (lst < 0.0) lst += 360.0;

  /* now, lst is the local sideral time or RA of middle camera, in degrees */

  sprintf (r, "\'%02d-%02d-%04d\' ",
              utime.tm_mday,
              utime.tm_mon,
              utime.tm_year);
  j = (DATEline * CARDLEN) + 10;        /* header line for date 'DD-MM-YYYY' */
  t = (byte *) r;
  while (*t) bufferh[j++] = *(t++);     /* copy formatted date */

  sprintf (r, "\'%02d:%02d:%02d\' ",
              utime.tm_hour,
              utime.tm_min,
              utime.tm_sec);
  j = (TIMEline * CARDLEN) + 10;        /* header line for time 'HH:MM:SS' */
  t = (byte *) r;
  while (*t) bufferh[j++] = *(t++);     /* copy formatted time */
}


void getconfig (texte rcname)   /* read a config file */
{
  FILE *config;
  sint n, m;
  uint i, r, h;
  uint *ui;
  double *d;
  texte t;
  byte *c;
  CCDtext *f;
  uint cr, cg, cb;
  char line[128];
  char s[33];

  config = fopen (rcname, "rb");
  if (config == 0) Die ("config file not found", rcname); 
  
  while ((t = fgets (line, 128, config)) != 0)
    {
      if (line[0] == 0) continue;       /* skip empty lines */
      if (line[0] == 13) continue;      /* skip empty lines */
      if (line[0] == '#') continue;     /* skip comment lines */
      n = sscanf (line, "%[0123456789"
                          "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
                          "abcdefghijklmnopqrstuvwxyz]", s);      /* get id */
      if (n != 1) continue;             /* skip if no id */
      r = strlen (s);
      i = 0;
      while (choice[i].name)
        {
          if (! strcmpi (choice[i].name, s))    /* token found */
            {
              if ((h = choice[i].how) == SETui) /* set unsigned integer */
                {
                  ui = (uint *) (choice[i].here);
                  n = sscanf (& (line[r]), "%*[ =]%i", ui); /* get value */
                }
              else if (h == SETr)               /* set real number */
                {
                  d = (double *) (choice[i].here);
                  n = sscanf (& (line[r]), "%*[ =]%lf", d); /* get value */
                }
              else if (h == SETs)               /* set character string */
                {
                  t = (texte) (choice[i].here);
                  n = sscanf (& (line[r]), "%*[ =\'\"]%[^\'\"]", t); /* get value */
                }
              else if (h == SETp)               /* set palette color */
                {
#ifdef _LINUX_
#else
                  c = (byte *) (choice[i].here);
                  n = sscanf (& (line[r]), "%*[ ]%i%*[ =]%i%*[ ,]%i%*[ ,]%i",
                              & m, & cr, & cg, & cb);
                  c += m * 3;
                  *(c++) = 63 & cr;  *(c++) = 63 & cg;  *c = 63 & cb;
#endif
                }
              else if (h == SETv)               /* set video mode */
                {
#ifdef _LINUX_
#else
                  n = sscanf (& (line[r]), "%*[ ]%i%*[ =]%i%*[ ,]%i%*[ ,]%i",
                              & Video.mode, & Video.horpix,
                              & Video.verlin, & Video.colors);
                  if ((Video.mode == 0) ||
                      (Video.horpix < 640) || (Video.horpix > 800) ||
                      (Video.verlin < 480) || (Video.verlin > 600) ||
                      (Video.colors != 16))
                    {
                      initVideoMode (0x12, 640, 480);  /* when bad mode */
                    }
#endif
                }
              else if (h == SETl)               /* new FITS header lines */
                {
                  f = (CCDtext *) (choice[i].here);
                  n = sscanf (& (line[r]), "%*[ =]%i", & m);  /* # lines */
                  if (m > 7) f->n = 7; else f->n = m;
                  n = 0;
                  while (n < m)
                    {
                      t = fgets (line, 128, config);
                      if (t == 0) break;
                      r = 0;
                      while (r < 79)
                        {
                          if (line[r] < ' ') line[r] = 0;
                          r++;
                        }
                      line[79] = 0;
                      if (n < 7) strcpy (f->s[n], line);
                      n++;
                    }
                }
              break;
            }
          else i++;
        }
    }

  (void) fclose (config);

  Num8 = FirstShown + TotalShown;
  if (Num8 > ChipPixels) Num8 = ChipPixels;
  Num8 -= FirstShown;
  Num8 >>= 3;

  TimeStamp ("begin");
  setDateTime ();                  /* find sidereal time */
  lstinc = lineTime * 0.004178075; /* 0.004178075 degrees per second */

  if (TECresistor < 0.01) TECresistor = 0.01;

  fprintf (LOG, "telescope \"%s\"  latitude: %.3f  longitude: %.3f\n",
                telescope,
                latitude,
                longitude);
  n = (60 * (local.tm_hour - utime.tm_hour)) + (local.tm_min - utime.tm_min);
  if (n > 720) n -= 1440;          /* + 12 h to - 12 h */
  fprintf (LOG, "minutes (local time - universal time): %d  dst: %d\n",
                n,
                local.tm_isdst);
  for (i = 0; i < 3; i++)
    if (CCDexists[i])
      {
        fprintf (LOG, "camera CCD%d  RA offset: %.3f  Dec: %.3f\n",
                    i,
                    CCDoffRA[i],
                    CCDdeclin[i]);
        CCDdeclin[i] -= 1.466765;  /* 1st pixel points half a CCD southwards */
      }
}


void buildFITS (void)   /* build the FITS header */
{
  sint j;
  uint i, h;
  byte s[CARDLEN];
  str24 r;
  byte *t;
  uint *ui;
  double *d;

  bufndxh = 0;          /* bufferh is empty */
  i = 0;
  while ((t = (byte *) header[i].name) != 0)
    {
      for (j = 0; j < CARDLEN; j++) s[j] = ' ';
      j = 0;
      while (*t) s[j++] = *(t++);       /* copy the name */
      s[8] = '=';                       /* 9th char must be = */

      if ((h = header[i].how) == SETui) /* unsigned integer */
        {
          ui = (uint *) (header[i].here);
          sprintf (r, "%u", *ui);       /* make it a string */
          t = (byte *) r;
          j = (sint) (30 - strlen ((texte) t));
          while (*t) s[j++] = *(t++);   /* copy the string */
        }
      else if (h == SETr)               /* real number */
        {
          d = (double *) (header[i].here);
          sprintf (r, "%.4f", *d);
          t = (byte *) r;
          j = (sint) (30 - strlen ((texte) t));
          while (*t) s[j++] = *(t++);   /* copy the string */
        }
      else if (h == SETs)               /* singly quoted string */
        {
          t = (byte *) (header[i].here);
          j = 10;
          s[j++] = '\'';
          while (*t) s[j++] = *(t++);   /* copy the string */
          s[j++] = '\'';
        }
      else if (h == SETc)               /* constant string */
        {
          t = (byte *) (header[i].here);
          j = (sint) (30 - strlen ((texte) t));
          while (*t) s[j++] = *(t++);   /* copy the constant */
        }
      else if (h == SETl)               /* full line constant string */
        {
          t = (byte *) (header[i].here);
          s[8] = ' ';
          j = 10;
          while (*t) s[j++] = *(t++);   /* copy the constant */
        }

      if ((t = (byte *) header[i].remark) != 0)
        {
          s[31] = '/';                  /* a slash */
          j = 33;
          while (*t) s[j++] = *(t++);   /* copy the comment */
        }

      for (j = 0; j < CARDLEN; j++) bufferh[bufndxh++] = s[j];
      i++;
    }

  /* put an END card at the end */

  for (j = 0; j < CARDLEN; j++) s[j] = ' ';
  t = (byte *) "END";
  j = 0;
  while (*t) s[j++] = *(t++);           /* copy the END token */
  for (j = 0; j < CARDLEN; j++) bufferh[bufndxh++] = s[j];

  /* fill the buffer with spaces */

  while (bufndxh < FITSBLOC) bufferh[bufndxh++] = (byte) ' ';
}


void setCamera (char *name, uint n, CCDtext *f) /* set CCD name, coordinates */
{
  sint j, k;
  uint i, l;
  str24 r;
  byte *t;
  double ra;

  strcpy (r, "\'CCD9\'");
  r[4] = '0' + (char) n;
  j = (INSTline * CARDLEN) + 10;        /* header line for INSTRUME='CCDi' */
  t = (byte *) r;
  while (*t) bufferh[j++] = *(t++);     /* copy formatted instrument name */

  ra = lst + CCDoffRA[n];               /* add offset for this CCD */
  sprintf (r, "%21.6f ", ra);
  j = (RASCline * CARDLEN) + 9;         /* header line for RA in degrees */
  t = (byte *) r;
  while (*t) bufferh[j++] = *(t++);     /* copy formatted date */
  
  k = j = ENDFline * CARDLEN;           /* header END statement */
  i = f->n;
  l = 0;
  while (i > 0)                         /* supplementary FITS header lines */
    {
      t = (byte *) & (f->s[l]);
      while (*t) bufferh[j++] = *(t++);         /* copy one line */
      k += CARDLEN;
      while (j < k) bufferh[j++] = (byte) ' ';  /* fill with blanks */
      j = k;
      l++;
      i--;
    }
  t = (byte *) "END";                   /* final END statement */
  while (*t) bufferh[j++] = *(t++);                /* copy END token */
  while (j < FITSBLOC) bufferh[j++] = (byte) ' ';  /* fill with blanks */

  fprintf (LOG, "CCD%d  \"%s\"  RA: %.3f\n",
                 n,
                 name,
                 ra);
}


void patchHeader (FILE *f, uint lines)  /* change the NAXIS2 in the header */
{
  sint j;
  byte *t;
  str24 r;
  FitsBuff mybuff;

/* fprintf (prn, "patchHeader (%p, %u)\n", f, lines); -- DEBUG */
  j = fseek (f, 0L, SEEK_SET);
/* fprintf (prn, "fseek () = %d\n", j); -- DEBUG */
  if (fread (mybuff, FITSBLOC, 1, f) != 1)
    {
      status = 1;
/* fprintf (prn, "FITS header read failed\n"); -- DEBUG */
      Die ("seek & read failed", "FITS header");
    }
/* fprintf (prn, "FITS header read OK\n"); -- DEBUG */
  sprintf (r, "%21u ", lines);
  j = (XIS2line * CARDLEN) + 9;         /* header line for NAXIS2 */
  t = (byte *) r;
/* fprintf (prn, "j %d r %s\n", j, r); -- DEBUG */
  while (*t) mybuff[j++] = *(t++);      /* copy formatted string */

  j = fseek (f, 0L, SEEK_SET);
/* fprintf (prn, "fseek () = %d\n", j); -- DEBUG */
  if (fwrite (mybuff, FITSBLOC, 1, f) != 1)
    {
      status = 1;
/* fprintf (prn, "FITS header write failed\n"); -- DEBUG */
      Die ("seek & write failed", "FITS header");
    }
/* fprintf (prn, "FITS header written OK\n"); -- DEBUG */
}


uint makefiles (uint *theset)    /* open next set of files */
{
  uint lastset;
  uint n;
  long days, frac;
  fileset *myset;
  str24 r;
  str24 filename;
  /* 1st digit = 3 for TASS Mark III camera */
  /* 2nd digit = 0,1,2 for CCD0 (west), CCD1 (center), CCD2 (east) */
  /* letter T for TASS: The Amateur Sky Survey */
  /* four digits: days since Julian Date 2450000 */
  /* the dot separates the filename from the extension */
  /* three digits: fraction of day in 1000ths since 0h00 UT */
  
/* fprintf (prn, "makefiles (%p)\n", theset); -- DEBUG */
  strcpy (filename, "30T9999.999");
  if (Fset[0].ourset == 0) myset = & Fset[0];
  else myset = & Fset[1];
  lastset = (*theset)++;        /* increment file set number */
  myset->ourset = *theset;
  myset->lines = 0;
  
  /* first, build the filename */

  TimeStamp ("files");
  frac = (long) now + 43200L;           /* this noon */
  days = frac / 86400L;                 /* days since Jan 1, 1970, noon */
  frac -= days * 86400L;                /* seconds since this noon */
  days -= 9412L;                        /* days since J.D. 2450000 */
  sprintf (r, "%04u", (uint) days);
  filename[3] = r[0];
  filename[4] = r[1];
  filename[5] = r[2];
  filename[6] = r[3];
  frac *= 5L;                           /* (frac * 1000) / 86400 */
  frac /= 432L;                         /* day/1000ths since 12h00 */
  sprintf (r, "%03u", (uint) frac);
  filename[8] = r[0];
  filename[9] = r[1];
  filename[10] = r[2];

  /* second, open the files */

  for (n = 0; n < 3; n++)
    {
      filename[1] = '0' + (char) n;
      if (CCDexists[n])
        {
          myset->fil[n] = fopen (filename, "w+b");
/* fprintf (prn, "%p = fopen (%d)\n", myset->fil[n], n); -- DEBUG */
          if (myset->fil[n] == 0) Die ("file creation failed", filename);
        }
      else myset->fil[n] = 0;
    }

  /* third, modify, then write the FITS header */

/* fprintf (prn, "setDateTime\n"); -- DEBUG */
  setDateTime ();
  
  for (n = 0; n < 3; n++)
    {
      if (CCDexists[n])
        {
          filename[1] = '0' + (char) n;
/* fprintf (prn, "setCamera (%d)\n", n);  -- DEBUG */
          setCamera (filename, n, & CCDfits[n]);
          if (fwrite (bufferh, FITSBLOC, 1, myset->fil[n]) != 1)
            Die ("write failed", "FITS header");
          myset->bufndx[n] = 0;
        }
    }

  strcpy (fname, filename);
  if (Display == 'A') fname[1] = 'x'; else
  if (Display == 'W') fname[1] = '0'; else
  if (Display == 'M') fname[1] = '1'; else
  if (Display == 'E') fname[1] = '2';
/* fprintf (prn, "return (%p)\n", lastset); -- DEBUG */
  return (lastset);
}


void closefiles (uint *theset)  /* close last set of files */
{
  uint i;
  uint ndx;
  sint n;
  fileset *myset;

/* fprintf (prn, "closefiles %p\n", theset); -- DEBUG */
  
  if (Fset[0].ourset == *theset) myset = & Fset[0];
  else if (Fset[1].ourset == *theset) myset = & Fset[1];
  else return;

  for (i = 0; i < 3; i++)
    {
      if (CCDexists[i])
        {
/* fprintf (prn, "CCDexists[%d]\n", i); -- DEBUG */
          if (myset->bufndx[i] > 0)
            {
              ndx = myset->bufndx[i];
/* fprintf (prn, "ndx = %d\n", ndx); -- DEBUG */
              while (ndx < FITSBLOC) (*(myset->buffer[i]))[ndx++] = 0;
              n = fwrite (myset->buffer[i], FITSBLOC, 1, myset->fil[i]);
/* fprintf (prn, "n = fwrite (...) %d\n", n); -- DEBUG */
              if (n != 1)
                {
                  status = 1;
                  Die ("last write file failed", "image data");
                }
            }
          if (myset->lines != linesperfil)
            {
/* fprintf (prn, "patchHeader\n"); -- DEBUG */
              patchHeader (myset->fil[i], myset->lines);
            }
/* fprintf (prn, "fclose\n"); -- DEBUG */
          (void) fclose (myset->fil[i]);
        }
    }

  myset->ourset = 0;            /* this set is now unused */
  *theset = 0;
/* fprintf (prn, "done\n"); -- DEBUG */
}


void store (uint theset)    /* store lines into files */
{
  uint i, j;
  uint b;
  sint n;
  long a;
  fileset *myset;
  uPiP here;
  
  if (Fset[0].ourset == theset) myset = & Fset[0];
  else if (Fset[1].ourset == theset) myset = & Fset[1];
  else return;

  (myset->lines)++;
  for (j = 0; j < 3; j++)
    {
      if (CCDexists[j])
        {
          here = pixlin[j];
          for (i = 0; i < MaxPixels; i++)
            {
              a = ((long) ((*here)[i])) - 32768L;
              b = (uint) a >> 8;
              (*(myset->buffer[j]))[myset->bufndx[j]++] = (byte) b;
              if (myset->bufndx[j] >= FITSBLOC)
                {
                  n = fwrite (myset->buffer[j], FITSBLOC, 1, myset->fil[j]);
                  if (n != 1)
                    {
                      Die ("write failed", "image data");
                    }
                  myset->bufndx[j] = 0;
                }
              b = (uint) a;
              (*(myset->buffer[j]))[myset->bufndx[j]++] = (byte) b;
              if (myset->bufndx[j] >= FITSBLOC)
                {
                  n = fwrite (myset->buffer[j], FITSBLOC, 1, myset->fil[j]);
                  if (n != 1)
                    {
                      Die ("write failed", "image data");
                    } 
                  myset->bufndx[j] = 0;
                }
            }
        }
    }
}


void sayStats (void)    /* show stats for the last data line */
{
  long resi;
  double rt, tCCD, tLiq, iTEC;

  /* first, be nice and display the status of the Mark III */

  GotoLC (0,  0); SayString ("row ");
                  SayNumber ((long) totline);
                  SayChar (' ');
  resi = (196592400L / (((long) temp2) - 32768L)) - 1000L;   /* thermistor resistance */
  if (resi < 1300L) resi = 1300L;
  rt = 136.55 - 77.246 * sqrt (log ((double) resi) - 7.1671);
  tCCD = rt;
  GotoLC (0, 13); SayString ("CCD ");
                  SayReal (rt, 2);
                  SayChar (' ');
  resi = (196592400L / (((long) temp1) - 32768L)) - 1000L;   /* thermistor resistance */
  if (resi < 1300L) resi = 1300L;
  rt = 136.55 - 77.246 * sqrt (log ((double) resi) - 7.1671);
  tLiq = rt;
  GotoLC (0, 26); SayString ("Liq ");
                  SayReal (rt, 2);
                  SayChar (' ');
  rt = (0.0000763 * (((double) pvolt) - 32768.0)) * 3.0;
  GotoLC (0, 39); SayString ("+5v ");
                  SayReal (rt, 3);
                  SayChar (' ');
  rt = (0.0000763 * (((double) nvolt) - 32768.0)) * 3.0;
  GotoLC (0, 52); SayString ("-5v ");
                  SayReal (rt, 3);
                  SayChar (' ');
  rt = (0.0000763 * (((double) icool) - 32768.0)) / TECresistor;
  iTEC = rt;
  GotoLC (0, 65); SayString ("TEC ");
                  SayReal (rt, 3);
                  SayChar (' ');

  /* say something more */

  GotoLC (1, 0);  SayString (title);
  GotoLC (1, 13); SayString ("min ");
                  SayNumber ((long) lomost << 3);
                  SayChar (' ');
  GotoLC (1, 26); SayString ("med ");
                  SayNumber ((long) median);
                  SayChar (' ');
  lst += lstinc;            /* update the local sidereal time in degrees */
  if (Display == 'E') rt = lst + CCDoffRA[2]; else
  if (Display == 'M') rt = lst + CCDoffRA[1]; else
  if (Display == 'W') rt = lst + CCDoffRA[0];
  else rt = lst;
  while (rt > 360.0) rt -= 360.0;
  while (rt < 0.0) rt += 360.0;
  GotoLC (1, 39); SayString ("RA ");
                  SayHour (rt / 15.0);
                  SayChar (' ');
  GotoLC (1, 52); SayString ("deg ");
                  SayReal (rt, 3);
                  SayString ("  ");
  GotoLC (1, 65); SayString (fname);
                  SayChar (' ');

  /* famous last words */

  GotoLC (2, 0);  SayString ("TEC ");
                  SayNumber ((long) TECsetting);
                  SayString ("    ");
  GotoLC (2, 13); SayString ("VCO ");
                  SayNumber ((long) VCOsetting);
                  SayString ("    ");
  GotoLC (2, 26); SayString ("var ");
                  SayNumber ((long) uivar);
                  SayString ("    ");
  GotoLC (2, 39); if (TVBvar == 0) SayString ("(TEC)");
                  else SayString ("(VCO)");
  GotoLC (2, 47); SayChar (Display);
  GotoLC (2, 52); SayString ("09 +-=/ EMWA TV CRF Esc");

  /* temperature safety checks */

  if ((tCCD - tLiq) > 5.0)      /* TEC power supply probably reversed */
    {
      TECsetting = 0;
      setDACs ();
      if (LOG)
        {
          fprintf (LOG, "CCD temp: %.2f, Liquid coolant temp: %.2f\n",
                        tCCD, tLiq);
          fprintf (LOG, "TEC current: %.2f, TEC setting: %ld\n",
                        iTEC, (long) TECsetting);
        }
      Die ("CCD much hotter than liquid coolant, ",
           "TEC power supply probably reversed!");
    }
  if (tLiq > tLiqMax)           /* liquid coolant too hot or not circulating */
    {
      TECsetting = 0;
      setDACs ();
      if (LOG)
        {
          fprintf (LOG, "CCD temp: %.2f, Liquid coolant temp: %.2f\n",
                        tCCD, tLiq);
          fprintf (LOG, "TEC current: %.2f, TEC setting: %ld\n",
                        iTEC, (long) TECsetting);
        }
      Die ("Liquid coolant too hot or not circulating, ",
           "check pump and liquid level!");
    }
}


uint Interpol8 (uint x, uint lo, uint me, uint hi)
{                                       /* parabolic interpolation */
  long l, m;
  uint k;

  k = lo;  if (k > hi) k = hi;  if (k > me) k = me;
  lo -= k;  me -= k;  hi -= k;

  m = ((long) lo) - (long) hi;
  l = ((long) lo) + (long) hi;
  l -= ((long) me) + (long) me;
  if ((l == 0) || (m == 0)) m = 0;
  else
    {
      m += m;  m += m;  m /= l;
      if (m < -7) m = -7;
      else if (m > 7) m = 7;
    }
  m += 4 + (long) x;

  return ((uint) m);
}


double StdDev (long x0, long y0,     /* find the standard deviation: */
               long x1, long y1,     /* x0..x4 = deviates with resp. */
               long x2, long y2,     /* y0..y4 as local populations. */
               long x3, long y3,
               long x4, long y4)
{
  long sumy, sumyx2;
  double variance;
  
  x0 *= x0;  x1 *= x1;  x2 *= x2;  x3 *= x3;  x4 *= x4;  /* squared deviates */
  sumy = y0 + y1 + y2 + y3 + y4;
  sumyx2 = (y0 * x0) + (y1 * x1) + (y2 * x2) + (y3 * x3) + (y4 * x4);
  variance = ((double) sumyx2) / ((double) sumy);
  return (sqrt (variance));
}

                  
void Focus (uint top, uint ave)       /* process new data line to estimate focus */
{
  uint i, j, k, l, n;
  long value, older, lowest;
  double devi[5];
  uPiP here;
  sPiP trow;

  /* shift the focussing array */

  trow = PixNow.row[4];
  for (i = 4; i > 0; i--) PixNow.row[i] = PixNow.row[i-1];
  PixNow.row[0] = trow;
  
  /* contribute a line to the focussing array */

  for (n = 0; n < 3; n++)
    {
      if ((CCDexists[n]) && (LinShown[n]))
        {
          here = pixlin[n];

          /* build a smooth background */

          i = 14;
          value = 0;
          for (k = 0; k < 16; k++)
            {
              j = (*here)[i++];
              if (j > top) j = ave;
              value += (long) j;
            }
          for (k = 0; k < 16; k++) (*sums)[k] = value;
          for (k = 16; k < 768; k++)
            {
              l = (*here)[i-16];
              if (l > top) l = ave;
              value -= (long) l;
              j = (*here)[i++];
              if (j > top) j = ave;
              value += (long) j;
              (*sums)[k-16] += value;
              (*sums)[k] = value;
            }
          for (k = 752; k < 768; k++) (*sums)[k] += value;
          
          /* substract said smooth background */
          
          i = 14;
          for (k = 0; k < 768; k++)
            {
              value = ((long) (*here)[i++]) - ((*sums)[k] >> 5);
              /* remove saturated pixels */
              if ((value > 32767L) || (value < -32767L)) value = 0;
              (*trow)[k] = (sint) value;
            }
        }
    }

  /* every 5th line, display the correlation matrix */      

  if (PixNow.don == 4)
    {
      value = 0;                    /* shift down to get a zero average */
      for (j = 0; j < 5; j++)
        for (i = 0; i < 9; i++)
          value += autocorr[j][i];

      value /= 45L;
      lowest = accucorr[0][4];
      for (j = 0; j < 5; j++)      /* smooth down as ((old * 7) + new) / 8 */
        {
          for (i = 0; i < 9; i++)
            {
              older = accucorr[j][i];
              older += (older << 1) + (older << 2) + (autocorr[j][i] - value);
              older >>= 3;
              accucorr[j][i] = older;
              if (older < lowest) lowest = older;
            }
        }

      devi[0] = StdDev (0L, (accucorr[0][4] - lowest) >> 1,
                        1L, accucorr[0][3] - lowest,
                        2L, accucorr[0][2] - lowest,
                        3L, accucorr[0][1] - lowest,
                        4L, accucorr[0][0] - lowest);
      devi[1] = StdDev (0L, (accucorr[0][4] - lowest) >> 1,
                        1L, accucorr[1][3] - lowest,
                        2L, accucorr[2][2] - lowest,
                        3L, accucorr[3][1] - lowest,
                        4L, accucorr[4][0] - lowest);
      devi[2] = StdDev (0L, (accucorr[0][4] - lowest) >> 1,
                        1L, accucorr[1][4] - lowest,
                        2L, accucorr[2][4] - lowest,
                        3L, accucorr[3][4] - lowest,
                        4L, accucorr[4][4] - lowest);
      devi[3] = StdDev (0L, (accucorr[0][4] - lowest) >> 1,
                        1L, accucorr[1][5] - lowest,
                        2L, accucorr[2][6] - lowest,
                        3L, accucorr[3][7] - lowest,
                        4L, accucorr[4][8] - lowest);
      devi[4] = StdDev (0L, (accucorr[0][4] - lowest) >> 1,
                        1L, accucorr[0][5] - lowest,
                        2L, accucorr[0][6] - lowest,
                        3L, accucorr[0][7] - lowest,
                        4L, accucorr[0][8] - lowest);

      value = accucorr[0][4];
      for (j = 0; j < 5; j++)      /* show scaled-down autocorrelation */
        {
          GotoLC (3 + j, 0);
          for (i = 0; i < 9; i++) SayLong ((accucorr[j][i] * 100) / value);
          SayString (direction[j]);
          SayReal (devi[j], 2);
          SayChar (' ');
        }
    }

  PixNow.don = PixNow.cur;
  if (PixNow.cur >= 4) PixNow.cur = 0; else PixNow.cur++;
}


void sayCCDlines (void) /* show 1 data line per chip */
{
  byte bits0, bits1, bits2, bits3, val;
  uint i, j, k, l, m, n;
  uint losum, hisum;
  long value;
  uPiP here;
  
  if (TotalShown == 0) return;
  if (FirstShown >= ChipPixels) return;
  if (Num8 == 0) return;

  for (j = 0; j < 8192; j++) (*histo)[j] = 0;

  /* build a histogram */

  for (n = 0; n < 3; n++)
    {
      if ((CCDexists[n]) && (LinShown[n]))
        {
          here = pixlin[n];
          for (i = 14; i < 782; i++)
            {
              j = (uint) (((long) (*here)[i]) >> 3);
              ((*histo)[j])++;
            }
        }
    }

  /* find the median and lowest value */

  value = 0;
  i = 0;     losum = (*histo)[i];
  k = 8192;  hisum = 0;
  if (losum > 0) lomost = i;  else lomost = 8191;
  while (i < k)
    {
      if (losum <= hisum)
        {
          l = (*histo)[++i];
          losum += l;
          if ((lomost == 8191) && ((*histo)[i] > 0)) lomost = i;
          value += ((long) l) * (long) i;
        }
      else
       {
         l = (*histo)[--k];
         hisum += l;
       }
    }

  /* now, i == k == (median >> 3), so interpolate median by */
  /* finding the maximum of the minimum of (losum, hisum)   */

  median = i << 3;  n = (*histo)[i];
  if (losum < hisum) l = losum; else l = hisum;
  median = Interpol8 (median, losum - n, l, hisum - n);
  value <<= 3;
  if (losum != 0) value /= losum;
  m = (uint) value;                       /* average below median */

  /* show the pixels */
  
  m >>= 4;
  for (n = 0; n < 3; n++)
    {
      if ((CCDexists[n]) && (LinShown[n]))
        {
          here = pixlin[n];
          i = FirstShown;              /* index into (*here) */
          for (k = 0; k < Num8; k++)   /* index into bitplane[0,1,2,3] */
            {
              j = (uint) (((long) (*here)[i++]) >> 4);
              if (j < m) j = 0; else j -= m;
              j >>= 3;
              if (j >= 256) val = logLUT[255]; else val = logLUT[j];
              bits0 = (val & 1);  val >>= 1;
              bits1 = (val & 1);  val >>= 1;
              bits2 = (val & 1);  val >>= 1;
              bits3 = (val & 1);
              for (l = 1; l < 8; l++)  /* bit index into byte */
                {
                  bits0 <<= 1;  bits1 <<= 1;  bits2 <<= 1;  bits3 <<= 1;
                  j = (uint) (((long) (*here)[i++]) >> 4);
                  if (j < m) j = 0; else j -= m;
                  j >>= 3;
                  if (j >= 256) val = logLUT[255]; else val = logLUT[j];
                  bits0 |= (val & 1);  val >>= 1;
                  bits1 |= (val & 1);  val >>= 1;
                  bits2 |= (val & 1);  val >>= 1;
                  bits3 |= (val & 1);
                }
              bitplane[0][k] = bits0;
              bitplane[1][k] = bits1;
              bitplane[2][k] = bits2;
              bitplane[3][k] = bits3;
            }
          setVideoLine (OffLine[n] + PixLine[n], Num8);
          PixLine[n]++;
          if (PixLine[n] >= LinShown[n]) PixLine[n] = 0;
          ClearVideoLine (OffLine[n] + PixLine[n], 0);
        }
    }

  sayStats ();          /* say something about what's going on */

  if (Focussing)        /* process to build the focussing array */
    {
      value = (((long) median) << 1) - (long) lomost;
      if (value > 61439L) value = 61439L;
      Focus ((uint) value, median);
    }
}


void waitForReady (sint flag)            /* wait for a Ready signal */
{
  byte val;
  byte want;
  sint count;
  uint spill;

  if (flag) want = TestBit; else want = 0;
  count = 255;
  spill = 0;
  do {
      if (count-- <= 0)
        {
          count = 255;
          if (KeyPressed ()) ProcessKey (GetKey (), 0);
        }
      if (spill == 65535u)
        {
          spill = 0;
          SayChar ('.');
        }
      else spill++;
      val = BIOget (TASSports + ReadyIn);
  } while ((val & TestBit) != want);
}


void clearReady (void)                  /* clear the Ready signal */
{
  BIOput (TASSports + PathSetup, ResetReady);
  BIOput (TASSports + AuxLoad, 0);          /* reset the READY bit */
  waitForReady (1);                         /* wait till it is done */
}


uint GetValue (byte Mux, sint *first)    /* get a value from ADC input Mux */
{
  byte xhi, xlo;
  long value;
  byte start;

  clearReady ();                 /* reset the READY bit */

  if (*first)
    {
      *first = 0;
      start = ShiftStart | Mux;  /* horizontal shift and start ADC */
    }
  else
    {
      start = StartOnly | Mux;   /* no horizontal shift but start ADC */
    }
  BIOput (TASSports + PathSetup, start);
  BIOput (TASSports + AuxLoad, 0);          /* start ADC & get high byte */
  waitForReady (0);                         /* wait for Ready to clear */

  xhi = BIOget (TASSports + DataIn);                    /* high byte */
  BIOput (TASSports + PathSetup, GetLoByte | Mux);      /* sets Ready bit */
  xlo = BIOget (TASSports + DataIn);                    /* low byte */
  value = (long) xhi;
  value = (value << 8) + (long) xlo;        /* assemble the bytes */
  if (xhi & 0x80) value |= 0xFFFF0000L;     /* MSBit raised = negative value */
  value += 32768L;                          /* from signed to unsigned */
  return ((uint) value);
}


void setALLthree (uint pix, uint val) /* set all 3 pixels to the same value */
{
  uint i;
  
  for (i = 0; i < 3; i++)
    {
      if (CCDexists[i]) (*(pixlin[i]))[pix] = val;
    }
}


void getCCDlines (void) /* get 1 data line of ChipPixels pixels per chip */
{
  uint i, k;
  uint pix;
  uint val;
  sint first;
  sint *trow;
  sint *orow[9];
  vector corr;

  if (Focussing)
    {
      trow = &((*(PixNow.row[PixNow.don]))[4]);
      for (k = 0; k < 9; k++)
        {
          corr[k] = 0;
          orow[k] = &((*(PixNow.row[0]))[k]);
        }
    }

#ifdef __WATCOMC__
  if (WATbug != 0)              /* to avoid a bug of the WATCOM compiler */
    {
/*      fprintf (LOG, ".");  */
      WATbug = 0;
    }
#endif
  waitForReady (0);             /* wait for end of vertical shift */

  for (pix = 0; pix < 18; pix++)
    {
      k = 0;
      first = 1;
      for (i = 0; i < 3; i++)
        {
          if (CCDexists[i])
            {
              val = GetValue (CCD[i], & first);
              if (Reversed) val = (uint) (65535L - (long) val);
              (*(pixlin[i]))[pix] = (uint) val;
            }
        }
    }

  for (pix = 18; pix < 778; pix++)
    {
      k = 0;
      first = 1;
      for (i = 0; i < 3; i++)
        {
          if (Focussing)
            {
              corr[k] += product (*trow, *(orow[k]++)) >> 9;
              k++;
              corr[k] += product (*trow, *(orow[k]++)) >> 9;
              k++;
              corr[k] += product (*trow, *(orow[k]++)) >> 9;
              k++;
            }
          if (CCDexists[i])
            {
              val = GetValue (CCD[i], & first);
              if (Reversed) val = (uint) (65535L - (long) val);
              (*(pixlin[i]))[pix] = (uint) val;
            }
        }
      if (Focussing) trow++;
    }

  for (pix = 778; pix < ChipPixels; pix++)
    {
      k = 0;
      first = 1;
      for (i = 0; i < 3; i++)
        {
          if (CCDexists[i])
            {
              val = GetValue (CCD[i], & first);
              if (Reversed) val = (uint) (65535L - (long) val);
              (*(pixlin[i]))[pix] = (uint) val;
            }
        }
    }

  if (Focussing)
    {
      for (k = 0; k < 9; k++) autocorr[PixNow.don][k] = corr[k] >> 4;
    }

  first = 0;
  pix = ChipPixels;
  setALLthree (pix, (temp2 = GetValue (Therm2, & first)));  pix++;
  setALLthree (pix, (temp1 = GetValue (Therm1, & first)));  pix++;
  setALLthree (pix, (icool = GetValue (TEC, & first)));     pix++;
  setALLthree (pix, (pvolt = GetValue (Plus5, & first)));   pix++;
  setALLthree (pix, (nvolt = GetValue (Minus5, & first)));
  while (++pix < MaxPixels) setALLthree (pix, 0);

  clearReady ();                /* reset the READY bit for vshift */
}


void setDACs (void)             /* set the DACs */
{
  char buf[33];

  /* load DAC1 with TEC value */

  BIOput (TASSports + DACsetup, DACloTEC);
  BIOput (TASSports + DACload, (byte) (TECsetting));
  BIOput (TASSports + DACsetup, DAChiTEC);
  BIOput (TASSports + DACload, (byte) (TECsetting >> 8));
  
  /* load DAC0 with VCO frequency and set clocked vert. shift */

  BIOput (TASSports + DACsetup, DACloVCO);
  BIOput (TASSports + DACload, (byte) (VCOsetting));
  BIOput (TASSports + DACsetup, DAChiVCO);
  BIOput (TASSports + DACload, (byte) (VCOsetting >> 8));

  /* clear DAC setup flag */
  DACflag = 0;
  sprintf (buf, "set  TEC: %d  VCO: %d", TECsetting, VCOsetting);
  TimeStamp (buf);

#ifdef __WATCOMC__
  WATbug = 1;              /* to avoid a bug of the WATCOM compiler */
#endif
}


void clearCCD (uint n)  /* get & ignore num data lines as fast as possible */
{
  uint oldline, hack;
  str24 oldtitle;

  strcpy (oldtitle, title);
  strcpy (title, "clearing  ");
  oldline = totline;
  totline = hack = n;
  lstinc = 0.175 * 0.004178075;  /* 0.175 sec/row * 0.004178075 degrees/sec */

  BIOput (TASSports + DACsetup, ClrAddr);   /* non-clocked vertical shift */
  while (n--)
    {
      /* generate a vertical shift pulse */
      (void) BIOget (TASSports + SelData);

      getCCDlines ();
      sayCCDlines ();
      totline--;

      /* check if a key was pressed */
      while (KeyPressed ()) ProcessKey (GetKey (), 0);
      if (DACflag)
        {
          setDACs ();
          BIOput (TASSports + DACsetup, ClrAddr);   /* non-clocked vertical shift */
        }
    }

  totline = oldline;
  strcpy (title, oldtitle);
  setDACs ();
  lstinc = lineTime * 0.004178075;   /* 0.004178075 degrees per second */
}


void ramp_up (uint n)   /* get & show n data lines without storing */
{
  uint oldline;
  str24 oldtitle;

  strcpy (oldtitle, title);
  strcpy (title, "ramp up   ");
  oldline = totline;
  totline = n;

  while (n--)
    {
      getCCDlines ();
      sayCCDlines ();
      totline--;

      /* check if a key was pressed */
      while (KeyPressed ()) ProcessKey (GetKey (), 1);
      if (DACflag) setDACs ();
    }

  totline = oldline;
  strcpy (title, oldtitle);
}


void ProcessKey (sint Key, sint CRok)   /* process a key */
{
  sint k;

  k = toupper (Key & 255);
  if (k == 27)                          /* escape = abort program */
    {
      Die ("program interrupted by", "Escape key");
    }
  else if (k == 3)                      /* Ctrl-C = abort program */
    {
      Die ("program interrupted by", "Ctrl-C key");
    }
  else if (k == 'C')
    {
      if (CRok > 0)
        {
          TimeStamp ("clear");
          clearCCD (ChipLines);         /* clear CCDs once */
          if (CRok > 1)
            {
              TimeStamp ("ramp-up");
              ramp_up (ChipLines);          /* ramp-up the CCD */
            }
          TimeStamp ("ready");
        }
    }
  else if (k == 'T') TVBvar = 0;        /* variable is TEC control */
  else if (k == 'V') TVBvar = 1;        /* variable is Vertical shift clock */
  else if (k == 'R') Reversed ^= 1;     /* reverse the CCD image */
  else if ((k == 'A') ||                /* display 3 images together */
           (k == 'E') ||                /* display eastern image */
           (k == 'M') ||                /* display middle image */
           (k == 'W'))                  /* display western image */
    {
      setDisplayArea ((char) k);
    }
  else if (k == 'F') ToggleFocus ();    /* set focussing ON or OFF */
  else if (k == '/') uivar = 0;         /* slash: clear variable */
  else if (isdigit (k))                 /* it's a digit - change value */
    {
      uivar *= 10;
      uivar += (uint) (k - '0');
    }
  else if (k == '+')                    /* plus: add to variable */
    {
      if (TVBvar == 0) TECsetting += uivar;
      else VCOsetting += uivar;
      DACflag = 1;
      uivar = 0;
    }
  else if (k == '-')                    /* minus: sub from variable */
    {
      if (TVBvar == 0) TECsetting -= uivar;
      else VCOsetting -= uivar;
      DACflag = 1;
      uivar = 0;
    }
  else if (k == '=')                    /* equals: assign to variable */
    {
      if (TVBvar == 0) TECsetting = uivar;
      else VCOsetting = uivar;
      DACflag = 1;
      uivar = 0;
    }
}


void EndLog (void)
{
  TimeStamp ("end");
  fprintf (LOG, "\n");
  fclose (LOG);
  LOG = NULL;
/* fclose (prn); -- DEBUG */
}


void Die (texte s1, texte s2)   /* let's die a violent death */
{
  sint Dead = Dying;

  Dying++;
  if (LOG)
    {
      fprintf (LOG, "*** %s %s ***\n", s1, s2);
      if (errno != 0)
        fprintf (LOG, "errno %d %s\n", errno, sys_errlist [errno]);
      if (_doserrno != 0)
        fprintf (LOG, "_doserrno %d\n", _doserrno);
    }
  if ((status == 2) && (!Dead)) /* at the end, close all open files */
    {
      if (oldset) closefiles (& oldset);
      if (curset) closefiles (& curset);
      status = 1;
    }
  if (LOG) EndLog ();
  if ((status == 1) && (!Dead)) /* terminate cleanly */
    {
      GraphicsQuit ();
      status = 0;
    }

  fprintf (stderr, "*** %s %s ***\n", s1, s2);
  if (Dead) Dying--;
  else
    {
      (void) fcloseall ();      /* make sure no FILE* stays open */
      exit (-1);
    }
}


void CtlBrk (sint sig)
{
  Die ("program interrupted by", "Ctrl-Break key"); 
}


void DivErr (sint sig)
{
  str24 buf;

  sprintf (buf, "%ld", (long) sig);
  if (sig < 128) Die ("integer error", buf);
  else Die ("floating-point error", buf);
}

    
int main (sint argc, texte argv[])
{
  uint curline; /* currently collected line */
  uint overlap; /* start of overlap area */

  /* initialize all global variables to default values */
  initialize ();
  setCtrlBreak ();
  signal (SIGINT, CtlBrk);
  signal (SIGFPE, DivErr);

  /* read the config file "tass.rc" in the current directory */
  getconfig ("tass.rc");

  /* prepare the FITS header */
  buildFITS ();

  /* prepare the display */
  GraphicsInit ();
  status = 1;
  strcpy (title, "collecting");

  /* clear the CCD chips, then ramp-up: show the images but don't keep data */
  TimeStamp ("clear");
  clearCCD (ChipLines * 2);
  TimeStamp ("ramp-up");
  ramp_up (ChipLines);

  /* open the first set of files */
  curset = 0;
  oldset = makefiles (& curset);
  status = 2;
  
  /* main loop: get as many image lines per CCD as requested */
  overlap = linesperfil - linesoverlap;
  curline = 1;
  totline = 1;
  while (linesperCCD--)
    {
      /* get 1 image line per CCD chip */
      getCCDlines ();
      sayCCDlines ();
      curline++;
      totline++;

      /* store these in the current files */
      store (curset);

      /* at the beginning of the overlap, the current set of files becomes */
      /* the previous set of files and we open a new current set of files */
      if (curline == overlap) oldset = makefiles (& curset);

      /* during the overlap, store these also in the previous files */
      if (curline > overlap) store (oldset);
      /* at the end of the overlap, close the previous files */
      if (curline >= linesperfil)
        {
          closefiles (& oldset);
          curline = linesoverlap;
        }

      /* check if a key was pressed */
      while (KeyPressed ()) ProcessKey (GetKey (), 2);
      if (DACflag) setDACs ();
    }

  /* at the end, close all open files */

  if (oldset) closefiles (& oldset);
  if (curset) closefiles (& curset);
  status = 1;
  if (LOG) EndLog ();

  /* if required, wait for Esc key, then terminate cleanly */
  if (DontStop)
    {
      strcpy (title, "press Esc ");
      GotoLC (1, 0);  SayString (title);
      while (GetKey () != 27);
    }
  GraphicsQuit ();
  status = 0;

  for (curline = 0; curline < 5; curline++) free (PixNow.row[curline]);
  for (curline = 0; curline < 3; curline++) free (pixlin[curline]);
  for (curline = 0; curline < 3; curline++) free (Fset[0].buffer[curline]);
  for (curline = 0; curline < 3; curline++) free (Fset[1].buffer[curline]);
  free (histo);
  free (sums);
  (void) fcloseall ();          /* make sure no FILE* stays open */
  return (0);
}
