/***************************************************************************
 *                                                                         *
 *                          ---------------                                *
 *                           ssmitdrlatlon   2.1                           *
 *                          ---------------                                *
 *                                                                         *
 *     Read NESDIS SSM/I TDR data set.  Create a new TDR file containing   *
 *     scans within specified latitude-longitude boundaries.               *
 *                                                                         *
 ***************************************************************************
 *                                                                         *
 *     Usage:  ssmitdrlatlon [-c] south north west east infile outfile     *
 *             ssmitdrlatlon -help                                         *
 *                                                                         *
 *                                                                         *
 *          Options:                                                       *
 *                                                                         *
 *     The -c option ("check") is used to check the specified lat-lon      *
 *     boundaries without actually performing the filtering; it shows      *
 *     how the program interprets the command-line parameters.             *
 *                                                                         *
 *     The -help option prints this help summary.                          *
 *                                                                         *
 *     There are also verbose (-v) and debug (-d) options, which reverse   *
 *     the sense of the default settings.  These are of more use to the    *
 *     programmer than the general user, so they aren't documented in the  *
 *     help listing.                                                       *
 *                                                                         *
 *                                                                         *
 *          Latitude/longitude limits:                                     *
 *                                                                         *
 *     Latitude may range from -90 to 90 inclusive, i.e. 90S - 90N.        *
 *     Longitude may range from -180 to 180 inclusive, i.e. 180W - 180E.   *
 *                                                                         *
 *     However, south must be less than north, and west must be unequal    *
 *     to east.  (Note:  in the special case where west = -180 and         *
 *        east = 180, the region of interest (ROI) covers all 360 degrees  *
 *        of longitude.  See the third example below.)                     *
 *                                                                         *
 *     North latitude and east longitude are positive integers.            *
 *     South latitude and west longitude are negative integers.            *
 *                                                                         *
 *                                                                         *
 *          Files:                                                         *
 *                                                                         *
 *     Input and output file names must be different.  If the output       *
 *     file already exists, it will be overwritten.                        *
 *                                                                         *
 *                                                                         *
 *          Examples:                                                      *
 *                                                                         *
 *     ssmitdrlatlon 25 50 -125 -65 tdrfile.dat conus.dat                  *
 *        gets scans from 25N to 50N latitude and 125W to 65W longitude    *
 *                                                                         *
 *     ssmitdrlatlon -20 20 150 -150 tdr.dat CEPEX.out                     *
 *        gets scans from 20S to 20N latitude and 150E to 150W longitude   *
 *                                                                         *
 *     ssmitdrlatlon 0 90 -180 180 in.dat Nhemi.dat                        *
 *        gets scans from northern hemisphere                              *
 *                                                                         *
 ***************************************************************************
 *                                                                         *
 *     Written 11/94 by Don Moss, University of Alabama in Huntsville      *
 *                                                                         *
 *     Revised 3/95 by Don Moss for DPR 95-092  -  Release 1.1             *
 *        Define fseek parameters for non-standard compilers, i.e. not     *
 *        compliant with ISO 9899:1990 sect. 7.9.9.2.                      *
 *                                                                         *
 *     Revised 4/95 by Don Moss for DPR 95-093  -  Release 2.0             *
 *        - Don't try to read and write same file - some implementations   *
 *        don't handle this correctly.                                     *
 *        - Add more error checking (after rewrites in FixHdr).            *
 *        - Report statistics at end.                                      *
 *        - Make program name a fixed string - argv[0] format varies       *
 *        from system to system.                                           *
 *        - Make a few cosmetic changes.                                   *
 *                                                                         *
 *     Revised  5/97 by Don Moss for DOR 97-239 - Release 2.1              *
 *        Enable reading of new NESDIS "NPR" format.                       *
 *                                                                         *
 ***************************************************************************/

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <limits.h>
#include <math.h>

#define TRUE 1
#define FALSE 0

#ifndef SEEK_SET
#define SEEK_SET 0
#define SEEK_CUR 1
#define SEEK_END 2
#endif

#define ERROUT   stderr
#define BUFSIZE    4444
#define SPN_FRAME 12798
#define NPR_FRAME  3604
#define NPR_HDRS   2158
#define SPN_FILLER 0xA5
#define DATSEQ_OFF   28
#define DATSEQ_LEN   32
#define REVHDR_OFF 2128
#define REVHDR_LEN   30
#define HDR1_LEN     76
#define HDR2_LEN    194
#define DATA_LEN   3334
#define EOF_LEN       6
#define LORES        64

/*-----return codes-----*/
#define BAD_LEN    -1
#define PAST_EOF   -2
#define OK          0
#define END_FILE    1
#define CANT_OPEN   2
#define BAD_HDRS    3
#define BAD_EOF     4
#define WRITE_ERR   6
#define FATAL_ERR   8

/*-----TDR block names-----*/
#define ProdID      0
#define DataSeq     1
#define RevHdrDD    2
#define ScanHdr1DD  3
#define ScanHdr2DD  4
#define ScanDD      5
#define RevHdr      6
#define ScanHdr1    7
#define ScanHdr2    8
#define Scan        9
#define EoF        10

/*-----macros-----*/
/*
 * V1 evaluates a 1-byte binary value as an unsigned number
 * V2 evaluates a 2-byte binary value as an unsigned number
 * V4 evaluates a 4-byte binary value as an unsigned number
 */
#define V1(p) ((int32) *(buf+p))
#define V2(p) (256L * (int32) *(buf+p) + (int32) *(buf+p+1))
#define V4(p) (256L * (256L * (256L * (int32) *(buf+p) + (int32) *(buf+p+1)) + (int32) *(buf+p+2)) + (int32) *(buf+p+3))

/*-----seldom-changed option-----*/
#define VERBOSE TRUE		 /* enable verbose mode */
#define DEBUG FALSE		 /* disable debugging */

/*-----program name and version-----*/
#define PROG_NAME "ssmitdrlatlon"   /* name of this program */

                              /***************************************************/
#define RELEASE_LVL "2.1"    /* NOTE:  change release level with every revision */
                            /***************************************************/

/*-----types-----*/
typedef char bool;
#if INT_MAX < 0x7FFFFFFF
    typedef long int32;
#else
    typedef int int32;
#endif
#if USHRT_MAX < 0xFFFF
    typedef unsigned int uint16;
    typedef int int16;
#else
    typedef unsigned short uint16;
    typedef short int16;
#endif

/*-----functions-----*/
void ShowUsage(void);		 /* explain syntax */
void PrintHelp(void);		 /* print help screen */
void PrLat(int32 lat);		 /* print latitude in conventional form  */
void PrLon(int32 lon);		 /* print longitude in conventional form */
int Copy(int blktype);		 /* copy header blocks to output file */
int GetBlock(void);
uint16 CheckSum(int len);
uint16 GenSum(int len, unsigned char *cp);
uint16 Sum(int n, unsigned char *cp);
int ReadValid(int blktype);
bool InROI(void);		 /* true if current scan in region of interest */
bool SpotCheck(int lat_offset);	 /* true if spot in ROI */
int WriteScan(unsigned char *hdr1, unsigned char *hdr2, unsigned char *data);
int FixHdr(unsigned char *datseq,
    unsigned char *revhdr);      /* write updated values into header blocks */
void EndianCopy(void *to, void *from, int n);    /* copy values into buffer, */
			    /* reversing bytes if on a little endian machine */
void ShowStat(int nominal_in, int read_ok, int selected); /* report statistics */


/*-----header info-----*/
int32
    fcyr, fcmon, fcday,		 /* file creation date */
    fchr, fcmin,		 /* file creation time */
    scans,			 /* number of scans in file (from DataSeq) */
    scid,			 /* spacecraft ID */
    rev,			 /* nominal rev # */
    bjld, bhr, bmin, bsec,	 /* begin day of year, time */
    ejld, ehr, emin, esec,	 /* ending day of year, time */
    ajld, ahr, amin, asec,	 /* ascending node doy, time */
    lsat;			 /* logical satellite ID */
/*
 * NOTE:  the beginning and ending day and time are changed in the output
 * file to reflect the actual first and last scans put into the file.
 */

/*-----scan data-----*/
int32
    scann,			 /* scan number (from ScanHdr1) */
    bst;			 /* B-scan start time (sec) scaled by 10000 */

/*-----other global variables-----*/
int32 ejld0;			 /* original ejld */
int32 first_bst_used;		 /* time of first output scan */
int32 last_bst_used;		 /* time of last output scan */
const int endian_test = 1;	 /* value used to test byte sex */
bool little_endian;		 /* true if host is little endian */
bool debug = DEBUG;		 /* also set (or reset) by the -d option */
bool verbose = VERBOSE;		 /* also set (or reset) by the -v option */
uint16 oscan = 0;		 /* output scan number */
int scans_read = 0;		 /* number of scans read */

FILE *infp;			 /* input file pointer */
FILE *outfp;			 /* output file pointer */
unsigned char buf[BUFSIZE];      /* input file buffer */
int blklen[] = {28, 32, 190, 370, 1138, 370, 30,
		HDR1_LEN, HDR2_LEN, DATA_LEN, EOF_LEN};
char *blknam[] = {"ProdID", "DataSeq", "RevHdrDD", "ScanHdr1DD", "ScanHdr2DD",
                    "ScanDD", "RevHdr", "ScanHdr1", "ScanHdr2", "Scan", "EoF"};
bool SPN;			 /* true if reading a NESDIS SPN file */
bool NPR;			 /* true if reading a NESDIS NPR file */
int framesize;			 /* frame size of SPN or NPR file */
int32
    latS,			 /* southern boundary */
    latN,			 /* northern boundary */
    lonW,			 /* western boundary  */
    lonE,			 /* eastern boundary  */
    ZlatS, ZlatN, ZlonW, ZlonE;	 /* boundaries scaled by 100 */
bool longwrap;			 /* true if ROI contains prime meridian */



/*==================== main ====================*/
int main(int argc, char * argv[])
{
    bool check = FALSE;		 /* true if -c option was entered */
    bool help = FALSE;		 /* true if -h option was entered */
    int arg;			 /* command line arg index */
    int32 z;			 /* intermediate variable */
    char c;			 /* intermediate variable */

    char *infn;			 /* input file name pointer */
    char *outfn;		 /* output file name pointer */
    char blk1check[] = {0, 14, 1, 1, 'F', 'N', 'O', 'C'};
       /* the data above is what's supposed to be in the Product ID Block */
    char eof_blk[] = {0, 3, 1, 2, '\xFE', '\xFB'};
    int32 len;			 /* block length */
    int32 len0301 = 2 * 0x0301;  /* quasi-length of certain erroneous SPN blocks */
    int32 len0681 = 2 * 0x0681;  /* quasi-length of certain erroneous SPN blocks */
    int32 offset;		 /* offset to next frame, for skipping bad blocks */
    int32 status;		 /* error condition */
    int32 satid;		 /* satellite ID */
    int32 bst0 = 0;		 /* previous bst */
    int32 jld;			 /* current scan's julian day */
    unsigned char
	datseq[DATSEQ_LEN],	 /* holding area for Data Sequence Block */
	revhdr[REVHDR_LEN],	 /* holding area for Rev Header Block    */
	hdr1[HDR1_LEN],		 /* holding area for Scan Header Block 1 */
        hdr2[HDR2_LEN];		 /* holding area for Scan Header Block 2 */



    /*-----determine byte sex-----*/
    memcpy(&little_endian, &endian_test, 1);

    /*-----check for minimal number of command line arguments-----*/
    if (argc < 2) {
	ShowUsage();
        return FATAL_ERR;
    }

    /*-----read options-----*/
    arg = 1;   /* number of arguments processed so far */
    while (arg < argc && argv[arg][0] == '-') {
	c = argv[arg++][1];
	if      (c == 'c') check   = TRUE;
	else if (c == 'h') help    = TRUE;
	else if (c == 'd') debug   = !debug;
	else if (c == 'v') verbose = !verbose;
	else {
	    /*---not an option, must be a number---*/
	    arg--;
	    break;
	}
    }

    /*-----print help if requested-----*/
    if (help) {
	PrintHelp();
	return OK;
    }

    /*-----there should be 6 arguments remaining-----*/
    if (argc - arg != 6) {
	ShowUsage();
	return FATAL_ERR;
    }

    /*-----get user's boundary coordinates-----*/
    latS = atol(argv[arg++]);
    latN = atol(argv[arg++]);
    lonW = atol(argv[arg++]);
    lonE = atol(argv[arg++]);

    /*-----scale by 100 and change range to [0:90] and [0:360]-----*/
    /*-----  to make commensurate with values to be tested    -----*/
    ZlatS = (latS + 90) * 100;
    ZlatN = (latN + 90) * 100;
    ZlonW = ((lonW + 360) % 360) * 100;
    ZlonE = ((lonE + 360) % 360) * 100;

    /*-----check whether longitude wraps from 360 to 0-----*/
    longwrap = (ZlonW >= ZlonE);

    /*-----get input and output file names-----*/
    infn = argv[arg++];
    outfn = argv[arg++];

    /*-----show interpreted parameters-----*/
    printf("\n%s %s\n\n", PROG_NAME, RELEASE_LVL);
    printf("Parameters entered:\n");
    printf("     south:  %4d", latS); PrLat(latS);
    printf("     north:  %4d", latN); PrLat(latN);
    printf("     west:   %4d", lonW); PrLon(lonW);
    printf("     east:   %4d", lonE); PrLon(lonE);
    printf("     input:  %s\n", infn);
    printf("     output: %s\n", outfn);

    /*-----check parameters for errors-----*/
    if ( latS <  -90  ||  latS >  90
      || latN <  -90  ||  latN >  90
      || lonW < -180  ||  lonW > 180
      || lonE < -180  ||  lonE > 180 ) {
        fprintf(ERROUT, "\n*****  error -- one or more of the lat/lon's above is out of range  *****\n");
	return FATAL_ERR;
    }

    if (latS >= latN) {
	fprintf(ERROUT, "\n*****  error -- south boundary is not south of north boundary  *****\n");
	return FATAL_ERR;
    }

    if (lonW == lonE) {
	fprintf(ERROUT, "\n*****  error -- west boundary is equal to east boundary  *****\n");
	return FATAL_ERR;
    }

    if (! strcmp(infn, outfn)) {
	fprintf(ERROUT, "\n*****  error -- input and output files must be different  *****\n");
	return FATAL_ERR;
    }

    /*-----show size (in degrees) of region of interest-----*/
    z = lonE - lonW;
    printf("\nNOTE:  Your region of interest covers %d deg. of latitude and %d deg. of longitude.\n\n",
      latN - latS, (z >= 0) ? z : z + 360);

    /*-----if this is just a check, quit-----*/
    if (check) return OK;

    /*-----open input file (read, binary)-----*/
    if ( ! ( infp = fopen(infn, "rb") ) ) {
        fprintf(ERROUT, "\n*****  error opening input file  *****\n");
        return CANT_OPEN;
    }

    /*-----open output file (read/write, binary)-----*/
    if ( ! ( outfp = fopen(outfn, "wb+") ) ) {
        fprintf(ERROUT, "\n*****  error opening output file  *****\n");
        return CANT_OPEN;
    }

    /*-----read first NPR frame-----*/
    fseek(infp, NPR_HDRS, SEEK_SET);	/* skip NPR headers */
    if (fread(buf, 2, 1, infp) != 1) {	/* read next two bytes */
	fprintf(ERROUT, "\n*****  Error reading first frame  *****\n");
	return BAD_HDRS;
    }

    /*-----check for NPR filler-----*/
    NPR = (buf[0] == 0 && buf[1] == 0);
    if (NPR) {
	SPN = FALSE;
	framesize = NPR_FRAME;
    } else {
	/*-----check for SPN filler-----*/
	fseek(infp, SPN_FRAME - 2, SEEK_SET); /* skip to end of first SPN frame */
	if (fread(buf, 2, 1, infp) != 1) {	/* read last two bytes of it */
	    fprintf(ERROUT, "\n*****  Error reading first frame  *****\n");
	    return BAD_HDRS;
	}
	SPN = (buf[0] == SPN_FILLER && buf[1] == SPN_FILLER);
	framesize = SPN_FRAME;	/* make it nonzero in any case */
    }

    /*-----reset file-----*/
    fseek(infp, 0, SEEK_SET);	 /* return to beginning of file */



/*-----copy non-repeating header blocks-----*/


    /*-----Product ID Block-----*/
    if (ReadValid(ProdID    )) return BAD_HDRS;

    if (memcmp(buf, blk1check, 8)) {
	fprintf(ERROUT, "%s:  invalid ProdID block\n", PROG_NAME);
	return BAD_HDRS;
    }

    satid = 10 * (V1(18) - 48) + V1(19) - 48;
    fcyr = V2(20); fcmon = V1(22); fcday = V1(23);
    fchr = V1(24); fcmin = V1(25);

    if (verbose)
	printf("SatID %d   File date %d/%02d/%02d   time %02d:%02d\n",
	  satid, fcyr, fcmon, fcday, fchr, fcmin);

    if (Copy(     ProdID    )) return WRITE_ERR;


    /*-----Data Sequence Block-----*/
    if (ReadValid(DataSeq   )) return BAD_HDRS;

    scans = V2(14);
    if (verbose)
	printf("Scans %d\n", scans);

    if (Copy(     DataSeq   )) return WRITE_ERR;
    memcpy(datseq, buf, DATSEQ_LEN);	/* save for later updating */


    /*-----Data Description Blocks-----*/
    if (ReadValid(RevHdrDD  )) return BAD_HDRS;
    if (Copy(     RevHdrDD  )) return WRITE_ERR;

    if (ReadValid(ScanHdr1DD)) return BAD_HDRS;
    if (Copy(     ScanHdr1DD)) return WRITE_ERR;

    if (ReadValid(ScanHdr2DD)) return BAD_HDRS;
    if (Copy(     ScanHdr2DD)) return WRITE_ERR;

    if (ReadValid(ScanDD    )) return BAD_HDRS;
    if (Copy(     ScanDD    )) return WRITE_ERR;


    /*-----Rev Header Block-----*/
    if (ReadValid(RevHdr    )) return BAD_HDRS;
    scid = V4(4);   rev  = V4(8);
    bjld = V2(12);  bhr  = V1(14);  bmin = V1(15);  bsec = V1(16);
    ejld = V2(17);  ehr  = V1(19);  emin = V1(20);  esec = V1(21);
    ajld = V2(22);  ahr  = V1(24);  amin = V1(25);  asec = V1(26);
    lsat = V1(27);
    if (verbose) {
	printf("SCID %d   REV# %d\n", scid, rev);
	printf("BJLD %03d   %02d:%02d:%02d\n",
	  bjld, bhr, bmin, bsec);
	printf("EJLD %03d   %02d:%02d:%02d\n",
	  ejld, ehr, emin, esec);
	printf("AJLD %03d   %02d:%02d:%02d   LSAT %d\n",
	  ajld, ahr, amin, asec, lsat);
    }

    ejld0 = ejld;   /* save original ejld */
    ejld = bjld;    /* initialize for output */
    jld  = bjld;    /* initialize current jld */

    if (Copy(     RevHdr    )) return WRITE_ERR;
    memcpy(revhdr, buf, REVHDR_LEN);	/* save for later updating */

    /*-----if NPR, position to end of block-----*/
    if (NPR)
	fseek(infp, framesize, SEEK_SET);



/*----------main loop----------*/
/*----------read and process scans----------*/


    for (;;) {     /* loop until end of file */


	/*---read Scan Header Block 1---*/
	if ((len = GetBlock()) == BAD_LEN)
	    return FATAL_ERR;
	else if (len == EOF_LEN || len == 0 || len == PAST_EOF) {
	    status = END_FILE;
	    break;   /* exit for */
	} else if (len != blklen[ScanHdr1]) {
	    fprintf(ERROUT, "%s:  unexpected block length %d, now @ %08X\n", PROG_NAME, len, ftell(infp));
	    if (SPN && len == len0301) {
		fprintf(ERROUT, "        -- skipping to next frame\n");
		offset = framesize - (ftell(infp) % framesize);
		fseek(infp, offset, SEEK_CUR);
	    }
	    continue;
	} else if (CheckSum(len)) {
	    fprintf(ERROUT, "\n%s:  invalid hdr1 block, now @ %08X\n", PROG_NAME, ftell(infp));
	    continue;
	}

	/*---extract parameters from Scan Header Block 1---*/
	scann = V2(4);
	bst = V4(6);

	if (debug) {
	    printf("%5d", scann);    /* scan number */
	    printf("%17d  ", bst);   /* B-scan start time */
	    printf("hdr1 ");
	}

	/*---save hdr1---*/
	memcpy(hdr1, buf, HDR1_LEN);

	/*---see if julian day needs to be changed---*/
	if (bst < bst0)		 /* if time reverses (crosses midnight), */
	    jld = ejld0;	 /*   set current jld to original ejld */
	bst0 = bst;		 /* keep up with previous bst */



	/*---read Scan Header Block 2---*/
	if ((len = GetBlock()) == BAD_LEN)
	    return FATAL_ERR;
	else if (len == EOF_LEN || len == 0) {
	    status = BAD_EOF;
	    break;   /* exit for */
	} else if (len != blklen[ScanHdr2]) {
	    if (debug) printf("\n");
	    fprintf(ERROUT, "%s:  unexpected block length %d, now @ %08X\n", PROG_NAME, len, ftell(infp));
	    continue;
	} else if (CheckSum(len)) {
	    if (debug) printf("\n");
	    fprintf(ERROUT, "\n%s:  invalid hdr2 block, now @ %08X\n", PROG_NAME, ftell(infp));
	    continue;
	}

	if (debug) printf("hdr2 ");

	/*---save hdr2---*/
	memcpy(hdr2, buf, HDR2_LEN);



	/*---read Scan Data Block---*/
	if ((len = GetBlock()) == BAD_LEN)
	    return FATAL_ERR;
	else if (len == EOF_LEN || len == 0) {
	    status = BAD_EOF;
	    break;   /* exit for */
	} else if (len != blklen[Scan]) {
	    if (debug) printf("\n");
	    fprintf(ERROUT, "\n%s:  unexpected block length %d, now @ %08X\n", PROG_NAME, len, ftell(infp));
	    if (SPN && len == len0681) {
		fprintf(ERROUT, "        -- skipping to next frame\n");
		offset = framesize - (ftell(infp) % framesize);
		fseek(infp, offset, SEEK_CUR);
	    }
	    continue;
	} else if (CheckSum(len)) {
	    if (debug) printf("\n");
	    fprintf(ERROUT, "\n%s:  invalid data block, cksm %04X, now @ %08X\n", PROG_NAME, CheckSum(len), ftell(infp));
	    continue;
	}

	if (debug) printf("data ");
	scans_read++;



	/*---write scan blocks if scan is in ROI---*/
	if (InROI()) {
	    if (debug) printf("- used\n");
	    if (WriteScan(hdr1, hdr2, buf)) {
		fprintf(ERROUT, "%s:  error writing output file\n", PROG_NAME);
		return WRITE_ERR;
	    }

	    /*--and save latest values to use in output header--*/
	    if (oscan == 1) {
		bjld = jld;
		first_bst_used = bst;
	    }
	    ejld = jld;
	    last_bst_used = bst;
	} else {
	    if (debug) printf("- not used\n");
	}

    } /*-----end of for loop to read & process scans-----*/



    /*-----finish output file-----*/
    memcpy(buf, eof_blk, EOF_LEN);     /* write end-of-file */
    if (Copy(EoF)) return WRITE_ERR;   /*    block          */
    if (FixHdr(datseq, revhdr))
	return WRITE_ERR;



    /*-----check status, end program-----*/
    switch (status) {
	case END_FILE:
	    fclose(outfp);
	    ShowStat(scans, scans_read, oscan);
	    return OK;

	case BAD_EOF:
	    fprintf(ERROUT, "%s:  unexpected end-of-file encountered\n", PROG_NAME);
	    ShowStat(scans, scans_read, oscan);
	    return BAD_EOF;
    }
}



/*==================== ShowUsage ====================*/
void ShowUsage()

/*
 * Explain syntax.
 */

{
    fprintf(ERROUT, "Usage:  %s [-c] south north west east infile outfile\n", PROG_NAME);
    fprintf(ERROUT, "        %s -help\n", PROG_NAME);
}



/*==================== PrintHelp ====================*/
void PrintHelp()

/*
 * Print help summary.
 */

{
   printf("\n======================================================================\n\n");
   printf("\t\t%s %s\n\n\n", PROG_NAME, RELEASE_LVL);
   printf("Function:  Filter NESDIS TDR data set, selecting scans\n");
   printf("           within specified latitude-longitude boundaries.\n\n");
   printf("Usage:  %s [-c] south north west east infile outfile\n", PROG_NAME);
   printf("        %s -help\n\n\n", PROG_NAME);
   printf("\t\tOptions:\n\n");
   printf("The -c option (\"check\") is used to check the specified lat-lon\n");
   printf("boundaries without actually performing the filtering; it shows\n");
   printf("how the program interprets the command-line parameters.\n\n");
   printf("The -help option prints this help summary.\n\n\n");
   printf("\t\tLatitude/longitude limits:\n\n");
   printf("Latitude may range from -90 to 90 inclusive, i.e. 90S - 90N.\n");
   printf("Longitude may range from -180 to 180 inclusive, i.e. 180W - 180E.\n\n");
   printf("However, south must be less than north, and west must be unequal\n");
   printf("to east.  (Note:  in the special case where west = -180 and\n");
   printf("   east = 180, the region of interest (ROI) covers all 360 degrees\n");
   printf("   of longitude.  See the third example below.)\n\n");
   printf("North latitude and east longitude are positive integers.\n");
   printf("South latitude and west longitude are negative integers.\n\n\n");
   printf("\t\tFiles:\n\n");
   printf("Input and output file names must be different.  If the output\n");
   printf("file already exists, it will be overwritten.\n\n\n");
   printf("\t\tExamples:\n\n");
   printf("%s 25 50 -125 -65 tdrfile.dat conus.dat\n", PROG_NAME);
   printf("   gets scans from 25N to 50N latitude and 125W to 65W longitude\n\n");
   printf("%s -20 20 150 -150 tdr.dat CEPEX.out\n", PROG_NAME);
   printf("   gets scans from 20S to 20N latitude and 150E to 150W longitude\n\n");
   printf("%s 0 90 -180 180 in.dat Nhemi.dat\n", PROG_NAME);
   printf("   gets scans from northern hemisphere\n\n");
   printf("======================================================================\n\n");
}



/*==================== PrLat ====================*/
void PrLat(int32 lat)

/*
 * Print latitude using conventional (N/S) notation.
 */

{
    if (lat)
	printf("  (%d deg. %c)\n", abs(lat), (lat < 0) ? 'S' : 'N');
    else
	printf("  (equator)\n");
}



/*==================== PrLon ====================*/
void PrLon(int32 lon)

/*
 * Print longitude using conventional (E/W) notation.
 */

{
    if (lon)
	printf("  (%d deg. %c)\n", abs(lon), (lon < 0) ? 'W' : 'E');
    else
	printf("  (0 deg.)\n");
}



/*================================ GetBlock =================================*/
int GetBlock()
/*
 * Get a block if possible, and return the block length.  Otherwise, if initial
 * read is past EOF, return PAST_EOF.  Otherwise, return BAD_LEN.
 */
{
    int len,			 /* block length in bytes */
	len2, 			 /* intermediate variable */
        offset;			 /* offset to next frame, for skipping bad blocks */
    bool bad,			 /* true if block length is bad */
	skip;			 /* true if reading SPN filler */

    /*-----loop until a good block is read-----*/
    do {
	do {
	    /*-----read block length (i.e. number of two-byte words)-----*/
	    if (fread(buf, 2, 1, infp) != 1) {
		if (feof(infp))
		    return PAST_EOF;
		else {
		    fprintf(ERROUT, "%s:  can't read next block length code\n",
			PROG_NAME);
		    return BAD_LEN;
		}
	    }

	    /*-----if an SPN filler was read, skip to next block and read again-----*/
	    skip = (SPN && buf[0] == SPN_FILLER && buf[1] == SPN_FILLER);
	    if (skip) {
		offset = framesize - (ftell(infp) % framesize);
		fseek(infp, offset, SEEK_CUR);
	    }
	} while (skip);

	/*-----convert length to bytes-----*/
	len = V2(0) * 2;

	/*-----bytes remaining to be read-----*/
	len2 = len - 2;

	/*-----is it valid?-----*/
	bad = (len != 0 && (len < 4 || len > BUFSIZE));
	if (bad) {
	    printf("%s:  *****  invalid block length %d, now @ %08X  *****\n",
		PROG_NAME, len, ftell(infp));
	    /*-----no, skip to next frame if this is a NESDIS SPN or NPR file-----*/
	    if (SPN || NPR) {
		printf("        -- skipping to next frame\n");
		offset = framesize - (ftell(infp) % framesize);
		fseek(infp, offset, SEEK_CUR);
		continue;
	    } else {
		printf("        -- aborting\n");
		return BAD_LEN;
	    }
	}

	/*-----read the rest of the block-----*/
	if (len2 > 0) {
	    if (fread(buf + 2, len2, 1, infp) != 1) {
		fprintf(ERROUT, "%s:  can't read next %d bytes\n", PROG_NAME, len2);
		return BAD_LEN;
	    }
	}
    } while (bad);

    /*-----return the block length-----*/
    return len;
}



/*================================ CheckSum =================================*/
uint16 CheckSum(int len)
{
    return Sum(len, buf);
}



/*================================= GenSum ==================================*/
uint16 GenSum(int len, unsigned char *cp)
{
    return ~ Sum(len - 2, cp) + 1;
}



/*=================================== Sum ===================================*/
uint16 Sum(int n, unsigned char *cp)
{
    int i;
    uint16 sm = 0;

    for (i = 0; i < n; i += 2) sm += (uint16) (256L * *(cp+i) + *(cp+i+1));
    return sm;
}



/*================================ ReadValid ================================*/
int ReadValid(int blktype)
/*
 * Read and validate a data block.  Return OK or BAD_HDRS code.
 */
{
    int length = blklen[blktype];

    /*-----read-----*/
    if (fread(buf, length, 1, infp) != 1) {
	fprintf(ERROUT, "%s:  can't read %s block, now @ %08X\n",
	    PROG_NAME, blknam[blktype], ftell(infp));
	return BAD_HDRS;
    }

    /*-----validate-----*/
    if (CheckSum(length)) {
	fprintf(ERROUT, "%s:  invalid %s block, now @ %08X\n",
	    PROG_NAME, blknam[blktype], ftell(infp));
	return BAD_HDRS;
    }

    /*-----return-----*/
    return OK;
}



/*================================== Copy ===================================*/
int Copy(int blktype)
/*
 * Write a data block.  Return OK or WRITE_ERR code.
 */
{
    int length = blklen[blktype];

    /*-----write-----*/
    if (fwrite(buf, length, 1, outfp) != 1) {
	fprintf(ERROUT, "%s:  can't write %s block\n",
	    PROG_NAME, blknam[blktype]);
	return WRITE_ERR;
    }

    /*-----return-----*/
    return OK;
}



/*=============================== WriteScan =================================*/
int WriteScan(unsigned char *hdr1, unsigned char *hdr2, unsigned char *data)
{
    uint16 cksm;	 /* new block checksum */

    /*-----increment output scan counter and store in Scan Header Block 1-----*/
    oscan++;
    EndianCopy(hdr1 + 4, &oscan, 2);

    /*-----fix block checksum-----*/
    cksm = GenSum(HDR1_LEN, hdr1);
    EndianCopy(hdr1 + HDR1_LEN - 2, &cksm, 2);


    /*-----write Scan Header Block 1-----*/
    if (fwrite(hdr1, HDR1_LEN, 1, outfp) != 1) {
	fprintf(ERROUT, "%s:  can't write %s block\n",
	    PROG_NAME, "hdr1");
	return WRITE_ERR;
    }

    /*-----write Scan Header Block 2-----*/
    if (fwrite(hdr2, HDR2_LEN, 1, outfp) != 1) {
	fprintf(ERROUT, "%s:  can't write %s block\n",
	    PROG_NAME, "hdr2");
	return WRITE_ERR;
    }

    /*-----write Data Block-----*/
    if (fwrite(data, DATA_LEN, 1, outfp) != 1) {
	fprintf(ERROUT, "%s:  can't write %s block\n",
	    PROG_NAME, "data");
	return WRITE_ERR;
    }

    /*-----return-----*/
    return OK;
}



/*=============================== InROI ================================*/
bool InROI(void)

/*
 * Check whether scan is in region of interest.
 * 
 * Earth locations (latitude and longitude) are two-byte integers scaled
 * by 100.  There are 128 lat/lon pairs for the A-scan and 128 for the
 * B-scan.  Latitude and longitude are checked at each point in the scan.
 * If any point falls within the ROI, TRUE is returned.
 */

{
    int bb;			 /* offset into buffer */
    int spotL;			 /* low-res spot index */

    for (spotL = 0; spotL < LORES; spotL++) {

	bb = spotL * 52;

	if (SpotCheck(bb +  6)) return TRUE;  /* A-scan first spot */
	if (SpotCheck(bb + 26)) return TRUE;  /* B-scan first spot */
	if (SpotCheck(bb + 36)) return TRUE;  /* A-scan second spot */
	if (SpotCheck(bb + 46)) return TRUE;  /* B-scan second spot */

    }

    return FALSE;
}



/*=============================== SpotCheck ================================*/
bool SpotCheck(int lat_offset)

/*
 * Check whether spot is in ROI; return TRUE or FALSE.
 */

{
    int32 lat = V2(lat_offset);		/* latitude of spot to test */
    int32 lon = V2(lat_offset + 2);	/* longitude of spot to test */

    if (longwrap)
	return (lat >= ZlatS && lat <= ZlatN) && (lon >= ZlonW || lon <= ZlonE);
    else
	return (lat >= ZlatS && lat <= ZlatN) && (lon >= ZlonW && lon <= ZlonE);
}



/*=============================== FixHdr =================================*/
int FixHdr(unsigned char *datseq, unsigned char *revhdr)
{
    uint16 cksm;		 /* new block checksum */
    int16 jd;			 /* intemediate variable */
    int32 z;			 /* intermediate variable */



/*-----fix Data Sequence Block-----*/
    memcpy(buf, datseq, DATSEQ_LEN);	  /* recall Data Sequence Block */

    EndianCopy(buf + 14, &oscan, 2);	  /* copy scan count into block */
    cksm = GenSum(DATSEQ_LEN, buf);	  /* generate new checksum */
    EndianCopy(buf + DATSEQ_LEN - 2, &cksm, 2); /* put cksm into block */

    fseek(outfp, DATSEQ_OFF, SEEK_SET);   /* position at Data Sequence Block */
    if (fwrite(buf, DATSEQ_LEN, 1, outfp) != 1) {	  /* write block */
	fprintf(ERROUT, "%s:  FixHdr can't write DatSeq Block\n", PROG_NAME);
	return WRITE_ERR;
    }



/*-----fix Rev Header Block-----*/
    memcpy(buf, revhdr, REVHDR_LEN);	  /* recall Rev Header Block */

    memset(buf + 8, 0, 4);		  /* zero out rev# */

    /*-----calculate beginning and ending times-----*/
    if (oscan) {
	z = first_bst_used / 10000;
	  bhr  = z / 3600;
	  bmin = (z %= 3600) / 60;
	  bsec = z % 60;

	z = last_bst_used / 10000;
	  ehr  = z / 3600;
	  emin = (z %= 3600) / 60;
	  esec = z % 60;
    } else {
	bjld = bhr = bmin = bsec = 0;
	ejld = ehr = emin = esec = 0;
    }

    /*-----copy days & times into header block-----*/
    jd = (int16) bjld;
    EndianCopy(buf + 12, &jd, 2);
    buf[14] = bhr;
    buf[15] = bmin;
    buf[16] = bsec;

    jd = (int16) ejld;
    EndianCopy(buf + 17, &jd, 2);
    buf[19] = ehr;
    buf[20] = emin;
    buf[21] = esec;

    memset(buf + 22, 0, 5);		  /* zero out equator crossing day/time */

    cksm = GenSum(REVHDR_LEN, buf);	  /* generate new checksum */
    EndianCopy(buf + REVHDR_LEN - 2, &cksm, 2); /* put cksm into block */

    fseek(outfp, REVHDR_OFF, SEEK_SET);   /* position at Rev Header Block */
    if (fwrite(buf, REVHDR_LEN, 1, outfp) != 1) {	  /* write block */
	fprintf(ERROUT, "%s:  FixHdr can't write RevHdr Block\n", PROG_NAME);
	return WRITE_ERR;
    }

    return OK;
}



/*============================== EndianCopy ===============================*/
void EndianCopy(void *to, void *from, int n)
{
    char c;
    char *p1, *p2;

    memcpy(to, from, n);
    if (little_endian) {   /* perform necessary evil */
	for (p1 = to, p2 = p1 + n - 1; p1 < p2; c = *p1, *p1++ = *p2, *p2-- = c)
	    ;
    }
}



/*=============================== ShowStat ==============================*/
void ShowStat(int nominal_in, int read_ok, int selected)
{
    printf("\n\n========================================\n");
    printf("     Nominal input scans . . . %5d\n", nominal_in);
    printf("     Scans read  . . . . . . . %5d\n", read_ok);
    printf("     Scans selected  . . . . . %5d\n", selected);
    printf("========================================\n");
}
