// Copyright 2003 Regents of the University of California

// Astropulse 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, or (at your option) any later
// version.

// Astropulse 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 Astropulse; see the file COPYING.  If not, write to the Free Software
// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.

/* ap_fileio.C */
/* Functions for reading data, etc for AstroPulse client */
#include "ap_config.h"

#ifdef _WIN32
#include "windows.h"
#endif

#include <cstdio>
#include <cstdlib>
#include <vector>
#include <string>
#include <iostream>
#include <sstream>
#include <algorithm>

#include "parse.h"
#include "xml_util.h"
#include "db_table.h"
#include "ap_schema.h"
#include "astropulse.h"
#include "util.h"
#undef read

#include <cstring>
#include <cassert>
#include <cstdlib>
#include <cstdio>
#include <cerrno>
#ifdef HAVE_UNISTD_H
#include <unistd.h>
#endif


namespace Astropulse {
const int AP_OUTPUT_PROTOCOL_VERSION = 3;

// Filenames
const char* WU_FNAME    = "in.dat";
const char* OUT_FNAME   = "pulse.out";
const char* STATE_FNAME = "ap_state.dat";
const char* FOLD_FNAME  = "fold.dat";

inline size_t get_file_size(const char* filename) {
  FILE* f = boinc_fopen(filename, "r");
  if (!f) return 0;
  fseek(f, 0, SEEK_END);
  size_t size = ftell(f);
  fclose(f);
  return size;
}

inline bool file_exists_p(const char* filename) {
  FILE* f = boinc_fopen(filename, "r");
  if (f) {
    fclose(f);
    return true;
  } else {
    return false;
  }
}

void read_signal_vector(std::vector<ap_signal> &signal_vector) {
  client.status = "Reading signal vector.";

  string resolved_filename;
  /* See Outfile::init for discussion */
  boinc_resolve_filename_s(OUT_FNAME, resolved_filename);

  bool signal_file_exists = file_exists_p(resolved_filename.c_str());
  std::ifstream signal_file(resolved_filename.c_str(), std::ifstream::in);
  if(!signal_file) {
    printf("No signal file %s, signal vector will remain empty\n", resolved_filename.c_str());
    // fprintf(stderr, "Error opening signal file %s: %s\n", resolved_filename.c_str(), strerror(errno));
    // exit(FOPEN_ERR);
    return;
  }

  ap_signal s;
  s.time_series.encoding=_x_hex;

  while(!signal_file.eof()) {
    signal_file >> s;
    // if(is_blank(s)) break;
    if(signal_file.eof()) break;
    signal_vector.push_back(s);
  }

  signal_file.close();
}

bool is_blank(ap_signal s) {
  if(s.dm == 0 && s.peak_bin == 0 && s.scale == 0)
    return 1;
  else
    return 0;
}

/* Add signal s to vector signal_vector.
 * If another signal in signal_vector already has the same
 * peak_bin as the signal s, then we choose whichever of the two
 * signals has the larger peak_power.
 */
bool add_signal(std::vector<ap_signal> &signal_vector, ap_signal s) {
  std::vector<ap_signal>::iterator iter;
  iter = signal_vector.begin();
  while(iter != signal_vector.end()) {
    if(similar_ap_signals(*iter, s)) {
      // Choose between the two signals, based on power level.
      if( (s.period == 0 && (s.peak_power > (*iter).peak_power)) ||
          (s.period != 0 && (s.num_std_devs > (*iter).num_std_devs))  ) {
        *iter = s;
      }
      return false;
    }
    iter++;
  }
  signal_vector.push_back(s);
  return true;
}

bool similar_ap_signals(ap_signal s1, ap_signal s2) {
  if(s1.period == 0 && s2.period == 0 &&
    s1.scale == s2.scale &&
    s1.peak_bin == s2.peak_bin) {
     return true;
  }
  if(s1.period != 0 && s2.period != 0 && 
    s1.scale == s2.scale &&
    s1.ffa_scale == s2.ffa_scale &&
    fabs(1 - s2.period / s1.period) < 0.01) {
     return true;
  }
  return false;
}

void Outfile::init() {
  client.status = "Opening output file.";

  string resolved_filename;
  /* boinc API.  finds the resolved filename (physical filename)
   * corresponding to the logical filename OUT_FNAME.
   * The association between the two is established in pulse_split.C,
   * by the boinc API function create_work.  (I think.)
   */
  boinc_resolve_filename_s(OUT_FNAME, resolved_filename);

  // size_t size = get_file_size(resolved_filename.c_str());
  bool outfile_exists = file_exists_p(resolved_filename.c_str());

  if (outfile.open(resolved_filename.c_str(), "w")) {
    fprintf(stderr, "Error opening outfile %s: %s\n", resolved_filename.c_str(), strerror(errno));
    exit(FOPEN_ERR);
  }

  // consistency checking
  if (client.statefile.resumed) {
    /* This is okay now, because Outfile::init gets called inside of
     * Outfile::write_main, so there may be no outfile yet.
    if (!outfile_exists) {
      fprintf(stderr,
              "Error resuming: statefile exists, but output file doesn't exist.\n");
      exit(FREAD_ERR);
    }
    */
  } else {
    /* I don't see how this is inconsistent.  The output file gets
       created first, and there is a pause of 1-2 seconds before the
       statefile gets created.  So it's entirely possible for the
       program to crash or quit during that 1-2 seconds.
    if (outfile_exists) {
      fprintf(stderr,
              "Error initializing: statefile doesn't exist, but output file exists.\n");
      exit(FREAD_ERR);
    }
    */

    outfile.flush();
  }
}

// Read in state file to resume computation
void Statefile::Read() {
  client.status = "Reading state file.";
  // Checking for blank statefile
  // printf("At Statefile::Read()\n");
  // fprintf(stderr, "At Statefile::Read()\n");

  int num_iter = 0;
  // Loop up to 100 times
  while(1) {
    FILE* statefile = boinc_fopen(STATE_FNAME, "rb");
    if (statefile == NULL) return;
    int nread = fread(&client.ap_shmem->ap_gdata.state, 1, sizeof(state_t), statefile);
    // fclose(statefile);
    // exit(0);
    if (nread != sizeof(state_t)) {
      fprintf(stderr,
              "Error reading from statefile: wanted %ld bytes, got %ld\n",
              sizeof(state_t), nread
             );
      exit(FREAD_ERR);
    }
    fclose(statefile);
    if((client.ap_shmem->ap_gdata.state).dm_chunk_large == 0 && num_iter < 100) {
      num_iter++;
      printf("In ap_fileio.cpp, Statefile::Read, statefile is 0'd, trying again: iteration %d\n", num_iter);
      fprintf(stderr, "In ap_fileio.cpp, Statefile::Read, statefile is 0'd, trying again: iteration %d\n", num_iter);
      boinc_sleep(5);
      continue;
    } else if((client.ap_shmem->ap_gdata.state).dm_chunk_large == 0 && num_iter >= 100) {
      
       // How many times have we done this? 
       int num_exit;
       FILE* zsl = boinc_fopen("zeroed_statefile_log.txt", "r");
       if(!zsl) { num_exit = 0; }
       else { 
	  fscanf(zsl, "%d", &num_exit); 
	  fclose(zsl);
       }
       num_exit++;
       zsl = boinc_fopen("zeroed_statefile_log.txt", "w"); 
       fprintf(zsl, "%d", num_exit);
       fclose(zsl);

       fprintf(stderr, "Statefile::Read: %d iterations with 0'd statefile.\n", num_exit);
       if(num_exit >= 3) {
	  fprintf(stderr, "Exiting with error.\n");
          exit(-389); // arbitrarily chosen number
       }
       
       // Removing statefile and outfile, starting over.\n");
       if(file_exists_p(STATE_FNAME) && (remove(STATE_FNAME) != 0) ) {
         fprintf(stderr, "Error: could not remove statefile.\n"); 
	 exit(-389);
       }
       string resolved_filename;
       boinc_resolve_filename_s(OUT_FNAME, resolved_filename);
       if(file_exists_p(resolved_filename.c_str())
	    && (remove(resolved_filename.c_str()) != 0) ) {
         fprintf(stderr, "Error: could not remove outfile.\n");
	 exit(-389);
       }
       exit(0);
    } else {
      break;
    }
  }
  resumed = true;
  if (boinc_is_standalone()) {
    fprintf(stderr, "## Warning: resuming in standalone mode\n");
  }

  return;
}

// Write state file so we can resume later
void Statefile::write() {
  client.status = "Saving computation state.";

  FILE* statefile = boinc_fopen(STATE_FNAME, "wb");
  if (statefile == NULL) {
    fprintf(stderr, "Error opening statefile for write: %s.\n", strerror(errno));
    exit(FOPEN_ERR);
  }

  // Checking for blank statefile
  if((client.ap_shmem->ap_gdata.state).dm_chunk_large == 0) {
    printf("In Statefile::write(), printing 0'd state.\n");
    fprintf(stderr, "In Statefile::write(), printing 0'd state.\n");
    (client.ap_shmem->ap_gdata.state).print();
  }
  int nwrite = fwrite(&client.ap_shmem->ap_gdata.state, sizeof(state_t), 1, statefile);
  if (nwrite != 1) {
    fprintf(stderr, "Error writing to statefile: %s.\n", strerror(errno));
    exit(FWRITE_ERR);
  }

  fclose(statefile);
}

void Foldfile::write() {
  client.status = "Saving fold buffer.";

  FILE* foldfile = boinc_fopen(FOLD_FNAME, "wb");
  if (foldfile == NULL) {
    fprintf(stderr, "Error opening foldfile for write: %s.\n", strerror(errno));
    exit(FOPEN_ERR);
  }
  
  int nwrite = fwrite(&(client.fold_buf_long_pos[0]), sizeof(float)*client.ap_shmem->ap_gdata.state.fold_buf_size_long, 1, foldfile);
  if (nwrite != 1) {
    fprintf(stderr, "Error writing long fold buffer to foldfile: %s.\n", strerror(errno));
    exit(FWRITE_ERR);
  }

  nwrite = fwrite(&(client.fold_buf_long_neg[0]), sizeof(float)*client.ap_shmem->ap_gdata.state.fold_buf_size_long, 1, foldfile);
  if (nwrite != 1) {
    fprintf(stderr, "Error writing long fold buffer to foldfile: %s.\n", strerror(errno));
    exit(FWRITE_ERR);
  }
  
  nwrite = fwrite(&(client.fold_buf_short_pos[0]), sizeof(float)*client.ap_shmem->ap_gdata.state.fold_buf_size_short, 1, foldfile);
  if (nwrite != 1) {
    fprintf(stderr, "Error writing short fold buffer to foldfile: %s.\n", strerror(errno));
    exit(FWRITE_ERR);
  }

  nwrite = fwrite(&(client.fold_buf_short_neg[0]), sizeof(float)*client.ap_shmem->ap_gdata.state.fold_buf_size_short, 1, foldfile);
  if (nwrite != 1) {
    fprintf(stderr, "Error writing short fold buffer to foldfile: %s.\n", strerror(errno));
    exit(FWRITE_ERR);
  }

  fclose(foldfile);
}

// Read saved folding array
int Foldfile::Read() {
  client.status = "Reading fold buffer.";

  FILE* foldfile = boinc_fopen(FOLD_FNAME, "rb");
  if (foldfile == NULL) {
    return(1);
  }

  int nread = fread(&(client.fold_buf_long_pos[0]), sizeof(float), client.ap_shmem->ap_gdata.state.fold_buf_size_long, foldfile);
  if (nread != client.ap_shmem->ap_gdata.state.fold_buf_size_long) {
    fprintf(stderr, "Error reading from foldfile for long_pos: wanted %ld bytes, but read only %ld bytes.\n", sizeof(float)*client.ap_shmem->ap_gdata.state.fold_buf_size_long, nread);
    return(1);
  }
  
  nread = fread(&(client.fold_buf_long_neg[0]), sizeof(float), client.ap_shmem->ap_gdata.state.fold_buf_size_long, foldfile);
  if (nread != client.ap_shmem->ap_gdata.state.fold_buf_size_long) {
    fprintf(stderr, "Error reading from foldfile for long_neg: wanted %ld bytes, but read only %ld bytes.\n", sizeof(float)*client.ap_shmem->ap_gdata.state.fold_buf_size_long, nread);
    return(1);
  }
  
  nread = fread(&(client.fold_buf_short_pos[0]), sizeof(float), client.ap_shmem->ap_gdata.state.fold_buf_size_short, foldfile);
  if (nread != client.ap_shmem->ap_gdata.state.fold_buf_size_short) {
    fprintf(stderr, "Error reading from foldfile for short_pos: wanted %ld bytes, but read only %ld bytes.\n", sizeof(float)*client.ap_shmem->ap_gdata.state.fold_buf_size_short, nread);
    return(1);
  }
  
  nread = fread(&(client.fold_buf_short_neg[0]), sizeof(float), client.ap_shmem->ap_gdata.state.fold_buf_size_short, foldfile);
  if (nread != client.ap_shmem->ap_gdata.state.fold_buf_size_short) {
    fprintf(stderr, "Error reading from foldfile for short_neg: wanted %ld bytes, but read only %ld bytes.\n", sizeof(float)*client.ap_shmem->ap_gdata.state.fold_buf_size_short, nread);
    return(1);
  }

  fclose(foldfile);
  return(0);
}

void Wufile::init() {
  client.status = "Opening WU file.";
  string resolved_filename;
  /* boinc API.  See discussion in Outfile::init */
  boinc_resolve_filename_s(WU_FNAME, resolved_filename);
  wufile.open(resolved_filename.c_str(),std::ios_base::binary);
  if (!wufile) {
    fprintf(stderr, "Error opening wufile %s: %s\n", resolved_filename.c_str(), strerror(errno));
    exit(FOPEN_ERR);
  }
}

// This parses the wuheader into client.wuheader, which
// is a temporary storage location.  It is used
// 
void Wufile::parse_header_for_writeout() {
  workunit_header &wuheader(client.wuheader);

  wufile.seekg(0, std::ios::beg);
  wufile >> wuheader;

  if (!wufile) {
    fprintf(stderr, "Error parsing WU header.\n");
    exit(PARSE_ERR);
  }
}

// Parse header from wufile
void Wufile::parse_header_to_state() {
  workunit_header &wuheader(client.wuheader);

  state_t& state = client.ap_shmem->ap_gdata.state;

  static const char *locations[]= {"Arecibo 403MHz Line Feed",
                                   "Arecibo 1.42GHz Flat Feed",
                                   "Arecibo 1.42GHz Gregorian Feed",
                                   "Unknown"
                                  };
  int s4_id=3;
  s4_id=wuheader.receiver_cfg->s4_id;
  if (s4_id>2) {
    locations[0]=client.wuheader.receiver_cfg->name;
    s4_id=0;
  }

  strncpy(state.recvname,locations[s4_id],sizeof(state.recvname));
  strncpy((char *) state.wuname,wuheader.name,sizeof(state.wuname));
  state.datasize=wuheader.data_desc.nsamples*wuheader.recorder_cfg->bits_per_sample/8;
  state.max_coadd=wuheader.analysis_cfg->max_coadd;
  state.dm_low=wuheader.analysis_cfg->dm_low;
  state.dm_hi=wuheader.analysis_cfg->dm_high;
  state.dm_chunk_large=wuheader.analysis_cfg->dm_chunk_large;
  state.dm_chunk_small=wuheader.analysis_cfg->dm_chunk_small;
  state.fold_buf_bytes_short=wuheader.analysis_cfg->fold_buf_bytes_short;
  state.remove_radar=wuheader.analysis_cfg->remove_radar;
  state.fft_len=wuheader.analysis_cfg->fft_len;
  state.remove_radar=wuheader.analysis_cfg->remove_radar;
  state.high_pass=wuheader.analysis_cfg->high_pass;
  state.ffa_threshold=wuheader.analysis_cfg->ffa_threshold;
  state.ra0=wuheader.data_desc.start.ra;
  state.dec0=wuheader.data_desc.start.dec;
  state.jd0=wuheader.data_desc.start.time;
  state.ra1=wuheader.data_desc.end.ra;
  state.dec1=wuheader.data_desc.end.dec;
  state.jd1=wuheader.data_desc.end.time;
  for (unsigned int i=0; i<wuheader.analysis_cfg->thresholds.size(); i++) {
    state.thresh[wuheader.analysis_cfg->thresholds[i].coadds]=
      wuheader.analysis_cfg->thresholds[i].threshold;
  }

  // Check for inconsistent/bad values
  if (state.fft_len > state.datasize*4) {
    fprintf(stderr, "Error parsing WU header: fft_len=%d > datasize=%d * 4\n",
            state.fft_len, state.datasize);
    exit(PARSE_ERR);
  }
  if (state.dm_hi <= state.dm_low) {
    fprintf(stderr, "Error parsing WU header: dm_hi=%d <= dm_low=%d\n",
            state.dm_hi, state.dm_low);
    exit(PARSE_ERR);
  }
  if (state.max_coadd > MAX_COADD) {
    fprintf(stderr, "Error parsing WU header: max_coadd=%d > %d\n",
            state.max_coadd, MAX_COADD);
    exit(PARSE_ERR);
  }
  if ((state.dm_chunk_large>>state.max_coadd) > 1) {
    fprintf(stderr, "Error parsing WU header: (dm_chunk_large=%d) >> (max_coadd=%d) > 1\n",
            state.dm_chunk_large, state.max_coadd);
    exit(PARSE_ERR);
  }
  if ((state.dm_chunk_small>>state.max_coadd) > 1) {
    fprintf(stderr, "Error parsing WU header: (dm_chunk_small=%d) >> (max_coadd=%d) > 1\n",
            state.dm_chunk_small, state.max_coadd);
    exit(PARSE_ERR);
  }
  if (state.remove_radar != 0 && state.remove_radar != 1) {
    fprintf(stderr, "Error parsing WU header: remove_radar=%d != 0 or 1\n",
             state.remove_radar);
    exit(PARSE_ERR);
  }  
  if (state.fold_buf_bytes_short <= 0) {
    fprintf(stderr, "Error parsing WU header: fold_buf_bytes_short=%d < 0\n",
             state.fold_buf_bytes_short);
    exit(PARSE_ERR);
  }
  if (state.fft_len < 0) {
    fprintf(stderr, "Error parsing WU header: fft_len=%d < 0\n",
            state.fft_len);
    exit(PARSE_ERR);
  }
  if (state.datasize < 0) {
    fprintf(stderr, "Error parsing WU header: datasize=%d < 0\n",
            state.datasize);
    exit(PARSE_ERR);
  }
  if (state.max_coadd < 0) {
    fprintf(stderr, "Error parsing WU header: max_coadd=%d < 0\n",
            state.max_coadd);
    exit(PARSE_ERR);
  }
  if (state.dm_hi < 0) {
    fprintf(stderr, "Error parsing WU header: dm_hi=%d < 0\n",
            state.dm_hi);
    exit(PARSE_ERR);
  }
  if (state.dm_low < 0) {
    fprintf(stderr, "Error parsing WU header: dm_low=%d < 0\n",
            state.dm_low);
    exit(PARSE_ERR);
  }

  // client.outfile.write_header();
}

// returns true on success
inline bool find_start_tag(FILE* file, const char* tag) {
  rewind(file);
  char buf[256];
  while (fgets(buf, 256, file)) {
    if (match_tag(buf, tag)) return true;
  }
  return false;
}

void Wufile::read_raw_data() {
  char buf[4096];
  client.status = "Reading raw data.";
  std::string data_chunk;
  data_chunk.reserve(client.ap_shmem->ap_gdata.state.datasize+1024);
  wufile.unsetf(std::ios_base::skipws);
  while (!wufile.eof()) {
    wufile.read(buf,4096);
    data_chunk+=std::string(buf,wufile.gcount());
  }
  client.rawdata=xml_decode_field<unsigned char>(data_chunk,(const char *)"data");
  if (client.rawdata.size() != client.ap_shmem->ap_gdata.state.datasize) {
    fprintf(stderr, "Error reading raw data: Expected %ld bytes, read %ld.\n", client.ap_shmem->ap_gdata.state.datasize, client.rawdata.size());
    exit(FREAD_ERR);
  }
}

void Wufile::finish() {
  wufile.close();
}

// encode the time series as a ASCII integers

void Outfile::write_header() {
  outfile.printf("<astropulse_output>\n");
  std::string s(client.wuheader.print_xml());
  outfile.printf("%s",s.c_str());
  outfile.printf("<fraction_blanked>%f</fraction_blanked>\n",
		 (double) client.ap_shmem->ap_gdata.state.numSamplesBlanked / 
                        NUM_SAMPLES);
  outfile.printf("<pulses>\n");
}

void Outfile::output_result(const ap_signal &sig) {
  std::string s(sig.print_xml(0,0,1));
  outfile.printf("%s",s.c_str());
  // REMINDER: do not flush here; only do so at checkpoint.  All state
  // files have to be consistent in case of crash.
}

void Outfile::write_main(std::vector<ap_signal> signal_vector) {
  if(outfile.tell() != -1) 
    outfile.close();
  init();
  write_header();
  std::vector<ap_signal>::iterator iter1;
  iter1 = signal_vector.begin();
  while(iter1 != signal_vector.end()) {
    output_result(*iter1);
    iter1++;
  }
  outfile.flush();
  outfile.close();

} 

// write best results for each coadd level (we do this for the sake of
// the validator).
void Outfile::finish_results() {

  if(outfile.tell() != -1) 
    outfile.close();

  string resolved_filename;
  /* See Outfile::init for discussion */
  boinc_resolve_filename_s(OUT_FNAME, resolved_filename);
  
  if (outfile.open(resolved_filename.c_str(), "a")) {
    fprintf(stderr, "Error opening outfile %s: %s\n", resolved_filename.c_str(), strerror(errno));
    exit(FOPEN_ERR);
  }

  outfile.printf("</pulses>\n");
  outfile.printf("<best_pulses>\n");
  state_signal *best=client.ap_shmem->ap_gdata.state.best;
  for (int l = 0; l < client.ap_shmem->ap_gdata.state.max_coadd; ++l) {
    assert(best[l].peak_power > -INFINITY);
    std::string s((ap_signal(best[l])).print_xml(0,0,1));
    outfile.printf("%s",s.c_str());
  }
  outfile.printf("</best_pulses>\n");
  outfile.printf("</astropulse_output>\n");
  outfile.flush();
  outfile.close();
}

void Outfile::finish() {
  outfile.flush();
  outfile.close();
}

void Statefile::finish() {
  unlink(STATE_FNAME);
}

void Foldfile::finish() {
  unlink(FOLD_FNAME);
}

void state_t::print() {
  fprintf(stderr, "\n*****************state file ********************\n");
  fprintf(stderr, "char recvname[64]: %s\n", recvname);
  fprintf(stderr, "char wuname[64]: %s\n", wuname);
  fprintf(stderr, "int datasize: %d\n", datasize);
  fprintf(stderr, "int dm_low: %d\n", dm_low);
  fprintf(stderr, "int dm_hi: %d\n", dm_hi);
  fprintf(stderr, "int dm_chunk_large: %d\n", dm_chunk_large);
  fprintf(stderr, "int dm_chunk_small: %d\n", dm_chunk_small);
  fprintf(stderr, "int fft_len: %d\n", fft_len);
  fprintf(stderr, "float high_pass: %f\n", high_pass);
  fprintf(stderr, "float ffa_threshold: %f\n", ffa_threshold);
  fprintf(stderr, "int max_coadd: %d\n", max_coadd);
  fprintf(stderr, "int fold_buf_size_short: %d\n", fold_buf_size_short);
  fprintf(stderr, "int fold_buf_size_long: %d\n", fold_buf_size_long);
  fprintf(stderr, "int remove_radar: %d\n", remove_radar);
  fprintf(stderr, "int fold_buf_bytes_short: %d\n", fold_buf_bytes_short);
  fprintf(stderr, "int fold_level_large: %d\n", fold_level_large);
  fprintf(stderr, "int fold_level_small: %d\n", fold_level_small);
  for(int i = 0; i < MAX_COADD; i++)
    fprintf(stderr, "double thresh[%d] = %f\n", i, thresh[i]);
  fprintf(stderr, "double ra0: %f\n", ra0);
  fprintf(stderr, "double ra1: %f\n", ra1);
  fprintf(stderr, "double dec0: %f\n", dec0);
  fprintf(stderr, "double dec1: %f\n", dec1);
  fprintf(stderr, "double jd0: %f\n", jd0);
  fprintf(stderr, "double jd1: %f\n", jd1);
  fprintf(stderr, "int data_chunk_now: %d\n", data_chunk_now);
  fprintf(stderr, "int dm_chunk_large_now: %d\n", dm_chunk_large_now);
  fprintf(stderr, "int dm_chunk_small_now: %d\n", dm_chunk_small_now);
  fprintf(stderr, "int dm_now: %d\n", dm_now);
  fprintf(stderr, "int dm_sign: %d\n", dm_sign);
  fprintf(stderr, "int num_sub_buffers: %d\n", num_sub_buffers);
  fprintf(stderr, "int sub_buffer: %d\n", sub_buffer);
  fprintf(stderr, "float freq: %f\n", freq);
  fprintf(stderr, "float min_freq: %f\n", min_freq);
  fprintf(stderr, "int nfb: %d\n", nfb);
  fprintf(stderr, "int fold_buf_loc_short_pos: %d\n", fold_buf_loc_short_pos);
  fprintf(stderr, "int fold_buf_loc_short_neg: %d\n", fold_buf_loc_short_neg);
  fprintf(stderr, "int fold_buf_loc_long_pos: %d\n", fold_buf_loc_long_pos);
  fprintf(stderr, "int fold_buf_loc_long_neg: %d\n", fold_buf_loc_long_neg);
  fprintf(stderr, "double frac_done: %f\n", frac_done);
  fprintf(stderr, "double pulse_jd: %f\n", pulse_jd);
  fprintf(stderr, "int result_count_single: %d\n", result_count_single);
  fprintf(stderr, "int result_count_rep: %d\n", result_count_rep);
  fprintf(stderr, "enum code_segment: %d\n", (int) code_segment);
  for(int j = 0; j < MAX_COADD; j++) {

  }
  fprintf(stderr, "\n********************************************\n");
}
} 
