/******************************************************************************

   FILE:  SCANPING.C  (for CODAS3)
   USAGE : SCANPING <control file name>
   INPUT : 1) ping data files
           2) user buffer definition file, if needed
   OUTPUT: 1) output log file
           2) transit fix file, if requested

   Program scans binary ping data files and produces an output file
   listing the header and profile number, recorded profile time and
   the time interval between two consecutive profiles. It also
   flags bad header and bad profiles.

   It is used before loading the ping data files into a CODAS database
   using LOADPING.EXE in order to verify that the ping data files can
   be read and interpreted correctly, the recorded times are correct,
   etc.  The information in the output file can be used to prepare the
   control file for LOADPING.EXE, indicating any time corrections or
   which headers and/or profiles must be skipped due to bad data.

   If the pingdata file is recorded using ADCP (RD-INSTR) together with
   the program MAG1157.EXE, the navigation information will be stored in
   the USER_BUFFER with a specific format. In this case, the user can
   request transit fix information by specifying the type of
   USER_BUFFER. Four types of USER_BUFFER format may be created by
   MAG1157.EXE: (a) and (b) 128-byte type, versions 1280 and 1281; (c)
   102-byte type; and (d) 132-byte type.  In any case, printing the
   transit fix information is useful in obtaining the difference between
   the PC recorded time and the fix time.

   If the user buffer contains the information in ASCII format, the user
   can specify the type of USER_BUFFER as ascii to print out the user
   buffer in an output file.

   If some other program was used to write user buffer in the format
   other than these three kinds of format (128-byte, 102-byte, 132-byte,
   ascii), the user can still obtain a printout of its contents by
   specifying the USER_BUFFER type as "other" and supplying the
   appropriate definition in the user buffer definition file.  It is
   recommended that the user also use FORMAT statements in that file to
   override the default printing format which takes up at least one line
   per element. See the file \CODAS3\DOC\CODAS3.DOC for how to create an
   external structure definition file.)

   The control file for SCANPING.EXE must have the following structure:

   ---------------------------------------------------------------------
     OUTPUT_FILE:         <output file name>
     SHORT_FORM:          <yes|no>
     UB_OUTPUT_FILE:      <user buffer output file name|none>
     USER_BUFFER_TYPE:    <none|1280|1281|1020|1320|2240|720|ascii|other>
     UB_DEFINITION:       <user buffer definition file name|none>
     PINGDATA_FILES:
        <pingdata file 1>
        <pingdata file 2>
           ...

   ---------------------------------------------------------------------

   All uppercase terms in the above are keywords and should never be
   changed.  All items in angular brackets are parameters and must be
   supplied by the user. Following is a brief explanation of each
   keyword and parameter.

     (1) OUTPUT_FILE:
            specifies the output file name.

     (2) SHORT_FORM:
            yes -- will not print out the data types name recorded
                   in each header;
            no  -- otherwise.

            If a lot of headers are in one pingdata file, but few
            profiles are under each header, then selecting "yes" will
            reduce the printing time.

     (3) UB_OUTPUT_FILE:
            specifies the user buffer output file name.
            If "none", no user buffer output file will be created.

            If 102-byte type, 132-byte type, or 128-byte type user
            buffer is used, only the transit fix information will be
            printed in the following format:

             column
               1     --- fix_time (decimal day)
               2     --- longitude (deg)
               3     --- latitude  (deg)
               4     --- elevation (m)
               5     --- iterations
               6     --- dr_dist (m)
               7     --- used
               8     --- (pc_time - fix_time) (sec)

            From column 8, the user can easily calculate the PC clock rate
            to be applied during time correction when loading pingdata
            files into CODAS database using LOADPING.EXE.

            If other type of user buffer is used, all fields of the user buffer
            will be printed, unless FORMAT statements are used to control
            printing.

     (4) USER_BUFFER_TYPE:
            1280 -- 128-byte type user buffer is used;
            1281 -- 128-byte type user buffer is used w/ new arrangement
                    of elements
            1020 -- 102-byte type user buffer is used;
            1320 -- 132-byte type user buffer is used;
                2240 -- 224-byte type user buffer is used;
                 720 --  72-byte type user buffer is used;
           ascii -- ASCII type user buffer is used;
           other -- other type of user buffer;
            none -- No user buffer is used or user buffer is unknown.
                    In this case, no user buffer output file will be created.

     (5) UB_DEFINITION:
            specifies the user buffer definition file name.
            Typing "none" means there is no user buffer definition file to
            be read in.

            The user buffer definition file contains information about the
            structure of the user buffer:  variable names, types,
            units, etc.  If your user buffer conforms to those already
            known to scanping (1280, 1281, 1020, 1320, 2240, 720, ascii) and you
            are running the program on a PC, then there is no need to
            provide a UB_DEFINITION so set it to "none".  Otherwise, if
            you want user buffer output from scanping, you must make your
            own user buffer definition file--see documentation
            \CODAS3\DOC\CODAS3.DOC for details and the files UB*.DEF
            as examples.

     (6) PINGDATA_FILES:
           specifies the names of pingdata files to be scanned.


   -----------
   The examples of a control file, output log file, user buffer definition
   file and user buffer output-transit fix file are shown in
   \CODAS3\ADCP\DEMO\SCAN\:  SCANPING.CNT, A901.SCN, UB*.DEF and
   A901.TRN, respectively.

 --------------------------------------------------------------------------

                 Hui Zhu, University of Hawaii, JIMAR   88/01/15
                                                        88/04/21 (revised)
88/12/03 - EF - Changed to print number of bytes of user buffer recorded.
89/03/28 - EF - Changed to show time intervals even when there is a new header.
89/06/14 - HZ - Changed for CODAS3 version, Control file format and documentation.
90/06/27 - JR - Changed to accommodate 1281-type user buffer.
90/08/20 - JR - Changed to accommodate 132-byte user buffer.
91/08/21 - JR - Fixed bug due to attempting to close the user buffer
                output file when it had not been opened in the first place
                (no ub output option).
91/09/05 - EF - Added new column in logfile output
                for 132-byte user buffer type:  (PC - GPS fix time)
92/10/23 - JR - Added support for 224-byte and 72-byte user buffer
94/10/07 - SS - Added support for 192-byte user buffer
95/07/03 - EF - Added Beam Statistics summary for each file to output file.

******************************************************************************/
#include "geninc.h"
#define FIRST_DEC
#include "readping.h"     /* HEADER FILE FOR LOADPING & SCANPING FUNCTIONS    */
#include "ioserv.h"  /* PARAMETER_LIST_ENTRY_TYPE, get_parameters(), report_msg() */
#include "userbuff.h"    /* TRANSIT_FIX_TYPE, N_REF_AVGS */
#include "vstat.h"

#define TYPE_1280  1
#define TYPE_1020  2
#define TYPE_ASCII 3
#define TYPE_OTHER 4
#define TYPE_1281  5   /* jr+ 6-27-90 */
#define TYPE_1320  6   /* jr+ 8-20-90 */
#define TYPE_2240  7   /* jr+10-22-92 */
#define TYPE_720   8   /* jr+10-22-92 */
#define TYPE_1920  9   /* ss+10-07-94 */

static YMDHMS_TIME_TYPE t_hold = {0, 0, 0, 0, 0, 0};
static int short_form, i_ub_type;
char   ub_name[40], ubdef_name[40], msg[100];
int    print_bottom_msg = 0;

UNISTAT_TYPE bs_stat;
float  bs_float[4];


static NAME_LIST_ENTRY_TYPE short_type[]=
{
   {"no",        0},
   {"yes",       1},
   {NULL,        0}
};

static NAME_LIST_ENTRY_TYPE ub_type[]=
{
   {"none",        0},
   {"1280",        1},
   {"1020",        2},
   {"ascii",       3},
   {"other",       4},
   {"1281",        5},
   {"1320",        6},
   {"2240",        7},
   {"720" ,        8},
   {"1920",        9},
   {NULL,          0}
};

static NAME_LIST_TYPE name_list[]=
{
   short_type,
   ub_type
};

static   PARAMETER_LIST_ENTRY_TYPE parameters[] =
   {
      { " OUTPUT_FILE:",     " %79s", logname,     TYPE_STRING },
      { " SHORT_FORM:",      " %s",   &short_form, TYPE_TRANS },
      { " UB_OUTPUT_FILE:",  " %79s", ub_name,     TYPE_STRING },
      { " USER_BUFFER_TYPE:"," %s",   &i_ub_type,  TYPE_TRANS+256 },
      { " UB_DEFINITION:"   ," %79s", ubdef_name,  TYPE_STRING },
      { " PINGDATA_FILES:",  NULL,    NULL,        0           },
      { NULL,                NULL,    NULL,        0           }
   };                   /* parameters[] */

/* --------------- function declaration --------------------- */
#if PROTOTYPE_ALLOWED
void   check_time(void);
double ymdhms_to_ddtime(YMDHMS_TIME_TYPE *yt);
void   write_transit_summary(FILE *fp, TRANSIT_FIX_TYPE *ub_fix);
void   print_flag(void);
void   flag_print(char *str);
int    output_ub(FILE *fp);
void   check_bottom(void);
#else
void   check_time();
double ymdhms_to_ddtime();
void   write_transit_summary();
void   print_flag();
void   flag_print();
int    output_ub();
void   check_bottom();
#endif
/***************************  MAIN ROUTINE  *************************/
/*                                                                  */
/*  Entry point to this set of routines is from the function do_it, */
/*  which is called from the main program \CODAS\LIB\MAIN_C_S.OBJ   */
/*  (small memory model compilation of \CODAS\IOSERV\MAIN_CNT.C),   */
/*  a general-purpose main program for opening a control file,      */
/*  stripping it of comments prior to processing, and then closing  */
/*  it at the end.                                                  */
/*                                                                  */
/********************************************************************/

#if PROTOTYPE_ALLOWED
void do_it(FILE *fpcnt)
#else
void do_it(fpcnt)
FILE *fpcnt;
#endif
{
   FILE  *fp_ub = NULL;
   USHORT   ub_n;
   char *temp;
   int i;


   if (get_parameters(fpcnt, parameters, name_list)) exit(-1);
        print_parameters(stdout, parameters, name_list, "\n");
   if (i_ub_type)
   {
      if (stricmp(ub_name, "none")) fp_ub = check_fopen(ub_name, "a");
      if (stricmp(ubdef_name, "none") && load_structure_definition(ubdef_name,
         &ub_strdef, &ub_n, (FORMAT_HDR_TYPE **) &temp))
      {
         fprintf(stderr,
            "\n ERROR: Reading UB_DEFINITION FILE %s.\n\n", ubdef_name);
         return;
      }
   }

   fplog = check_fopen(logname, "w");
   set_msg_file(fplog);

   allocate_unistat(&bs_stat, 4, "Beam Statistics", U_VARIANCE | U_EXTREMA);

   while ( fscanf(fpcnt,"%s", bname) == 1 )
   {
      fpb = check_fopen(bname, "rb");
      sprintf(msg,"\n DATA FILE NAME %s \n\n",bname);
      report_msg(msg);
      zero_unistat(&bs_stat);

      initial();
      while ( (file_status = read_record(fpb)) > 0 )
      {
         header_check();
         if (hdr_flag)
         {
            hdr_flag = 0;
            if (file_note_flag) free(file_notes);
            if (get_hdr_block()) goto close;


            if (nhdr_block != 1 && !short_form)
            {
               fprintf(fplog,"\n DATA RECORDED UNDER HEADER %d: ",nhdr_block-1);
               print_flag();
            }
            printf(" Header %d\n",nhdr_block);
            if (nhdr_block == 1 || !short_form)
            {
               fprintf(fplog,
                  " head  prof   date      time       day    interval");
               if (i_ub_type == TYPE_1320 || i_ub_type == TYPE_720 ||
                   i_ub_type == TYPE_2240 || i_ub_type == TYPE_1920)
               {
                  fprintf(fplog, "   pc-fix time");
                  fprintf(fplog, "\n                                          (min:sec)");
                  fprintf(fplog, "      (sec)\n");
               }
               else
                  fprintf(fplog, "\n");
            }
         }
         else
         {
            if (data_flag)
            {
               if(get_data_block()) goto close;
               check_bottom();
               check_time();
               if (user_buffer_flag && i_ub_type)
                   if (output_ub(fp_ub)) goto close;
               if (beam_stats_flag)
               {
                  for (i = 0; i < 4; i++)    bs_float[i] = beam_stats[i];
                  update_unistat(&bs_stat, bs_float, 4);
               }
            }
            data_flag = 0;
            if (user_buffer_flag) free(user_buffer_block);
         }
         if (bad_data_flag)
            fprintf(fplog," WARNING: bad data found in HDR %d PRF %d\n",
               nhdr_block,nprof_in_hdr);
         start_blk = 0;
         bad_data_flag = 0;

      }

      if (file_status == 0) {
         if(!short_form) {
           fprintf(fplog,"\n DATA RECORDED UNDER HEADER %d: ",nhdr_block);
           print_flag();
           }
         if (beam_stats_flag)
         {
            calculate_unistat(&bs_stat);
            write_unistat(fplog, &bs_stat, 1.0, NULL, "beam", 0);
         }
         sprintf(msg,"\n END OF FILE: %s\n\n",bname);
         }
      else
         sprintf(msg," ERROR: reading file %s.\n\n",bname);
      report_msg(msg);
      fclose(fpb);
   }
close:
   if (fp_ub) fclose(fp_ub);
   if (fplog) fclose(fplog);
   free_unistat(&bs_stat);
}

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

#if PROTOTYPE_ALLOWED
void check_time(void)
#else
void check_time()
#endif
{
   /* unsigned */ long delt;
   int m = 0, s = 0;

   if (t_hold.year)
   {
      delt = TIMDIF(&t_hold,&prof_time);
      m = delt/60;
      s = delt%60;
   }

   fprintf(fplog, "%5d", nhdr_block);
   fprintf(fplog, " %5d", nprof_in_hdr);
   fprintf(fplog, "  %04d/%02d/%02d ", prof_time.year, prof_time.month,
      prof_time.day);
   fprintf(fplog, " %02d:%02d:%02d  %f  %2d:%02d",
      prof_time.hour, prof_time.minute, prof_time.second,
      ymdhms_to_ddtime(&prof_time), m, s);
   t_hold = prof_time;

   /* 2001/07/15 Pierre Jaccard */
#  include "../../gfi/src/plugs/scanping.c"

   if (i_ub_type != TYPE_1320 && i_ub_type != TYPE_720 &&
       i_ub_type != TYPE_2240 && i_ub_type != TYPE_1920)
      fprintf(fplog, "\n");
}

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

#if PROTOTYPE_ALLOWED
void print_flag(void)  /* read data block */
#else
void print_flag()  /* read data block */
#endif
{
   if (record_RPHT_stat_flag) flag_print("RPHT_stat   ");
   if (velocity_flag)         flag_print("VEL   ");
   if (amplitude_flag)        flag_print("AMP   ");
   if (perc_good_flag)        flag_print("PERCENT_GOOD   ");
   if (bottom_track_flag)
   {
      flag_print("NAV_DATA   ");
      if (bottom_flag) flag_print("BOTTOM_VEL   ");
   }
   if (user_buffer_flag)
   {
      flag_print("");
      fprintf(fplog,"USER_BUFFER_%d_bytes   ",user_buffer_count);
   }
   if (CTD_data_flag)         flag_print("CTD_data   ");
   if (spw_data_flag)         flag_print("spw_data   ");
   if (raw_doppler_flag)      flag_print("raw_doppler_data   ");
   if (raw_amplitude_flag)    flag_print("raw_amplitude_data   ");
   if (raw_spw_data_flag)     flag_print("raw_spw_data   ");
   if (three_beam_flag)       flag_print("perc_3_beam_sol   ");
   if (beam_stats_flag)       flag_print("beam_stat   ");
   if (standard_dev_flag)     flag_print("std_dev   ");
   fprintf(fplog,"\n\n ");
}

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

#if PROTOTYPE_ALLOWED
void flag_print(char *str)
#else
void flag_print(str)
char *str;
#endif
{
   static int i = 0;  /* keeps track of no. flags printed so far */

   if (i % 6 == 0) fprintf(fplog,"\n   ");
   fprintf(fplog,"%s",str);
   i++;
}

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

#if PROTOTYPE_ALLOWED
void write_transit_summary(FILE *fp_out, TRANSIT_FIX_TYPE *fix)
#else
void write_transit_summary(fp_out, fix)
FILE *fp_out;
TRANSIT_FIX_TYPE *fix;
#endif
{
   double ddtime;

   if (fix->sat_id)
   {
      ddtime = fix->fix_time / 86400.0;
      fprintf(fp_out, "%13.7f  %13.7f %13.7f  %3d %2d %5.2f %2d  %ld\n",
                       ddtime,
                       fix->lon/360000.0,
                       fix->lat/360000.0,
                       fix->elevation,
                       fix->iterations,
                       fix->dr_dist / 100.0,
                       fix->used,
                       fix->pc_time - fix->fix_time);
   }
}

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

#if PROTOTYPE_ALLOWED
double ymdhms_to_ddtime(YMDHMS_TIME_TYPE *yt)
#else
double ymdhms_to_ddtime(yt)
YMDHMS_TIME_TYPE *yt;
#endif
{
   return(year_day(yt, yt->year));
}

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

#if PROTOTYPE_ALLOWED
void check_bottom(void)
#else
void check_bottom()
#endif
{
   if (bottom_flag && print_bottom_msg == 0)
   {
      print_bottom_msg = 1;
      fprintf(fplog,"---- bottom velocity is ON\n");
   }
   else if (bottom_flag == 0 && print_bottom_msg == 1)
   {
      print_bottom_msg = 0;
      fprintf(fplog,"---- bottom velocity is OFF\n");
   }
   return;
}

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

#if PROTOTYPE_ALLOWED
int  output_ub(FILE *fp)
#else
int  output_ub(fp)
FILE *fp;
#endif
{
   TRANSIT_FIX_TYPE ub_fix;
   char *tr_fix;
   int i, cr;
   LONG gps_time, p_time_seconds, tdif;
   USER_BUFFER_2240_TYPE *u;

#define ubb user_buffer_block
#if (HOST_ENVIRONMENT != PC_COMPATIBLE_HOST)
   char *buff;
   if (i_ub_type != TYPE_ASCII)
   {
      if ( (buff = malloc(user_buffer_count)) == NULL ) return(1);
      if (convert_select(buff, (char *)ubb, PC_COMPATIBLE_HOST, HOST_ENVIRONMENT,
         -user_buffer_count, STRUCT_VALUE_CODE, "USER_BUFFER", ub_strdef)
         == BADUINT) return(1);
      move_byte(buff, (char *)ubb, user_buffer_count);
      free(buff);
   }
#endif
   switch(i_ub_type)
   {
      case TYPE_1280:
      case TYPE_1020:  /* need to swap bytes */
         if (fp)
         {
            tr_fix = (i_ub_type == TYPE_1280) ?
               (char *)(ubb + sizeof(SHORT) * (8+2*N_REF_AVGS) + sizeof(LONG)) :
               (char *)(ubb + sizeof(SHORT) * (7+2*N_REF_AVGS) + sizeof(LONG)) ;
            move_byte(tr_fix    , (char *) &(ub_fix.ref_u), 3 * sizeof(SHORT));
            move_byte(tr_fix+34 , (char *) &(ub_fix.sat_id), sizeof(SHORT));
            move_byte(tr_fix+ 6 , (char *) &(ub_fix.pc_time), 5 * sizeof(LONG));
            move_byte(tr_fix+26 , (char *) &(ub_fix.dr_dist), 4 * sizeof(SHORT));
            move_byte(tr_fix+36 , (char *) &(ub_fix.used), 8 * sizeof(BYTE));
            write_transit_summary(fp, &ub_fix);
         }
         break;
      case TYPE_1281:
         if (fp)
         {
            tr_fix = (char *)(ubb + sizeof(SHORT) * (8+2*N_REF_AVGS));
            write_transit_summary(fp, (TRANSIT_FIX_TYPE *) tr_fix);
         }
         break;
      case TYPE_1320:
         if (fp)
         {
            tr_fix = (char *)( ubb + sizeof(SHORT) * (8+2*N_REF_AVGS_1320) +
               (N_REF_AVGS_1320 + 1) * sizeof(L_FIX_TYPE) );
            write_transit_summary(fp, (TRANSIT_FIX_TYPE *) tr_fix);
         }

         gps_time = *((long *)( ubb + sizeof(SHORT) * (8+2*N_REF_AVGS_1320) +
            (N_REF_AVGS_1320 + 1) * sizeof(L_FIX_TYPE)
            + sizeof(TRANSIT_FIX_TYPE)));
         if (gps_time != BADLONG)
         {
            p_time_seconds = prof_time.second + 60L *
               (prof_time.minute + 60L * prof_time.hour);
            tdif = p_time_seconds - gps_time;
            fprintf(fplog, "  %10ld\n", tdif);
         }
         else
            fprintf(fplog, "       99999\n");
         break;
      case TYPE_2240:
      case TYPE_720:
      case TYPE_1920:
         p_time_seconds = prof_time.second + 60L * (prof_time.minute + 60L * prof_time.hour);
         u = (USER_BUFFER_2240_TYPE *) ubb;
         switch(u->fix[1].msg_type)
         {
            case GGA_FIX:
            case PBN_FIX:
               fprintf(fplog, "  %10ld\n", p_time_seconds - u->fix[1].gps_seconds);
               break;
            case MSG8_FIX:
               fprintf(fplog, "  %10ld\n", p_time_seconds - (u->fix[1].gps_seconds % SECONDS_PER_DAY));
               break;
            default:
               fprintf(fplog, "       99999\n");
               break;
         }
         break;
      case TYPE_ASCII:
         if (fp)
         {
            fprintf(fp,
               "\nPROFILE TIME:  %f ddays (%04hd/%02hd/%02hd  %02hd:%02hd:%02hd)\n",
               ymdhms_to_ddtime(&prof_time),
               prof_time.year, prof_time.month, prof_time.day,
               prof_time.hour, prof_time.minute, prof_time.second);
            cr = 1;
            for (i = 0; i < user_buffer_count; i++)
            {
               if (ubb[i] != ' ' && cr == 1) cr = 0;
               if (ubb[i-1] == 0x0d && ubb[i] == 0x0a) cr = 1;
               if (ubb[i] != 0x0d) fprintf(fp, "%c", ubb[i]);
            }
            if (cr == 0) fprintf(fp,"\n");  /* avoid printing CR twice */
         }
         break;
      default:
         if (fp)
           print_structure(fp, (char *) ubb, "USER_BUFFER", user_buffer_count, ub_strdef);
         break;
   }
   return(0);
}


