bool chk_cursor(SQL_CURSOR fd, const char *s) {
  bool rv=true; 
  if (fd<0) rv=false;
  if (fd>(sql::MAXCURSORS-1)) rv=false;
  if (rv) rv=(cursor_arr[fd].active);
  if (!rv) {
    myerrormsg=s;
    fprintf(stderr,"%s: %s\r\n",s,sql_error_message());
  } else {
    myerrormsg=0;
  }
  return (rv);
}
bool sql_database(const char* dbname, const char* password, int exclusive) {
  char buf[256], *p, *host;
  if ((!dbname) || (strlen(dbname)==0)) {
    dbname=getenv("DATABASE");
  }
  buf[0]=0;
  if (dbname) {
    strncpy(buf,dbname,254);
    strncpy(sql_dbname,dbname,254);
    buf[255]=0;
  }
  if ((p=strchr(buf,'@'))) {
    host=p+1;
    *p=0;
  } else {
    host=0;
  }
  mysql = mysql_init(0);
  if (!mysql) {
    fprintf(stderr,"mysql_init() failed:");
    fprintf(stderr,"%s\r\n",sql_error_message());
    return false;
  }
  
  if (buf[0]) {
    mysql = mysql_real_connect(mysql, host, 0, password, buf, 0, 0, 0);
  } else {
    mysql = mysql_real_connect(mysql, host, 0, password, 0, 0, 0, 0);
  }

  if (!mysql) {
    fprintf(stderr,"mysql_real_connect(%p,%s,0,%s,%s,0,0,0) failed:",mysql,host,password,buf);
    fprintf(stderr,"%s\r\n",sql_error_message());
    return false;
  }
  return true;
}
int make_wu_headers(std::vector<dr2_compact_block_t> &tapebuffer, telescope_id
tel, std::vector<workunit> &wuheader) {
  int procid=getpid();
  double receiver_freq;
  int bandno;
  FILE *tmpfile;
  char tmpstr[256];
  char buf[64];
  static const receiver_config &r(rcvr);
  static const settings &s(splitter_settings);
  bool group_is_vlar;

  if (!strncmp(s.splitter_cfg->data_type,"encoded",
     std::min(static_cast<size_t>(7),sizeof(s.splitter_cfg->data_type)))) {
    noencode=0;
  } else {
      noencode=1;
  }

  tapebuffer[0].header.samplerate*=1e+6;
  seconds sample_time(1.0/tapebuffer[0].header.samplerate);
  seti_time start_time(tapebuffer[0].header.data_time
           -tapebuffer[0].data.size()*0.5*sample_time);
  seti_time end_time(tapebuffer[tapebuffer.size()-1].header.data_time);


  workunit_grp wugrp;
  sprintf(wugrp.name,"%s.%ld.%d.%d.%d",
    tapebuffer[0].header.name,
    procid, 
    tapebuffer[0].header.dataseq,
    tel-AO_430,
    s.id);
  wugrp.receiver_cfg=r; 
  wugrp.recorder_cfg=s.recorder_cfg; 
  wugrp.splitter_cfg=s.splitter_cfg; 
  wugrp.analysis_cfg=s.analysis_cfg; 

  wugrp.data_desc.nsamples=NSAMPLES;
  wugrp.data_desc.true_angle_range=0;
  coordinate_t start_coord(cmap_interp(coord_history,start_time));
  coordinate_t end_coord(cmap_interp(coord_history,end_time));
  wugrp.data_desc.start_ra=start_coord.ra;
  wugrp.data_desc.end_ra=end_coord.ra;
  wugrp.data_desc.start_dec=start_coord.dec;
  wugrp.data_desc.end_dec=end_coord.dec;
  coordinate_t last_coord=start_coord;
  double sample_rate=tapebuffer[0].header.samplerate/NSTRIPS;

  // find the bracketing entries in the coordinate history
  std::map<seti_time,coordinate_t>::iterator above(coord_history.upper_bound(end_time));
  std::map<seti_time,coordinate_t>::iterator below(coord_history.lower_bound(start_time));
  std::map<seti_time,coordinate_t>::iterator p;
  if (above==coord_history.begin()) {
    above++;
  }
  if (below==coord_history.end()) {
    below=above;
    below--;
  }
  if (above==below) {
    below--;
  }
  // Calculate the angular distance the beam has traveled
  for (p=below;p!=above;p++)
  {
    wugrp.data_desc.true_angle_range+=angdist(last_coord,p->second);
    last_coord=p->second;
  }
  wugrp.data_desc.true_angle_range+=angdist(last_coord,end_coord);
  if (wugrp.data_desc.true_angle_range==0) wugrp.data_desc.true_angle_range=1e-10;
  // Calculate the number of unique signals that could be found in a workunit.
  // We will use these numbers to calculate thresholds.
  double numgauss=2.36368e+08/std::min(wugrp.data_desc.true_angle_range,10.0);
  double numpulse=std::min(4.52067e+10/std::min(wugrp.data_desc.true_angle_range,10.0),2.00382e+11);
  double numtrip=std::min(3.25215e+12/std::min(wugrp.data_desc.true_angle_range,10.0),1.44774e+13);

  // check for VLAR workunits
  if (wugrp.data_desc.true_angle_range < 0.12) {
    group_is_vlar=true;
  } else {
    group_is_vlar=false;
  }
    

  // if (useanalysiscfgid > 0) { 
  //   log_messages.printf(SCHED_MSG_LOG::MSG_NORMAL,"Re-reading analysis cfg id: %d (set by user):\n",useanalysiscfgid);
  //   s.analysis_cfg = useanalysiscfgid;
  //   }

  // Calculate a unique key to describe this analysis config.
  long keyuniq=floor(std::min(wugrp.data_desc.true_angle_range*100,1000.0)+0.5)+
    s.analysis_cfg.id*1024;
  if ((keyuniq>((s.analysis_cfg.id+1)*1024)) ||(keyuniq<(s.analysis_cfg.id)*1024)) {
     log_messages.printf(SCHED_MSG_LOG::MSG_CRITICAL,"Invalid keyuniq value!\n");
     log_messages.printf(SCHED_MSG_LOG::MSG_CRITICAL,"%d %d %f\n",keyuniq,s.analysis_cfg.id,wugrp.data_desc.true_angle_range);
     exit(1);
  }

  keyuniq*=-1;
  long save_keyuniq=keyuniq;
  splitter_settings.analysis_cfg=wugrp.analysis_cfg;
  sprintf(tmpstr,"where keyuniq=%d",keyuniq); 
  // Check if we've already done this analysis_config...
  // Fetch through splitter_settings, since it's alias (s) is const.
  splitter_settings.analysis_cfg.id=0;
  splitter_settings.analysis_cfg->fetch(tmpstr);

  if (s.analysis_cfg->id==0) {
    if (keyuniq != save_keyuniq) {
      log_messages.printf(SCHED_MSG_LOG::MSG_CRITICAL,"keyuniq value changed!\n");
      exit(1);
    }

    // If not calculate the thresholds based upon the input analysis_config
    // Triplets are distributed exponentially...
    wugrp.analysis_cfg->triplet_thresh+=(log(numtrip)-29.0652);

    // Gaussians are based upon chisqr...
    double p_gauss=lcgf(32.0,wugrp.analysis_cfg->gauss_null_chi_sq_thresh*32.0);
    p_gauss-=(log(numgauss)-19.5358);
    wugrp.analysis_cfg->gauss_null_chi_sq_thresh=invert_lcgf(p_gauss,32,1e-4)*0.03125;

    // Pulses thresholds are log of the probability
    wugrp.analysis_cfg->pulse_thresh+=(log(numpulse)-24.7894);

    wugrp.analysis_cfg->keyuniq=keyuniq;
    wugrp.analysis_cfg->insert();
  } else {
    wugrp.analysis_cfg=s.analysis_cfg;
  }

  strlcpy(wugrp.data_desc.time_recorded,
      short_jd_string(start_time.jd().uval()),
      sizeof(wugrp.data_desc.time_recorded));
  wugrp.data_desc.time_recorded_jd=start_time.jd().uval();
  wugrp.data_desc.coords.clear();

  wugrp.data_desc.coords.push_back(start_coord);  
  for (p=below;p!=above;p++)
  {
     wugrp.data_desc.coords.push_back(p->second);  
  }
  wugrp.data_desc.coords.push_back(end_coord);

  wugrp.tape_info->id=0;
  sprintf(buf,"%d",tel-AO_ALFA_0_0);
  wugrp.tape_info->fetch(std::string("where name=\'")+tapebuffer[0].header.name+"\' and beam="+buf);
  wugrp.tape_info->start_time=tapebuffer[0].header.data_time.jd().uval();
  wugrp.tape_info->last_block_time=wugrp.tape_info->start_time;
  wugrp.tape_info->last_block_done=tapebuffer[0].header.dataseq;
  wugrp.tape_info->beam=tel-AO_ALFA_0_0;

  if (!nodb) {
    if (wugrp.tape_info.id) {
      if (!(wugrp.tape_info->update())) {
        char buf[1024];
        log_messages.printf(SCHED_MSG_LOG::MSG_CRITICAL,"%s",sql_error_message());
	exit(1);
      }
    } else {
      strlcpy(wugrp.tape_info->name,tapebuffer[0].header.name,sizeof(wugrp.tape_info->name));
      wugrp.tape_info->insert();
    }
  }

  if (!nodb) {
    sqlint8_t wgid;
    if ((wgid=wugrp.insert())<=0) {
      log_messages.printf(SCHED_MSG_LOG::MSG_CRITICAL,"Workunit_grp insert failed\nwgid=%d\nSQLCODE=%d\nLAST_NON_ZERO_SQLCODE=%d\n",wgid,sql_error_code(),sql_last_error_code());
      exit( 1 );
    }
    wugrp.id=wgid;
  }
  int i; 
  wu_database_id.resize(NSTRIPS);
  bin_data.resize(NSTRIPS);
  wuheader.resize(NSTRIPS);
  for (i=0;i<NSTRIPS;i++) {
    bin_data[i].clear();
    wuheader[i].group_info=wugrp;
    wuheader[i].group_info.id=wugrp.id;
    sprintf(wuheader[i].name,"%s.%ld.%d.%ld.%d.%d",tapebuffer[0].header.name,
       procid, tapebuffer[0].header.dataseq,
       tel-AO_430,s.id,i);
    if (group_is_vlar) {
      strlcat(wuheader[i].name,".vlar",sizeof(wuheader[i].name));
    }
    wuheader[i].subband_desc.sample_rate=tapebuffer[0].header.samplerate/NSTRIPS;
 
    receiver_freq=tapebuffer[0].header.sky_freq;

    bandno=((i+NSTRIPS/2)%NSTRIPS)-NSTRIPS/2;

    wuheader[i].subband_desc.base=receiver_freq+
	       (double)(bandno)*wuheader[i].subband_desc.sample_rate;
    wuheader[i].subband_desc.center=receiver_freq+wuheader[i].subband_desc.sample_rate*NSTRIPS*((double)IFFT_LEN*bandno/FFT_LEN+(double)IFFT_LEN/(2*FFT_LEN)-1.0/(2*FFT_LEN));
    wuheader[i].subband_desc.number=i;

    if (!nodb ) {
      if (!(wu_database_id[i]=wuheader[i].id=wuheader[i].insert())) {
        log_messages.printf(SCHED_MSG_LOG::MSG_CRITICAL,"Database error in make_wu_headers()\n");
        exit(EXIT_FAILURE);
      }
    }
	
    sprintf(tmpstr,"./wu_inbox/%s",wuheader[i].name);
    if ((tmpfile=fopen(tmpstr,"w"))) {
      fprintf(tmpfile,"<workunit>\n");
      fprintf(tmpfile,wuheader[i].print_xml().c_str());
      fclose(tmpfile);
    } else {
      log_messages.printf(SCHED_MSG_LOG::MSG_CRITICAL,"Unable to open file ./wu_inbox/%s, errno=%d\n",wuheader[i].name,errno);
      exit(1);
    }
    bin_data[i].reserve(wuheaders[i].group_info->recorder_cfg->bits_per_sample*
       wuheaders[i].group_info->data_desc.nsamples/8);
  }
  return(1);
}
// assimilate_handler() is called by BOINC code and is passed the canonical 
// result for a workunit.  assimilate_handler() reads the referenced result
// file and inserts the result and its signals into the master science DB.
// BOINC also passes the workunit (as it appears in the BOINC DB) and a vector
// containing all results (including the canonical one) for that workunit.
// We use the workunit to determine if there is an error condition.
int assimilate_handler(
    WORKUNIT& boinc_wu, vector<RESULT>& boinc_results, RESULT& boinc_canonical_result
) {
    int retval=0;
    int spike_count=0, spike_inserted_count=0, gaussian_count=0, gaussian_inserted_count=0, pulse_count=0, pulse_inserted_count=0, triplet_count=0, triplet_inserted_count=0;
    static receiver_config receiver_cfg;
    static analysis_config analysis_cfg;
    workunit s_wu;
    workunit_grp s_wu_grp;
    result sah_result;
    spike sah_spike;
    gaussian sah_gaussian;
    pulse sah_pulse;
    triplet sah_triplet;
    char filename[256];
    char * path;
    std::string path_str;
    long sah_result_id;
    sqlint8_t sah_spike_id, sah_gaussian_id, sah_pulse_id, sah_triplet_id;
    static bool first_time = true;
    int sql_error_code;
    long long seti_wu_id;
    time_t now;
    int hotpix_update_count;
    int hotpix_insert_count;

    APP_CONFIG sah_config;

    hotpix hotpix;
    list<long> qpixlist;            // will be a unique list of qpixes for
                                    // updating the hotpix table
    list<long>::iterator qpix_i;

    nanotime.tv_sec = 0;
    nanotime.tv_nsec = 1000000;


    // app specific configuration
    if (first_time) {
	first_time = false;
	receiver_cfg.id = 0;
	analysis_cfg.id   = 0;
    	retval = sah_config.parse_file("..");
    	if (retval) {
      		log_messages.printf(SCHED_MSG_LOG::MSG_CRITICAL,
       	     	"First entrance to handler : can't parse config file. Exiting.\n"
      		);
  		return(retval);
	} else {
		retval = db_change(sah_config.scidb_name); 
		if (!retval) {
      			log_messages.printf(SCHED_MSG_LOG::MSG_CRITICAL,
       	     		"First entrance to handler : could not open science DB %s. Exiting.\n",
			sah_config.scidb_name
      			);
  			return(retval);
		} else {
      			log_messages.printf(SCHED_MSG_LOG::MSG_NORMAL,
       	     		"First entrance to handler : using science DB %s\n",
			sah_config.scidb_name
      			);
		}
	}
    	// Sometimes we want to perform all assimilation functions
    	// *except* insertion into the science master DB.
    	if (noinsert) {
      		log_messages.printf(SCHED_MSG_LOG::MSG_NORMAL,
      		"[%s] assimilator is in noinsert mode.\n",
      		boinc_wu.name
      		);
    	}
    } else {
/*
	retval = db_change(sah_config.scidb_name);
        if (!retval) {
              log_messages.printf(SCHED_MSG_LOG::MSG_CRITICAL,
                  "First entrance to handler : could not open science DB %s. Exiting.\n",
                  sah_config.scidb_name
              );
              return(retval);
        } else {
              log_messages.printf(SCHED_MSG_LOG::MSG_NORMAL,
                  "First entrance to handler : using science DB %s\n",
                  sah_config.scidb_name
              );
        }
*/
   }
   if (noinsert) return 0;   	// Note that this will result in the WU being marked as assimilated - 
				// we will not see it again.

    // translate seti wuid for thos wus that changed ids during the DB merge
    seti_wu_id = new_wu_id((long long)boinc_wu.opaque);
   log_messages.printf(SCHED_MSG_LOG::MSG_DEBUG,
         	"[%s] old seti WU id is : %lld  new seti WU id is : %lld\n", 
            boinc_wu.name, (long long)boinc_wu.opaque, seti_wu_id
   );

    if (boinc_wu.canonical_resultid) {
      log_messages.printf(SCHED_MSG_LOG::MSG_DEBUG,
            "[%s] Canonical result is %d.  SETI workunit ID is %lld.\n", 
	    boinc_wu.name,  boinc_wu.canonical_resultid, seti_wu_id
      );
    } else {
      log_messages.printf(SCHED_MSG_LOG::MSG_CRITICAL,
            "[%s] No canonical result\n", boinc_wu.name
      );
    }

    if (!boinc_wu.canonical_resultid) {
	return 0;		// Note that this will result in the WU being marked as assimilated - 
                                // we will not see it again.  No canonical result means that
				// too many results were returned with no concensus. 

    }

    // Obtain and check the full path to the boinc result file.
    retval = get_output_file_path(boinc_canonical_result, path_str);
    if (retval) {
	if (retval == ERR_XML_PARSE) {
		log_messages.printf(SCHED_MSG_LOG::MSG_CRITICAL,
			"[%s] Cannot extract filename from canonical result %ld.\n",
            		boinc_wu.name,  boinc_wu.canonical_resultid);
        	return(retval);
	} else {
		log_messages.printf(SCHED_MSG_LOG::MSG_CRITICAL,
                        "[%s] unknown error from get_output_file_path() for result %ld.\n",
                        boinc_wu.name,  boinc_wu.canonical_resultid);
                return(retval);
   	}
     } else {
     	path = (char *)path_str.c_str();
     	if (!boinc_file_exists(path)) {
		log_messages.printf(SCHED_MSG_LOG::MSG_CRITICAL,
        		"[%s] Output file %s does not exist for result %ld.\n",
               	  	boinc_wu.name, path,  boinc_wu.canonical_resultid);
        	return(-1);
     	} else {
		log_messages.printf(SCHED_MSG_LOG::MSG_DEBUG,
                        "[%s] Result %ld : using upload file %s\n",
               	  	boinc_wu.name, boinc_wu.canonical_resultid, path);
	}
    }

    // Open it.
    std::ifstream result_file(path, ios_base::in);
    if (!result_file.is_open()) {
      log_messages.printf(SCHED_MSG_LOG::MSG_CRITICAL,
           "[%s] open error for result file %s : errno %d\n", 
           boinc_wu.name, path, errno
      );
      return -1;
    }

    retval = get_science_configs(boinc_wu, seti_wu_id, receiver_cfg, analysis_cfg);
    if (retval) {
	if (retval == 100) {
		return (0);
	} else {
		return (-1);
 	}
    }
    log_messages.printf(SCHED_MSG_LOG::MSG_DEBUG,
                        "[%s] Result %ld : using receiver_cfg %d and analysis_cfg %d\n",
               	  	boinc_wu.name, boinc_wu.canonical_resultid, receiver_cfg.id, analysis_cfg.id);

    // Insert a sah result
    retval = populate_seti_result(sah_result, boinc_canonical_result, boinc_wu, seti_wu_id);
    sah_result_id = sah_result.insert();
    if (sah_result_id) {
    	log_messages.printf(SCHED_MSG_LOG::MSG_NORMAL,
         		"[%s] Inserted result.  Boinc result id is %d.  Sah result id is %lld.\n", 
	 		boinc_wu.name, boinc_canonical_result.id, 
			(long long)sah_result_id
   	);
    } else {
	if (sql_last_error_code() == -239 || sql_last_error_code() == -268) {
		log_messages.printf(SCHED_MSG_LOG::MSG_CRITICAL,
                        "[%s] Could not insert duplicate result.  SQLCODE is %ld.  SQLMSG is %s.\n",
                        boinc_wu.name, sql_last_error_code(), sql_error_message()
        	);
		return 0; 	// non-fatal - we will never see this result again
	} else {
		log_messages.printf(SCHED_MSG_LOG::MSG_CRITICAL,
                        "[%s] Could not insert result.  SQLCODE is %ld.  SQLMSG is %s.\n",
                        boinc_wu.name, sql_last_error_code(), sql_error_message()
        	);
        	return -1;	// fatal - non-dup error
	}
    }

    // Insert all sah signals in turn
    insert_signals( sah_spike, 
                    "spike", 
                    boinc_wu.name, 
                    sah_result_id,
                    result_file, 
                    receiver_cfg, 
                    boinc_canonical_result.appid, 
                    analysis_cfg.max_spikes,
                    qpixlist);

    insert_signals( sah_gaussian, 
                    "gaussian", 
                    boinc_wu.name, 
                    sah_result_id,
                    result_file, 
                    receiver_cfg, 
                    boinc_canonical_result.appid, 
                    analysis_cfg.max_gaussians,
                    qpixlist);

    insert_signals( sah_pulse, 
                    "pulse", 
                    boinc_wu.name, 
                    sah_result_id,
                    result_file, 
                    receiver_cfg, 
                    boinc_canonical_result.appid, 
                    analysis_cfg.max_pulses,
                    qpixlist);

    insert_signals( sah_triplet, 
                    "triplet", 
                    boinc_wu.name, 
                    sah_result_id,
                    result_file, 
                    receiver_cfg, 
                    boinc_canonical_result.appid, 
                    analysis_cfg.max_triplets,
                    qpixlist);

    // update last hit time to now for each qpix hit
    qpixlist.unique();
    hotpix_update_count = 0;
    hotpix_insert_count = 0;
    time(&now); 
    for(qpix_i = qpixlist.begin(); qpix_i != qpixlist.end(); qpix_i++) {
        if (hotpix.fetch(*qpix_i)) {
            hotpix.last_hit_time = now;
            hotpix.update();
            hotpix_update_count++;
        } else {
            hotpix.id = *qpix_i;
            hotpix.last_hit_time = now;
            hotpix.insert(*qpix_i);
            hotpix_insert_count++;
        }
    }
    log_messages.printf(SCHED_MSG_LOG::MSG_DEBUG,
             "[%s] Updated %d rows and inserted %d rows in the hotpix table\n",
             boinc_wu.name, hotpix_update_count, hotpix_insert_count
    );

    

    return 0;   // the successful assimilation of one WU
}
int insert_signals( T&                  signal,   
                    char *              signal_name, 
                    char *              wu_name,
                    sqlint8_t           sah_result_id,
                    std::ifstream&      result_file, 
                    receiver_config&    receiver_cfg,   
                    int                 appid,
                    int                 max_signals_allowed,
                    list<long>&         qpixlist) {

    int signal_count=0, signal_inserted_count=0, retval=0, qpix;
    sqlint8_t signal_id=0;

    result_file.clear();
    result_file.seekg(0);
    while (!result_file.eof()) {
        result_file >> signal;
        if (!result_file.eof()) {
            signal_count++;
            if (max_signals_allowed == 0 || signal_count <= max_signals_allowed) {
                if (!(signal.rfi_found = check_values(signal, sah_result_id, wu_name))) {
                    // preprocess only if we have good values
                    retval = pre_process(signal, receiver_cfg);  
                    qpixlist.push_back(npix2qpix((long long)signal.q_pix));
                }
                signal.result_id = sah_result_id;
                if (appid == 2) signal.reserved = 1;        // temporary for enhanced rollout
                signal_id = signal.insert();
                if (signal_id) {
                    log_messages.printf(SCHED_MSG_LOG::MSG_DEBUG,
                        "[%s] Inserted %s %"INT8_FMT" for sah result %"INT8_FMT"\n",
                        wu_name, signal_name, INT8_PRINT_CAST(signal_id), INT8_PRINT_CAST(sah_result_id)
                    );
                    signal_inserted_count++;
                } else {
                    log_messages.printf(SCHED_MSG_LOG::MSG_CRITICAL,
                                "[%s] Could not insert %s for sah result %"INT8_FMT". SQLCODE is %d. q_pix is %"INT8_FMT"  ra is %lf  decl is %lf .\n",
                                wu_name, signal_name, sah_result_id, sql_last_error_code(), signal.q_pix, signal.ra, signal.decl
                    );
#if 0
                    log_messages.printf(SCHED_MSG_LOG::MSG_CRITICAL,
                                "[%s] Could not insert %s for sah result %ld. SQLCODE is %d.  SQLMSG is %s  q_pix is %"INT8_FMT".\n",
                                wu_name, signal_name, sah_result_id, sql_last_error_code(), sql_error_message(), signal.q_pix
                    );
#endif
                    return -1;
                }  
            }   
        }   
    } 

    log_messages.printf(SCHED_MSG_LOG::MSG_NORMAL,
        "[%s] Inserted %d out of %d %s(s) for sah result %"INT8_FMT" \n",
        wu_name, signal_inserted_count, signal_count, signal_name, INT8_PRINT_CAST(sah_result_id)
    );

}
int make_wu_headers(tapeheader_t tapeheader[],workunit wuheader[],
                    buffer_pos_t *start_of_wu) {
  int procid=getpid();
  int i,j,startframe=start_of_wu->frame;
  double receiver_freq;
  int bandno;
  SCOPE_STRING *lastpos;
  FILE *tmpfile;
  char tmpstr[256];
  char buf[64];
  static int HaveConfigTable=0;
  static ReceiverConfig_t ReceiverConfig;   
  static receiver_config r;
  static settings s;

  if(!HaveConfigTable) {
    sprintf(buf,"where s4_id=%d",gregorian?AOGREG_1420:AO_1420);
    r.fetch(std::string(buf));
    ReceiverConfig.ReceiverID=r.s4_id;
    strlcpy(ReceiverConfig.ReceiverName,r.name,
           sizeof(ReceiverConfig.ReceiverName));
    ReceiverConfig.Latitude=r.latitude;
    ReceiverConfig.Longitude=r.longitude;
    ReceiverConfig.WLongitude=-r.longitude;
    ReceiverConfig.Elevation=r.elevation;
    ReceiverConfig.Diameter=r.diameter;
    ReceiverConfig.BeamWidth=r.beam_width;
    ReceiverConfig.CenterFreq=r.center_freq;
    ReceiverConfig.AzOrientation=r.az_orientation;
    for (i=0;i<(sizeof(ReceiverConfig.ZenCorrCoeff)/sizeof(ReceiverConfig.ZenCorrCoeff[0]));i++) {
      ReceiverConfig.ZenCorrCoeff[i]=r.zen_corr_coeff[i];
      ReceiverConfig.AzCorrCoeff[i]=r.az_corr_coeff[i];
    }
    HaveConfigTable=1;
  }
  sprintf(buf,"where active=%d",app.id);
  if (!s.fetch(std::string(buf))) {
    log_messages.printf(SCHED_MSG_LOG::MSG_CRITICAL,"Unable to find active settings for app.id=%d\n",app.id);
    exit(1);
  }
  if (s.receiver_cfg->id != r.id) {
    log_messages.printf(SCHED_MSG_LOG::MSG_CRITICAL,"Receiver config does not match settings (%d != %d)\n",s.receiver_cfg->id, r.id);
    exit(1);
  }
  s.recorder_cfg->fetch();
  s.splitter_cfg->fetch();
  s.analysis_cfg->fetch();
  if (!strncmp(s.splitter_cfg->data_type,"encoded",
     std::min(static_cast<size_t>(7),sizeof(s.splitter_cfg->data_type)))) {
    noencode=0;
  } else {
      noencode=1;
  }

  workunit_grp wugrp;
  sprintf(wugrp.name,"%s.%ld.%d.%ld.%d",tapeheader[startframe].name,
    procid, 
    (current_record-TAPE_RECORDS_IN_BUFFER)*8+startframe,
    start_of_wu->byte,s.id);
  wugrp.receiver_cfg=r;
  wugrp.recorder_cfg=s.recorder_cfg;
  wugrp.splitter_cfg=s.splitter_cfg;
  wugrp.analysis_cfg=s.analysis_cfg;

  wugrp.data_desc.start_ra=tapeheader[startframe+1].telstr.ra;
  wugrp.data_desc.start_dec=tapeheader[startframe+1].telstr.dec;
  wugrp.data_desc.end_ra=tapeheader[startframe+TAPE_FRAMES_PER_WU].telstr.ra;
  wugrp.data_desc.end_dec=tapeheader[startframe+TAPE_FRAMES_PER_WU].telstr.dec;
  wugrp.data_desc.nsamples=NSAMPLES;
  wugrp.data_desc.true_angle_range=0;
  {
    double sample_rate=tapeheader[startframe].samplerate/NSTRIPS;
    /* startframe+1 contains the first valid RA and Dec */
    TIME st=tapeheader[startframe+1].telstr.st;
    TIME et=tapeheader[startframe+TAPE_FRAMES_PER_WU].telstr.st;
    double diff=(et-st).jd*86400.0;
    for (j=2;j<TAPE_FRAMES_PER_WU;j++) {
      wugrp.data_desc.true_angle_range+=angdist(tapeheader[startframe+j-1].telstr,tapeheader[startframe+j].telstr);
    }
    wugrp.data_desc.true_angle_range*=(double)wugrp.data_desc.nsamples/(double)sample_rate/diff;
    if (wugrp.data_desc.true_angle_range==0) wugrp.data_desc.true_angle_range=1e-10;
  }
  // Calculate the number of unique signals that could be found in a workunit.
  // We will use these numbers to calculate thresholds.
  double numgauss=2.36368e+08/wugrp.data_desc.true_angle_range;
  double numpulse=std::min(4.52067e+10/wugrp.data_desc.true_angle_range,2.00382e+11);
  double numtrip=std::min(3.25215e+12/wugrp.data_desc.true_angle_range,1.44774e+13);

  

  // Calculate a unique key to describe this analysis config.
  long keyuniq=floor(std::min(wugrp.data_desc.true_angle_range*100,1000.0)+0.5)+
    s.analysis_cfg.id*1024.0;
  if ((keyuniq>(13*1024)) ||(keyuniq<12*1024)) {
     log_messages.printf(SCHED_MSG_LOG::MSG_CRITICAL,"Invalid keyuniq value!\n");
     log_messages.printf(SCHED_MSG_LOG::MSG_CRITICAL,"%d %d %f\n",keyuniq,s.analysis_cfg.id,wugrp.data_desc.true_angle_range);
     exit(1);
  }

  keyuniq*=-1;
  long save_keyuniq=keyuniq;
  s.analysis_cfg=wugrp.analysis_cfg;
  sprintf(tmpstr,"where keyuniq=%d",keyuniq); 
  // Check if we've already done this analysis_config...
  s.analysis_cfg.id=0;
  s.analysis_cfg->fetch(tmpstr);

  if (s.analysis_cfg->id==0) {
    if (keyuniq != save_keyuniq) {
      log_messages.printf(SCHED_MSG_LOG::MSG_CRITICAL,"keyuniq value changed!\n");
      exit(1);
    }

    // If not calculate the thresholds based upon the input analysis_config
    // Triplets are distributed exponentially...
    wugrp.analysis_cfg->triplet_thresh+=(log(numtrip)-29.0652);

    // Gaussians are based upon chisqr...
    double p_gauss=lcgf(32.0,wugrp.analysis_cfg->gauss_null_chi_sq_thresh*32.0);
    p_gauss-=(log(numgauss)-19.5358);
    wugrp.analysis_cfg->gauss_null_chi_sq_thresh=invert_lcgf(p_gauss,32,1e-4)*0.03125;

    // Pulses thresholds are log of the probability
    wugrp.analysis_cfg->pulse_thresh+=(log(numpulse)-24.7894);

    wugrp.analysis_cfg->keyuniq=keyuniq;
    wugrp.analysis_cfg->insert();
  } else {
    wugrp.analysis_cfg=s.analysis_cfg;
  }

  strlcpy(wugrp.data_desc.time_recorded,
      short_jd_string(tapeheader[startframe+1].telstr.st.jd),
      sizeof(wugrp.data_desc.time_recorded));
  wugrp.data_desc.time_recorded_jd=tapeheader[startframe+1].telstr.st.jd;

  lastpos=&(tapeheader[startframe].telstr);
  coordinate_t tmpcoord;
  tmpcoord.time=tapeheader[startframe].telstr.st.jd;
  tmpcoord.ra=tapeheader[startframe].telstr.ra;
  tmpcoord.dec=tapeheader[startframe].telstr.dec;
  wugrp.data_desc.coords.push_back(tmpcoord);  

  for (j=1;j<TAPE_FRAMES_PER_WU;j++) {
    if ((tapeheader[startframe+j].telstr.st.jd-lastpos->st.jd) > (1.0/86400.0)) 
    {
      lastpos=&(tapeheader[startframe+j].telstr);
      tmpcoord.time=tapeheader[startframe+j].telstr.st.jd;
      tmpcoord.ra=tapeheader[startframe+j].telstr.ra;
      tmpcoord.dec=tapeheader[startframe+j].telstr.dec;
      wugrp.data_desc.coords.push_back(tmpcoord);  
    }
  }

  wugrp.tape_info->id=0;
  wugrp.tape_info->fetch(std::string("where name=\'")+tapeheader[startframe].name+"\'");
  wugrp.tape_info->start_time=tapeheader[startframe].st.jd;
  wugrp.tape_info->last_block_time=tapeheader[startframe].st.jd;
  wugrp.tape_info->last_block_done=tapeheader[startframe].frameseq;

  if (!nodb) {
    if (wugrp.tape_info.id) {
      if (!(wugrp.tape_info->update())) {
        char buf[1024];
        log_messages.printf(SCHED_MSG_LOG::MSG_CRITICAL,"%s",sql_error_message());
	exit(1);
      }
    } else {
      strlcpy(wugrp.tape_info->name,tapeheader[startframe].name,sizeof(wugrp.tape_info->name));
      wugrp.tape_info->insert();
    }
  }

  if (!nodb) wugrp.insert();
  
  for (i=0;i<NSTRIPS;i++) {
    bin_data[i].clear();
    wuheader[i].group_info=wugrp;
    sprintf(wuheader[i].name,"%s.%ld.%d.%ld.%d.%d",tapeheader[startframe].name,
       procid, (current_record-TAPE_RECORDS_IN_BUFFER)*8+startframe,
       start_of_wu->byte,s.id,i);
    wuheader[i].subband_desc.sample_rate=tapeheader[startframe].samplerate/NSTRIPS;
 
    receiver_freq=tapeheader[startframe].centerfreq;

    bandno=((i+NSTRIPS/2)%NSTRIPS)-NSTRIPS/2;

    wuheader[i].subband_desc.base=receiver_freq+
	       (double)(bandno)*wuheader[i].subband_desc.sample_rate;
    wuheader[i].subband_desc.center=receiver_freq+wuheader[i].subband_desc.sample_rate*NSTRIPS*((double)IFFT_LEN*bandno/FFT_LEN+(double)IFFT_LEN/(2*FFT_LEN)-1.0/(2*FFT_LEN));
    wuheader[i].subband_desc.number=i;

    if (!nodb ) {
      if (!(wu_database_id[i]=wuheader[i].insert())) {
        log_messages.printf(SCHED_MSG_LOG::MSG_CRITICAL,"Database error in make_wu_headers()\n");
        exit(EXIT_FAILURE);
      }
    }
	
    sprintf(tmpstr,"./wu_inbox/%s",wuheader[i].name);
    if ((tmpfile=fopen(tmpstr,"w"))) {
      fprintf(tmpfile,"<workunit>\n");
      fprintf(tmpfile,wuheader[i].print_xml().c_str());
      fclose(tmpfile);
    } else {
      log_messages.printf(SCHED_MSG_LOG::MSG_CRITICAL,"Unable to open file ./wu_inbox/%s, errno=%d\n",wuheader[i].name,errno);
      exit(1);
    }
    bin_data[i].reserve(wuheaders[i].group_info->recorder_cfg->bits_per_sample*
       wuheaders[i].group_info->data_desc.nsamples/8);
  }
  return(1);
}