// test string-vector parsing function XLALParseStringValueAsStringVector()
int
test_ParseStringVector(void)
{
#define STR1 "Hello, world!"
#define STR2 "xyda 3!#4134"
#define STR3 "&\\//.. :: some junk"
#define STR4 "H1"
#define STR5 "H2"
#define STR6 "L1"

  LALStringVector *strVect1;
  XLAL_CHECK ( (strVect1 = XLALCreateStringVector ( STR1, STR2, STR3, STR4, STR5, NULL )) != NULL, XLAL_EFUNC );

  XLAL_CHECK ( (strVect1 = XLALAppendString2Vector ( strVect1, STR6 )) != NULL, XLAL_EFUNC );

  // now 'print' this string-vector as a 'string-value', then re-parse back into a vector:
  CHAR *strValue1 = NULL;
  LALStringVector *strVect2 = NULL;
  XLAL_CHECK ( (strValue1 = XLALPrintStringValueOfSTRINGVector ( &strVect1 )) != NULL, XLAL_EFUNC );
  XLALPrintInfo ("String value of initial string-vector:   %s\n", strValue1 );

  XLAL_CHECK ( XLALParseStringValueAsSTRINGVector ( &strVect2, strValue1 ) == XLAL_SUCCESS, XLAL_EFUNC );
  CHAR *strValue2 = NULL;
  XLAL_CHECK ( (strValue2 = XLALPrintStringValueOfSTRINGVector ( &strVect2 )) != NULL, XLAL_EFUNC );
  XLALPrintInfo ("String value of re-parsed string-vector: %s\n", strValue2 );

  // ----- compare results
  // 1) compare string values
  XLAL_CHECK ( strcmp ( strValue1, strValue2 ) == 0, XLAL_EFAILED, "String values differ:\nstrValue1 = %s\nstrValue2 = %s\n", strValue1, strValue2 );

  // 2) compare string vectors
  UINT4 len1 = strVect1->length;
  UINT4 len2 = strVect2->length;
  XLAL_CHECK ( len1 == len2, XLAL_EFAILED, "String vectors vect1 and vect2 have different lengths (%d != %d )\n", len1, len2 );

  for ( UINT4 i = 0; i < len1; i++ )
    {
      if ( strcmp ( strVect1->data[i], strVect2->data[i] ) != 0 )
        {
          for ( UINT4 j=0; j < len1; j ++ ) {
            XLALPrintError ("j = %d:  s1[j] = %6s, s2[j] = %6s\n", j, strVect1->data[j], strVect2->data[j] );
          }
          XLAL_ERROR ( XLAL_EFAILED, "Printed and re-parsed string-vector differ!\n" );
        } // if s1[i] != s2[i]

    } // for i < len

  // clean up memory
  XLALFree ( strValue1 );
  XLALFree ( strValue2 );

  XLALDestroyStringVector ( strVect1  );
  XLALDestroyStringVector ( strVect2 );

  return XLAL_SUCCESS;

} // test_ParseStringVector()
/**
 * Test various StringVector functions
 */
int
XLALStringVector_TEST ( void )
{

  LALStringVector *strVect1 = NULL, *strVectCmp = NULL;
#define STR1 "Hello"
#define STR2 "World"
#define STR3 "foo"
#define STR4 "H1"
#define STR5 "H2"
#define STR6 "L1"

  if ( (strVect1 = XLALCreateStringVector ( STR1, STR2, STR3, STR4, STR5, NULL )) == NULL )
    XLAL_ERROR ( XLAL_EFUNC );

  if ( ( strVect1 = XLALAppendString2Vector ( strVect1, STR6 )) == NULL )
    XLAL_ERROR ( XLAL_EFUNC );

  // now sort string-vector according to strcmp()
  if ( XLALSortStringVector ( strVect1 ) != XLAL_SUCCESS ) {
    XLALDestroyStringVector ( strVect1 );
    XLAL_ERROR ( XLAL_EFUNC );
  }

  // generate 'manually' sorted string-vector, using CSV function instead
  // sort-order according to "LC_ALL=C sort"
  if ( ( strVectCmp = XLALParseCSV2StringVector ( STR4 "," STR5 "," STR1 "," STR6 "," STR2 "," STR3 )) == NULL ) {
    XLALDestroyStringVector ( strVect1 );
    XLAL_ERROR ( XLAL_EFUNC );
  }

  /* compare results */
  UINT4 len1 = strVect1->length;
  UINT4 len2 = strVectCmp->length;
  if ( len1 != len2 ) {
    XLALDestroyStringVector ( strVect1  );
    XLALDestroyStringVector ( strVectCmp );
    XLAL_ERROR ( XLAL_EFAILED, "String vectors have different lengths (%d != %d )\n", len1, len2 );
  }
  for ( UINT4 i = 0; i < len1; i++ )
    {
      if ( 0 != strcmp ( strVect1->data[i], strVectCmp->data[i] ) )
        {
          XLALPrintError ( "%s: Sorted string-vector differs from expectation!\n", __func__ );
          for ( UINT4 j=0; j < len1; j ++ )
            XLALPrintError ("j = %d:  s1[j] = %6s, s2[j] = %6s\n", j, strVect1->data[j], strVectCmp->data[j] );
          /* clean up memory, and return with error */
          XLALDestroyStringVector ( strVect1  );
          XLALDestroyStringVector ( strVectCmp );
          XLAL_ERROR ( XLAL_EFAILED, "String sorting failed.\n");
        } /* if s1[i] != s2[i] */
    } /* for i < len */

  /* clean up memory */
  XLALDestroyStringVector ( strVect1  );
  XLALDestroyStringVector ( strVectCmp );

  return XLAL_SUCCESS;

} /* XLALStringVector_TEST() */
Exemplo n.º 3
0
/**
 * Function to determine the PulsarParamsVector input from a user-input defining CW sources.
 *
 * This option supports a dual-type feature: if any string in the list is of the form '{...}', then
 * it determines the *contents* of a config-file, otherwise the name-pattern of config-files to be parsed by XLALFindFiles(),
 * NOTE: when specifying file-contents, options can be separated by ';' and/or newlines)
 */
PulsarParamsVector *
XLALPulsarParamsFromUserInput ( const LALStringVector *UserInput		///< [in] user-input CSV list defining 'CW sources'
                                )
{
  XLAL_CHECK_NULL ( UserInput, XLAL_EINVAL );
  XLAL_CHECK_NULL ( UserInput->length > 0, XLAL_EINVAL );

  PulsarParamsVector *allSources = NULL;

  for ( UINT4 l = 0; l < UserInput->length; l ++ )
    {
      const char *thisInput = UserInput->data[l];

      if ( thisInput[0] != '{' )	// if it's an actual file-specification
        {
          LALStringVector *file_list;
          XLAL_CHECK_NULL ( ( file_list = XLALFindFiles ( &thisInput[0] )) != NULL, XLAL_EFUNC );
          UINT4 numFiles = file_list->length;
          for ( UINT4 i = 0; i < numFiles; i ++ )
            {
              PulsarParamsVector *sources_i;
              XLAL_CHECK_NULL ( (sources_i = XLALPulsarParamsFromFile ( file_list->data[i] )) != NULL, XLAL_EFUNC );

              XLAL_CHECK_NULL ( (allSources = XLALPulsarParamsVectorAppend ( allSources, sources_i )) != NULL, XLAL_EFUNC );
              XLALDestroyPulsarParamsVector ( sources_i );
            } // for i < numFiles

          XLALDestroyStringVector ( file_list );

        } // if file-pattern given
      else
        {
          UINT4 len = strlen(thisInput);
          XLAL_CHECK_NULL ( (thisInput[0] == '{') && (thisInput[len-1] == '}'), XLAL_EINVAL, "Invalid file-content input:\n%s\n", thisInput );
          char *buf;
          XLAL_CHECK_NULL ( (buf = XLALStringDuplicate ( &thisInput[1] )) != NULL, XLAL_EFUNC );
          len = strlen(buf);
          buf[len-1] = 0;	// remove trailing '}'

          LALParsedDataFile *cfgdata = NULL;
          XLAL_CHECK_NULL ( XLALParseDataFileContent ( &cfgdata, buf ) == XLAL_SUCCESS, XLAL_EFUNC );
          XLALFree ( buf );

          PulsarParamsVector *addSource;
          XLAL_CHECK_NULL ( (addSource = XLALCreatePulsarParamsVector ( 1 )) != NULL, XLAL_EFUNC );

          XLAL_CHECK_NULL ( XLALReadPulsarParams ( &addSource->data[0], cfgdata, NULL ) == XLAL_SUCCESS, XLAL_EFUNC );
          XLAL_CHECK_NULL ( (addSource->data[0].name = XLALStringDuplicate ( "direct-string-input" )) != NULL, XLAL_EFUNC );
          XLALDestroyParsedDataFile ( cfgdata );

          XLAL_CHECK_NULL ( (allSources = XLALPulsarParamsVectorAppend ( allSources, addSource )) != NULL, XLAL_EFUNC );
          XLALDestroyPulsarParamsVector ( addSource );

        } // if direct config-string given

    } // for l < len(UserInput)

  return allSources;

} // XLALPulsarParamsFromUserInput()
Exemplo n.º 4
0
///
/// Free contents of setup data
///
void XLALWeaveSetupDataClear(
  WeaveSetupData *setup
  )
{
  if ( setup != NULL ) {
    XLALDestroyStringVector( setup->detectors );
    XLALSegListFree( setup->segments );
    XLALDestroySuperskyMetrics( setup->metrics );
    XLALDestroyEphemerisData( setup->ephemerides );
  }
}
Exemplo n.º 5
0
/**
 * Parse a given 'CWsources' config file for PulsarParams, return vector
 * of all pulsar definitions found [using sections]
 */
PulsarParamsVector *
XLALPulsarParamsFromFile ( const char *fname 		///< [in] 'CWsources' config file name
                           )
{
  XLAL_CHECK_NULL ( fname != NULL, XLAL_EINVAL );

  LALParsedDataFile *cfgdata = NULL;
  XLAL_CHECK_NULL ( XLALParseDataFile ( &cfgdata, fname ) == XLAL_SUCCESS, XLAL_EFUNC );

  LALStringVector *sections;
  XLAL_CHECK_NULL ( (sections = XLALListConfigFileSections ( cfgdata )) != NULL, XLAL_EFUNC );

  UINT4 numPulsars = sections->length;	// currently only single-section defs supported! FIXME

  PulsarParamsVector *sources;
  XLAL_CHECK_NULL ( (sources = XLALCreatePulsarParamsVector ( numPulsars )) != NULL, XLAL_EFUNC );

  for ( UINT4 i = 0; i < numPulsars; i ++ )
    {
      const char *sec_i = sections->data[i];

      if ( strcmp ( sec_i, "default" ) == 0 ) {	// special handling of 'default' section
        sec_i = NULL;
      }
      XLAL_CHECK_NULL ( XLALReadPulsarParams ( &sources->data[i], cfgdata, sec_i ) == XLAL_SUCCESS, XLAL_EFUNC );

      // ----- source naming convention: 'filename:section'
      char *name;
      size_t len = strlen(fname) + strlen(sections->data[i]) + 2;
      XLAL_CHECK_NULL ( (name = XLALCalloc(1, len)) != NULL, XLAL_ENOMEM );
      sprintf ( name, "%s:%s", fname, sections->data[i] );
      sources->data[i].name = name;

    } // for i < numPulsars

  XLALDestroyStringVector ( sections );
  XLALDestroyParsedDataFile ( cfgdata );

  return sources;

} // XLALPulsarParamsFromFile()
Exemplo n.º 6
0
///
/// Create coherent input data
///
WeaveCohInput *XLALWeaveCohInputCreate(
  const LALStringVector *setup_detectors,
  const WeaveSimulationLevel simulation_level,
  const SFTCatalog *sft_catalog,
  const UINT4 segment_index,
  const LALSeg *segment,
  const PulsarDopplerParams *min_phys,
  const PulsarDopplerParams *max_phys,
  const double dfreq,
  const EphemerisData *ephemerides,
  const LALStringVector *sft_noise_sqrtSX,
  const LALStringVector *Fstat_assume_sqrtSX,
  FstatOptionalArgs *Fstat_opt_args,
  const WeaveStatisticsParams *statistics_params,
  BOOLEAN recalc_stage
  )
{

  // Check input
  XLAL_CHECK_NULL( setup_detectors != NULL, XLAL_EFAULT );
  XLAL_CHECK_NULL( ( simulation_level & WEAVE_SIMULATE_MIN_MEM ) || ( sft_catalog != NULL ), XLAL_EFAULT );
  XLAL_CHECK_NULL( segment != NULL, XLAL_EFAULT );
  XLAL_CHECK_NULL( min_phys != NULL, XLAL_EFAULT );
  XLAL_CHECK_NULL( max_phys != NULL, XLAL_EFAULT );
  XLAL_CHECK_NULL( dfreq >= 0, XLAL_EINVAL );
  XLAL_CHECK_NULL( ephemerides != NULL, XLAL_EFAULT );
  XLAL_CHECK_NULL( Fstat_opt_args != NULL, XLAL_EFAULT );
  XLAL_CHECK_NULL( statistics_params != NULL, XLAL_EFAULT );

  // Allocate memory
  WeaveCohInput *coh_input = XLALCalloc( 1, sizeof( *coh_input ) );
  XLAL_CHECK_NULL( coh_input != NULL, XLAL_ENOMEM );

  // Set fields
  coh_input->setup_detectors = setup_detectors;
  coh_input->simulation_level = simulation_level;
  coh_input->seg_info_have_sft_info = ( sft_catalog != NULL );
  coh_input->Fstat_collect_timing = Fstat_opt_args->collectTiming;

  // Record information from segment
  coh_input->seg_info.segment_start = segment->start;
  coh_input->seg_info.segment_end = segment->end;

  // Decide what F-statistic quantities to compute
  WeaveStatisticType requested_stats = ( recalc_stage ) ? statistics_params->completionloop_statistics[1] : statistics_params->mainloop_statistics;
  if ( requested_stats & WEAVE_STATISTIC_COH2F ) {
    coh_input->Fstat_what_to_compute |= FSTATQ_2F;
  }
  if ( requested_stats & WEAVE_STATISTIC_COH2F_DET ) {
    coh_input->Fstat_what_to_compute |= FSTATQ_2F_PER_DET;
  }

  // Return now if simulating search with minimal memory allocation
  if ( coh_input->simulation_level & WEAVE_SIMULATE_MIN_MEM ) {
    return coh_input;
  }

  // Get a timeslice of SFT catalog restricted to the given segment
  SFTCatalog XLAL_INIT_DECL( sft_catalog_seg );
  XLAL_CHECK_NULL( XLALSFTCatalogTimeslice( &sft_catalog_seg, sft_catalog, &segment->start, &segment->end ) == XLAL_SUCCESS, XLAL_EFUNC );
  XLAL_CHECK_NULL( sft_catalog_seg.length > 0, XLAL_EINVAL, "No SFTs found in segment %u", segment_index );

  // Check that the number of SFTs in each segment matches the number provided by the segment list in the setup file, if nonzero
  const UINT4 sft_count = ( UINT4 ) segment->id;
  XLAL_CHECK_NULL( sft_count == 0 || sft_catalog_seg.length == sft_count, XLAL_EFAILED, "Number of SFTs found for segment %u (%u) is inconsistent with expected number of SFTs given by segment list (%u)", segment_index, sft_catalog_seg.length, sft_count );

  // Get list of detectors of SFT catalog in this segment
  LALStringVector *sft_catalog_seg_detectors = XLALListIFOsInCatalog( &sft_catalog_seg );
  XLAL_CHECK_NULL( sft_catalog_seg_detectors != NULL, XLAL_EFUNC );

  // Compute frequency range covered by spindown range over in the given segment
  LIGOTimeGPS sft_start = sft_catalog_seg.data[0].header.epoch;
  LIGOTimeGPS sft_end = sft_catalog_seg.data[sft_catalog_seg.length - 1].header.epoch;
  const double sft_end_timebase = 1.0 / sft_catalog_seg.data[sft_catalog_seg.length - 1].header.deltaF;
  XLALGPSAdd( &sft_end, sft_end_timebase );
  PulsarSpinRange XLAL_INIT_DECL( spin_range );
  XLAL_CHECK_NULL( XLALInitPulsarSpinRangeFromSpins( &spin_range, &min_phys->refTime, min_phys->fkdot, max_phys->fkdot ) == XLAL_SUCCESS, XLAL_EFUNC );
  double sft_min_cover_freq = 0, sft_max_cover_freq = 0;
  XLAL_CHECK_NULL( XLALCWSignalCoveringBand( &sft_min_cover_freq, &sft_max_cover_freq, &sft_start, &sft_end, &spin_range, 0, 0, 0 ) == XLAL_SUCCESS, XLAL_EFUNC );

  // Parse SFT noise sqrt(Sh) string vector for detectors in this segment
  // - This is important when segments contain data from a subset of detectors
  MultiNoiseFloor Fstat_injectSqrtSX;
  if ( sft_noise_sqrtSX != NULL ) {
    XLAL_CHECK_NULL( XLALParseMultiNoiseFloorMapped( &Fstat_injectSqrtSX, sft_catalog_seg_detectors, sft_noise_sqrtSX, setup_detectors ) == XLAL_SUCCESS, XLAL_EFUNC );
    Fstat_opt_args->injectSqrtSX = &Fstat_injectSqrtSX;
  }

  // Parse F-statistic assumed sqrt(Sh) string vector for detectors in this segment
  // - This is important when segments contain data from a subset of detectors
  MultiNoiseFloor Fstat_assumeSqrtSX;
  if ( Fstat_assume_sqrtSX != NULL ) {
    XLAL_CHECK_NULL( XLALParseMultiNoiseFloorMapped( &Fstat_assumeSqrtSX, sft_catalog_seg_detectors, Fstat_assume_sqrtSX, setup_detectors ) == XLAL_SUCCESS, XLAL_EFUNC );
    Fstat_opt_args->assumeSqrtSX = &Fstat_assumeSqrtSX;
  }

  // Load F-statistic input data
  coh_input->Fstat_input = XLALCreateFstatInput( &sft_catalog_seg, sft_min_cover_freq, sft_max_cover_freq, dfreq, ephemerides, Fstat_opt_args );
  XLAL_CHECK_NULL( coh_input->Fstat_input != NULL, XLAL_EFUNC );
  Fstat_opt_args->prevInput = coh_input->Fstat_input;

  // Map detectors in F-statistic data in the given segment to their index in the coherent results
  // - This is important when segments contain data from a subset of detectors
  // - Map entry 'i' in 'Fstat_detector_info' (F-statistic data) to entry 'idx' in 'detectors' (coherent results)
  if ( coh_input->Fstat_what_to_compute & FSTATQ_2F_PER_DET ) {
    const MultiLALDetector *Fstat_detector_info = XLALGetFstatInputDetectors( coh_input->Fstat_input );
    coh_input->Fstat_ndetectors = Fstat_detector_info->length;
    char *statistics_detectors_string = XLALConcatStringVector( statistics_params->detectors, "," );
    for ( size_t i = 0; i < coh_input->Fstat_ndetectors; ++i ) {
      const char *prefix = Fstat_detector_info->sites[i].frDetector.prefix;
      const int idx = XLALFindStringInVector( prefix, statistics_params->detectors );
      XLAL_CHECK_NULL( idx >= 0, XLAL_EFAILED, "Detector '%s' from F-statistic data not found in list of detectors '%s'", prefix, statistics_detectors_string );
      coh_input->Fstat_res_idx[i] = idx;
    }
    XLALFree( statistics_detectors_string );
  }

  // Record information from SFTs in the given segment
  {
    MultiSFTCatalogView *sft_catalog_seg_view = XLALGetMultiSFTCatalogView( &sft_catalog_seg );
    XLAL_CHECK_NULL( sft_catalog_seg_view != NULL, XLAL_EINVAL );
    for ( size_t j = 0; j < sft_catalog_seg_view->length; ++j ) {
      XLAL_CHECK_NULL( sft_catalog_seg_view->data[j].length > 0, XLAL_EINVAL );
      char *detector_name = XLALGetChannelPrefix( sft_catalog_seg_view->data[j].data[0].header.name );
      XLAL_CHECK_NULL( detector_name != NULL, XLAL_EFUNC );
      const int k = XLALFindStringInVector( detector_name, sft_catalog_seg_detectors );
      if ( k >= 0 ) {
        const UINT4 length = sft_catalog_seg_view->data[j].length;
        coh_input->seg_info.sft_first[k] = sft_catalog_seg_view->data[j].data[0].header.epoch;
        coh_input->seg_info.sft_last[k] = sft_catalog_seg_view->data[j].data[length - 1].header.epoch;
        coh_input->seg_info.sft_count[k] = length;
      }
      XLALFree( detector_name );
    }
    XLALDestroyMultiSFTCatalogView( sft_catalog_seg_view );
  }
  XLAL_CHECK_NULL( XLALGetFstatInputSFTBand( coh_input->Fstat_input, &coh_input->seg_info.sft_min_freq, &coh_input->seg_info.sft_max_freq ) == XLAL_SUCCESS, XLAL_EFUNC );


  // Cleanup
  XLALDestroyStringVector( sft_catalog_seg_detectors );

  return coh_input;

}
Exemplo n.º 7
0
// ---------- main ----------
int
main ( int argc, char *argv[] )
{
  XLAL_CHECK ( argc == 1, XLAL_EINVAL, "No input arguments allowed.\n" );
  XLAL_CHECK ( argv != NULL, XLAL_EINVAL );

  // ----- load ephemeris
  EphemerisData *ephem;
  XLAL_CHECK ( (ephem = XLALInitBarycenter ( TEST_DATA_DIR "earth00-19-DE405.dat.gz", TEST_DATA_DIR "sun00-19-DE405.dat.gz" )) != NULL, XLAL_EFUNC );

  // ----- setup injection and data parameters
  LALStringVector *detNames = NULL;
  XLAL_CHECK ( (detNames = XLALCreateStringVector ( "H1", "L1", NULL )) != NULL, XLAL_EFUNC );
  UINT4 numDetectors = detNames->length;

  // generate and assume some gaussian noise floors
  MultiNoiseFloor XLAL_INIT_DECL(injectSqrtSX);
  MultiNoiseFloor XLAL_INIT_DECL(assumeSqrtSX);
  injectSqrtSX.length = numDetectors;
  assumeSqrtSX.length = numDetectors;
  for ( UINT4 X = 0; X < numDetectors; X ++ )
    {
      injectSqrtSX.sqrtSn[X] = 0; // don't inject random noise to keep errors deterministic and informative (resampling differs much more on noise)
      assumeSqrtSX.sqrtSn[X] = 1.0 + 2.0*X;
    }

  LIGOTimeGPS startTime = {711595934, 0};
  REAL8 Tspan = 20 * 3600;
  LIGOTimeGPS endTime = startTime;
  XLALGPSAdd( &endTime, Tspan );
  REAL8 Tsft = 1800;

  LIGOTimeGPS refTime = { startTime.gpsSeconds - 2.3 * Tspan, 0 };

  MultiLIGOTimeGPSVector *multiTimestamps;
  XLAL_CHECK ( ( multiTimestamps = XLALCalloc ( 1, sizeof(*multiTimestamps))) != NULL, XLAL_ENOMEM );
  XLAL_CHECK ( ( multiTimestamps->data = XLALCalloc ( numDetectors, sizeof(multiTimestamps->data[0]) )) != NULL, XLAL_ENOMEM );
  multiTimestamps->length = numDetectors;
  LIGOTimeGPS startTimeX = startTime;
  for ( UINT4 X=0; X < numDetectors; X ++ )
    {
      XLAL_CHECK ( (multiTimestamps->data[X] = XLALMakeTimestamps ( startTimeX, Tspan, Tsft, 0 ) ) != NULL, XLAL_EFUNC );
      XLALGPSAdd ( &startTimeX, 0.5 * Tspan );	// shift start-times by 1/2 Tspan for each detector
      Tspan *= 2.0;
    } // for X < numDetectors

  // shift a few timestamps around to create gaps
  UINT4 numSFTsPerDet = multiTimestamps->data[0]->length;
  multiTimestamps->data[0]->data[numSFTsPerDet-1].gpsSeconds += 10000;
  multiTimestamps->data[0]->data[numSFTsPerDet-2].gpsSeconds += 5000;
  multiTimestamps->data[1]->data[0].gpsSeconds -= 10000;
  multiTimestamps->data[1]->data[1].gpsSeconds -=  5000;

  SFTCatalog *catalog;
  XLAL_CHECK ( (catalog = XLALMultiAddToFakeSFTCatalog ( NULL, detNames, multiTimestamps )) != NULL, XLAL_EFUNC );

  // ----- CW sources to injet ----------
  REAL8 Freq = 100.0;

  PulsarParamsVector *injectSources;
  XLAL_CHECK ( (injectSources = XLALCreatePulsarParamsVector(1)) != NULL, XLAL_EFUNC );

  injectSources->data[0].Amp.h0   = 1;
  injectSources->data[0].Amp.cosi = 0.5;
  injectSources->data[0].Amp.psi  = 0.1;
  injectSources->data[0].Amp.phi0 = 1.2;

  REAL8 asini = 0; // 1.4;	// sco-X1 like
  REAL8 Period = 0; // 19 * 3600;// sco-X1 like
  REAL8 ecc = 0; // 0.1;	// much larger than ScoX1
  PulsarDopplerParams XLAL_INIT_DECL(Doppler);
  Doppler.Alpha = 0.5;
  Doppler.Delta = -0.5;
  Doppler.fkdot[0] = Freq;
  Doppler.fkdot[1] = -1e-9;
  Doppler.refTime = refTime;

  Doppler.asini = asini;
  Doppler.ecc = ecc;
  Doppler.tp = startTime;
  Doppler.period = Period;
  Doppler.argp = 0.5;

  injectSources->data[0].Doppler = Doppler;

  REAL8 dFreq = 0.1 / Tspan;		// 10x finer than native FFT resolution
  REAL8 mis = 0.5;
  REAL8 df1dot = sqrt( 720.0 * mis ) / (LAL_PI * Tspan * Tspan);	// metric (f-projected) stepsize for given mismatch mis
  REAL8 dSky = 1e4 / (Freq * Tspan);	// rough estimate of a 'metric' sky step, eg. Eq.(118) in \cite Prix07

  REAL8 dPeriod = 3600;
  UINT4 numFreqBins = 1000;

  UINT4 numf1dotPoints  = 2;
  UINT4 numSkyPoints    = 2;
  UINT4 numPeriodPoints = 2;

  PulsarSpinRange XLAL_INIT_DECL(spinRange);
  spinRange.refTime = refTime;
  memcpy ( &spinRange.fkdot, &injectSources->data[0].Doppler.fkdot, sizeof(spinRange.fkdot) );
  spinRange.fkdotBand[0] = (numFreqBins - 1)*dFreq - 10*LAL_REAL8_EPS;
  spinRange.fkdotBand[1] = (numf1dotPoints - 1)*df1dot - 10*LAL_REAL8_EPS;

  Doppler.fkdot[0] -= 0.4 * spinRange.fkdotBand[0];

  REAL8 minCoverFreq, maxCoverFreq;
  XLAL_CHECK ( XLALCWSignalCoveringBand ( &minCoverFreq, &maxCoverFreq, &startTime, &endTime, &spinRange, asini, Period, ecc ) == XLAL_SUCCESS, XLAL_EFUNC );

  // ----- setup optional Fstat arguments
  FstatOptionalArgs optionalArgs = FstatOptionalArgsDefaults;
  optionalArgs.injectSources = injectSources;
  optionalArgs.injectSqrtSX = &injectSqrtSX;
  optionalArgs.assumeSqrtSX = &assumeSqrtSX;

  // ----- prepare input data with injection for all available methods
  FstatInput *input[FMETHOD_END];
  FstatResults *results[FMETHOD_END];
  for ( UINT4 iMethod = FMETHOD_START; iMethod < FMETHOD_END; iMethod ++ )
    {
      results[iMethod] = NULL;
      if ( !XLALFstatMethodIsAvailable(iMethod) ) {
        continue;
      }
      optionalArgs.FstatMethod = iMethod;
      XLAL_CHECK ( (input[iMethod] = XLALCreateFstatInput ( catalog, minCoverFreq, maxCoverFreq, dFreq, ephem, &optionalArgs )) != NULL, XLAL_EFUNC );
    }

  FstatQuantities whatToCompute = (FSTATQ_2F | FSTATQ_FAFB);
  // ----- loop over all templates {sky, f1dot, period}
  for ( UINT4 iSky = 0; iSky < numSkyPoints; iSky ++ )
    {
      for ( UINT4 if1dot = 0; if1dot < numf1dotPoints; if1dot ++ )
        {
          for ( UINT4 iPeriod = 0; iPeriod < numPeriodPoints; iPeriod ++ )
            {
              // ----- loop over all available methods and compare Fstat results
              FstatMethodType firstMethod = FMETHOD_START;
              for ( UINT4 iMethod = FMETHOD_START; iMethod < FMETHOD_END; iMethod ++ )
                {
                  if ( !XLALFstatMethodIsAvailable(iMethod) ) {
                    continue;
                  }
                  if ( firstMethod == FMETHOD_START ) {	// keep track of first available method found
                    firstMethod = iMethod;
                  }

                  XLAL_CHECK ( XLALComputeFstat ( &results[iMethod], input[iMethod], &Doppler, numFreqBins, whatToCompute ) == XLAL_SUCCESS, XLAL_EFUNC );

                  if ( lalDebugLevel & LALINFOBIT )
                    {
                      FILE *fp;
                      char fname[1024]; XLAL_INIT_MEM ( fname );
                      snprintf ( fname, sizeof(fname)-1, "twoF%s-iSky%02d-if1dot%02d-iPeriod%02d.dat", XLALGetFstatMethodName(iMethod), iSky, if1dot, iPeriod );
                      XLAL_CHECK ( (fp = fopen ( fname, "wb" )) != NULL, XLAL_EFUNC );
                      for ( UINT4 k = 0; k < results[iMethod]->numFreqBins; k ++ )
                        {
                          REAL8 Freq0 = results[iMethod]->doppler.fkdot[0];
                          REAL8 Freq_k = Freq0 + k * results[iMethod]->dFreq;
                          if ( whatToCompute & FSTATQ_FAFB ) {
                            fprintf ( fp, "%20.16g %10.4g   %10.4g %10.4g   %10.4g %10.4g\n",
                                      Freq_k, results[iMethod]->twoF[k],
                                      crealf(results[iMethod]->Fa[k]), cimagf(results[iMethod]->Fa[k]),
                                      crealf(results[iMethod]->Fb[k]), cimagf(results[iMethod]->Fb[k])
                                      );
                          } else {
                            fprintf ( fp, "%20.16g %10.4g\n",
                                      Freq_k, results[iMethod]->twoF[k] );
                          }
                        } // for k < numFreqBins
                      fclose(fp);
                    } // if info

                  // compare to first result
                  if ( iMethod != firstMethod )
                    {
                      XLALPrintInfo ("Comparing results between method '%s' and '%s'\n", XLALGetFstatMethodName(firstMethod), XLALGetFstatMethodName(iMethod) );
                      if ( compareFstatResults ( results[firstMethod], results[iMethod] ) != XLAL_SUCCESS )
                        {
                          XLALPrintError ("Comparison between method '%s' and '%s' failed\n", XLALGetFstatMethodName(firstMethod), XLALGetFstatMethodName(iMethod) );
                          XLAL_ERROR ( XLAL_EFUNC );
                        }
                    }

                }  // for i < FMETHOD_END

              Doppler.period += dPeriod;

            } // for iPeriod < numPeriodPoints

          Doppler.fkdot[1] += df1dot;

        } // for if1dot < numf1dotPoints

      Doppler.Alpha += dSky;

    } // for iSky < numSkyPoints

  // free remaining memory
  for ( UINT4 iMethod=FMETHOD_START; iMethod < FMETHOD_END; iMethod ++ )
    {
      if ( !XLALFstatMethodIsAvailable(iMethod) ) {
        continue;
      }
      XLALDestroyFstatInput ( input[iMethod] );
      XLALDestroyFstatResults ( results[iMethod] );
    } // for i < FMETHOD_END

  XLALDestroyPulsarParamsVector ( injectSources );
  XLALDestroySFTCatalog ( catalog );
  XLALDestroyMultiTimestamps ( multiTimestamps );
  XLALDestroyStringVector ( detNames );
  XLALDestroyEphemerisData ( ephem );

  LALCheckMemoryLeaks();

  return XLAL_SUCCESS;

} // main()
/**
 * Unit test for metric functions XLALComputeDopplerPhaseMetric()
 * and XLALComputeDopplerFstatMetric()
 *
 * Initially modelled afer testMetricCodes.py script:
 * Check metric codes 'getMetric' 'FstatMetric' and 'FstatMetric_v2' by
 * comparing them against each other.
 * Given that they represent 3 very different implementations of
 * metric calculations, this provides a very powerful consistency test
 *
 */
static int
test_XLALComputeDopplerMetrics ( void )
{
  int ret;
  const REAL8 tolPh = 0.01;	// 1% tolerance on phase metrics [taken from testMetricCodes.py]

  // ----- load ephemeris
  const char earthEphem[] = TEST_DATA_DIR "earth00-19-DE200.dat.gz";
  const char sunEphem[]   = TEST_DATA_DIR "sun00-19-DE200.dat.gz";
  EphemerisData *edat = XLALInitBarycenter ( earthEphem, sunEphem );
  XLAL_CHECK ( edat != NULL, XLAL_EFUNC, "XLALInitBarycenter('%s','%s') failed with xlalErrno = %d\n", earthEphem, sunEphem, xlalErrno );

  // ----- set test-parameters ----------
  const LIGOTimeGPS startTimeGPS = { 792576013, 0 };
  const REAL8 Tseg = 60000;

  const REAL8 Alpha = 1.0;
  const REAL8 Delta = 0.5;
  const REAL8 Freq  = 100;
  const REAL8 f1dot = 0;// -1e-8;

  LALStringVector *detNames = XLALCreateStringVector ( "H1", "L1", "V1",  NULL );
  LALStringVector *sqrtSX   = XLALCreateStringVector ( "1.0", "0.5", "1.5", NULL );

  MultiLALDetector multiIFO;
  XLAL_CHECK ( XLALParseMultiLALDetector ( &multiIFO, detNames ) == XLAL_SUCCESS, XLAL_EFUNC );
  XLALDestroyStringVector ( detNames );

  MultiNoiseFloor multiNoiseFloor;
  XLAL_CHECK ( XLALParseMultiNoiseFloor ( &multiNoiseFloor, sqrtSX, multiIFO.length ) == XLAL_SUCCESS, XLAL_EFUNC );
  XLALDestroyStringVector ( sqrtSX );

  // prepare metric parameters for modern XLALComputeDopplerFstatMetric() and mid-old XLALOldDopplerFstatMetric()
  const DopplerCoordinateSystem coordSys = { 4, { DOPPLERCOORD_FREQ, DOPPLERCOORD_ALPHA, DOPPLERCOORD_DELTA, DOPPLERCOORD_F1DOT } };
  const PulsarAmplitudeParams Amp = { 0.03, -0.3, 0.5, 0.0 };	// h0, cosi, psi, phi0
  const PulsarDopplerParams dop = {
    .refTime  = startTimeGPS,
    .Alpha    = Alpha,
    .Delta    = Delta,
    .fkdot    = { Freq, f1dot },
  };

  LALSegList XLAL_INIT_DECL(segList);
  ret = XLALSegListInitSimpleSegments ( &segList, startTimeGPS, 1, Tseg );
  XLAL_CHECK ( ret == XLAL_SUCCESS, XLAL_EFUNC, "XLALSegListInitSimpleSegments() failed with xlalErrno = %d\n", xlalErrno );

  const DopplerMetricParams master_pars2 = {
    .coordSys			= coordSys,
    .detMotionType		= DETMOTION_SPIN | DETMOTION_ORBIT,
    .segmentList		= segList,
    .multiIFO			= multiIFO,
    .multiNoiseFloor		= multiNoiseFloor,
    .signalParams		= { .Amp = Amp, .Doppler = dop },
    .projectCoord		= - 1,	// -1==no projection
    .approxPhase		= 0,
  };


  // ========== BEGINNING OF TEST CALLS ==========


  XLALPrintWarning("\n---------- ROUND 1: ephemeris-based, single-IFO phase metrics ----------\n");
  {
    OldDopplerMetric *metric1;
    DopplerPhaseMetric *metric2P;
    REAL8 diff_2_1;

    DopplerMetricParams pars2 = master_pars2;

    pars2.multiIFO.length = 1;	// truncate to first detector
    pars2.multiNoiseFloor.length = 1;	// truncate to first detector

    // 1) compute metric using old FstatMetric code, now wrapped into XLALOldDopplerFstatMetric()
    XLAL_CHECK ( (metric1 = XLALOldDopplerFstatMetric ( OLDMETRIC_TYPE_PHASE, &pars2, edat )) != NULL, XLAL_EFUNC );
    // 2) compute metric using modern UniversalDopplerMetric module: (used in lalapps_FstatMetric_v2)
    XLAL_CHECK ( (metric2P = XLALComputeDopplerPhaseMetric ( &pars2, edat )) != NULL, XLAL_EFUNC );

    // compare metrics against each other:
    XLAL_CHECK ( (diff_2_1 = XLALCompareMetrics ( metric2P->g_ij, metric1->g_ij )) < tolPh, XLAL_ETOL, "Error(g2,g1)= %g exceeds tolerance of %g\n", diff_2_1, tolPh );
    XLALPrintWarning ("diff_2_1 = %e\n", diff_2_1 );

    XLALDestroyOldDopplerMetric ( metric1 );
    XLALDestroyDopplerPhaseMetric ( metric2P );
  }


  XLALPrintWarning("\n---------- ROUND 2: Ptolemaic-based, single-IFO phase metrics ----------\n");
  {
    OldDopplerMetric *metric1;
    DopplerPhaseMetric *metric2P;
    REAL8 diff_2_1;

    DopplerMetricParams pars2 = master_pars2;

    pars2.multiIFO.length = 1;	// truncate to first detector
    pars2.multiNoiseFloor.length = 1;	// truncate to first detector

    pars2.detMotionType = DETMOTION_SPIN | DETMOTION_PTOLEORBIT;

    // 1) compute metric using old FstatMetric code, now wrapped into XLALOldDopplerFstatMetric()
    XLAL_CHECK ( (metric1 = XLALOldDopplerFstatMetric ( OLDMETRIC_TYPE_PHASE, &pars2, edat )) != NULL, XLAL_EFUNC );
    // 2) compute metric using modern UniversalDopplerMetric module: (used in lalapps_FstatMetric_v2)
    XLAL_CHECK ( (metric2P = XLALComputeDopplerPhaseMetric ( &pars2, edat )) != NULL, XLAL_EFUNC );

    // compare all 3 metrics against each other:
    XLAL_CHECK ( (diff_2_1 = XLALCompareMetrics ( metric2P->g_ij, metric1->g_ij )) < tolPh, XLAL_ETOL, "Error(g2,g1)= %g exceeds tolerance of %g\n", diff_2_1, tolPh );
    XLALPrintWarning ("diff_2_1 = %e\n", diff_2_1 );

    XLALDestroyOldDopplerMetric ( metric1 );
    XLALDestroyDopplerPhaseMetric ( metric2P );
  }


  XLALPrintWarning("\n---------- ROUND 3: ephemeris-based, multi-IFO F-stat metrics ----------\n");
  {
    OldDopplerMetric *metric1;
    DopplerFstatMetric *metric2F;
    REAL8 diff_2_1;

    DopplerMetricParams pars2 = master_pars2;

    pars2.detMotionType = DETMOTION_SPIN | DETMOTION_ORBIT;
    pars2.multiIFO      = multiIFO;	// 3 IFOs
    pars2.multiNoiseFloor = multiNoiseFloor;// 3 IFOs

    // 1) compute metric using old FstatMetric code, now wrapped into XLALOldDopplerFstatMetric()
    XLAL_CHECK ( (metric1 = XLALOldDopplerFstatMetric ( OLDMETRIC_TYPE_FSTAT, &pars2, edat )) != NULL, XLAL_EFUNC );
    // 2) compute metric using modern UniversalDopplerMetric module: (used in lalapps_FstatMetric_v2)
    XLAL_CHECK ( (metric2F = XLALComputeDopplerFstatMetric ( &pars2, edat )) != NULL, XLAL_EFUNC );

    // compare both metrics against each other:
    XLAL_CHECK ( (diff_2_1 = XLALCompareMetrics ( metric2F->gF_ij,   metric1->gF_ij ))   < tolPh, XLAL_ETOL, "Error(gF2,gF1)= %e exceeds tolerance of %e\n", diff_2_1, tolPh );
    XLALPrintWarning ("gF:   diff_2_1 = %e\n", diff_2_1 );
    XLAL_CHECK ( (diff_2_1 = XLALCompareMetrics ( metric2F->gFav_ij, metric1->gFav_ij )) < tolPh, XLAL_ETOL, "Error(gFav2,gFav1)= %e exceeds tolerance of %e\n", diff_2_1, tolPh );
    XLALPrintWarning ("gFav: diff_2_1 = %e\n", diff_2_1 );

    XLALDestroyOldDopplerMetric ( metric1 );
    XLALDestroyDopplerFstatMetric ( metric2F );
  }


  XLALPrintWarning("\n---------- ROUND 4: compare analytic {f,f1dot,f2dot,f3dot} phase metric vs XLALComputeDopplerPhaseMetric() ----------\n");
  {
    DopplerPhaseMetric *metric2P;
    REAL8 diff_2_1;

    DopplerMetricParams pars2 = master_pars2;

    pars2.multiIFO.length  = 1;	// truncate to 1st detector
    pars2.multiNoiseFloor.length  = 1;	// truncate to 1st detector
    pars2.detMotionType   = DETMOTION_SPIN | DETMOTION_ORBIT;
    pars2.approxPhase     = 1;	// use same phase-approximation as in analytic solution to improve comparison

    DopplerCoordinateSystem coordSys2 = { 4, { DOPPLERCOORD_FREQ, DOPPLERCOORD_F1DOT, DOPPLERCOORD_F2DOT, DOPPLERCOORD_F3DOT } };
    pars2.coordSys = coordSys2;
    gsl_matrix* gN_ij;

    // a) compute metric at refTime = startTime
    pars2.signalParams.Doppler.refTime = startTimeGPS;
    XLAL_CHECK ( (metric2P = XLALComputeDopplerPhaseMetric ( &pars2, edat )) != NULL, XLAL_EFUNC );
    gN_ij = NULL;
    XLAL_CHECK ( XLALNaturalizeMetric ( &gN_ij, NULL, metric2P->g_ij, &pars2 ) == XLAL_SUCCESS, XLAL_EFUNC );

    REAL8 gStart_ij[] = {   1.0/3,      2.0/3,    6.0/5,    32.0/15,      \
                            2.0/3,    64.0/45,    8.0/3,  512.0/105,      \
                            6.0/5,      8.0/3,   36.0/7,     48.0/5,      \
                            32.0/15,  512.0/105, 48.0/5, 4096.0/225 };
    const gsl_matrix_view gStart = gsl_matrix_view_array ( gStart_ij, 4, 4 );

    // compare natural-units metric against analytic solution
    XLAL_CHECK ( (diff_2_1 = XLALCompareMetrics ( gN_ij,   &(gStart.matrix) )) < tolPh, XLAL_ETOL,
                 "RefTime=StartTime: Error(g_ij,g_analytic)= %e exceeds tolerance of %e\n", diff_2_1, tolPh );
    XLALPrintWarning ("Analytic (refTime=startTime): diff_2_1 = %e\n", diff_2_1 );

    XLALDestroyDopplerPhaseMetric ( metric2P );
    gsl_matrix_free ( gN_ij );

    // b) compute metric at refTime = midTime
    pars2.signalParams.Doppler.refTime = startTimeGPS;
    pars2.signalParams.Doppler.refTime.gpsSeconds += Tseg / 2;

    XLAL_CHECK ( (metric2P = XLALComputeDopplerPhaseMetric ( &pars2, edat )) != NULL, XLAL_EFUNC );
    gN_ij = NULL;
    XLAL_CHECK ( XLALNaturalizeMetric ( &gN_ij, NULL, metric2P->g_ij, &pars2 ) == XLAL_SUCCESS, XLAL_EFUNC );

    REAL8 gMid_ij[] = { 1.0/3,    0,        1.0/5,         0,       \
                        0,        4.0/45,       0,   8.0/105,       \
                        1.0/5,    0,        1.0/7,         0,       \
                        0,        8.0/105,      0,  16.0/225  };
    const gsl_matrix_view gMid = gsl_matrix_view_array ( gMid_ij, 4, 4 );

    // compare natural-units metric against analytic solution
    XLAL_CHECK ( (diff_2_1 = XLALCompareMetrics ( gN_ij,   &(gMid.matrix) )) < tolPh, XLAL_ETOL,
                 "RefTime=MidTime: Error(g_ij,g_analytic)= %e exceeds tolerance of %e\n", diff_2_1, tolPh );
    XLALPrintWarning ("Analytic (refTime=midTime):   diff_2_1 = %e\n\n", diff_2_1 );

    XLALDestroyDopplerPhaseMetric ( metric2P );
    gsl_matrix_free ( gN_ij );
  }


  XLALPrintWarning("\n---------- ROUND 5: ephemeris-based, single-IFO, segment-averaged phase metrics ----------\n");
  {
    OldDopplerMetric *metric1;
    DopplerPhaseMetric *metric2P;
    REAL8 diff_2_1;

    DopplerMetricParams pars2 = master_pars2;

    pars2.detMotionType = DETMOTION_SPIN | DETMOTION_ORBIT;
    pars2.multiIFO.length = 1;	// truncate to first detector
    pars2.multiNoiseFloor.length = 1;	// truncate to first detector
    pars2.approxPhase = 1;

    const UINT4 Nseg = 10;
    LALSegList XLAL_INIT_DECL(NsegList);
    ret = XLALSegListInitSimpleSegments ( &NsegList, startTimeGPS, Nseg, Tseg );
    XLAL_CHECK ( ret == XLAL_SUCCESS, XLAL_EFUNC, "XLALSegListInitSimpleSegments() failed with xlalErrno = %d\n", xlalErrno );
    pars2.segmentList = NsegList;

    LALSegList XLAL_INIT_DECL(segList_k);
    LALSeg segment_k;
    XLALSegListInit( &segList_k );	// prepare single-segment list containing segment k
    segList_k.arraySize = 1;
    segList_k.length = 1;
    segList_k.segs = &segment_k;

    // 1) compute metric using old FstatMetric code, now wrapped into XLALOldDopplerFstatMetric()
    metric1 = NULL;
    for (UINT4 k = 0; k < Nseg; ++k) {
      // setup 1-segment segment-list pointing k-th segment
      DopplerMetricParams pars2_k = pars2;
      pars2_k.segmentList = segList_k;
      pars2_k.segmentList.segs[0] = pars2.segmentList.segs[k];
      // XLALOldDopplerFstatMetric() does not agree numerically with UniversalDopplerMetric when using refTime != startTime
      pars2_k.signalParams.Doppler.refTime = pars2_k.segmentList.segs[0].start;

      OldDopplerMetric *metric1_k;   // per-segment coherent metric
      XLAL_CHECK ( (metric1_k = XLALOldDopplerFstatMetric ( OLDMETRIC_TYPE_PHASE, &pars2_k, edat )) != NULL, XLAL_EFUNC );

      // manually correct reference time of metric1_k->g_ij; see Prix, "Frequency metric for CW searches" (2014-08-17), p. 4
      const double dt = XLALGPSDiff( &(pars2_k.signalParams.Doppler.refTime), &(pars2.signalParams.Doppler.refTime) );
      const double gFF = gsl_matrix_get( metric1_k->g_ij, 0, 0 );
      const double gFA = gsl_matrix_get( metric1_k->g_ij, 0, 1 );
      const double gFD = gsl_matrix_get( metric1_k->g_ij, 0, 2 );
      const double gFf = gsl_matrix_get( metric1_k->g_ij, 0, 3 );
      const double gAf = gsl_matrix_get( metric1_k->g_ij, 1, 3 );
      const double gDf = gsl_matrix_get( metric1_k->g_ij, 2, 3 );
      const double gff = gsl_matrix_get( metric1_k->g_ij, 3, 3 );
      gsl_matrix_set( metric1_k->g_ij, 0, 3, gFf + gFF*dt ); gsl_matrix_set( metric1_k->g_ij, 3, 0, gsl_matrix_get( metric1_k->g_ij, 0, 3 ) );
      gsl_matrix_set( metric1_k->g_ij, 1, 3, gAf + gFA*dt ); gsl_matrix_set( metric1_k->g_ij, 3, 1, gsl_matrix_get( metric1_k->g_ij, 1, 3 ) );
      gsl_matrix_set( metric1_k->g_ij, 2, 3, gDf + gFD*dt ); gsl_matrix_set( metric1_k->g_ij, 3, 2, gsl_matrix_get( metric1_k->g_ij, 2, 3 ) );
      gsl_matrix_set( metric1_k->g_ij, 3, 3, gff + 2*gFf*dt + gFF*dt*dt );

      XLAL_CHECK ( XLALAddOldDopplerMetric ( &metric1, metric1_k ) == XLAL_SUCCESS, XLAL_EFUNC );
      XLALDestroyOldDopplerMetric ( metric1_k );
    }
    XLAL_CHECK ( XLALScaleOldDopplerMetric ( metric1, 1.0 / Nseg ) == XLAL_SUCCESS, XLAL_EFUNC );

    // 2) compute metric using modern UniversalDopplerMetric module: (used in lalapps_FstatMetric_v2)
    XLAL_CHECK ( (metric2P = XLALComputeDopplerPhaseMetric ( &pars2, edat )) != NULL, XLAL_EFUNC );

    GPMAT( metric1->g_ij, "%0.4e" );
    GPMAT( metric2P->g_ij, "%0.4e" );

    // compare both metrics against each other:
    XLAL_CHECK ( (diff_2_1 = XLALCompareMetrics ( metric2P->g_ij, metric1->g_ij )) < tolPh, XLAL_ETOL, "Error(g2,g1)= %g exceeds tolerance of %g\n", diff_2_1, tolPh );
    XLALPrintWarning ("diff_2_1 = %e\n", diff_2_1 );

    XLALDestroyOldDopplerMetric ( metric1 );
    XLALDestroyDopplerPhaseMetric ( metric2P );

    XLALSegListClear ( &NsegList );
  }


  XLALPrintWarning("\n---------- ROUND 6: directed binary orbital metric ----------\n");
  {
    REAL8 Period = 68023.70496;
    REAL8 Omega = LAL_TWOPI / Period;
    REAL8 asini = 1.44;
    REAL8 tAsc = 897753994;
    REAL8 argp = 0;
    LIGOTimeGPS tP; XLALGPSSetREAL8 ( &tP, tAsc + argp / Omega );

    const PulsarDopplerParams dopScoX1 = {
      .refTime  = startTimeGPS,
      .Alpha    = Alpha,
      .Delta    = Delta,
      .fkdot    = { Freq },
      .asini    = asini,
      .period   = Period,
      .tp       = tP
    };
    REAL8 TspanScoX1 = 20 * 19 * 3600;	// 20xPorb for long-segment regime
    LALSegList XLAL_INIT_DECL(segListScoX1);
    XLAL_CHECK ( XLALSegListInitSimpleSegments ( &segListScoX1, startTimeGPS, 1, TspanScoX1 ) == XLAL_SUCCESS, XLAL_EFUNC );
    REAL8 tMid = XLALGPSGetREAL8(&startTimeGPS) + 0.5 * TspanScoX1;
    REAL8 DeltaMidAsc = tMid - tAsc;
    const DopplerCoordinateSystem coordSysScoX1 = { 6, { DOPPLERCOORD_FREQ, DOPPLERCOORD_ASINI, DOPPLERCOORD_TASC, DOPPLERCOORD_PORB, DOPPLERCOORD_KAPPA, DOPPLERCOORD_ETA } };
    DopplerMetricParams pars_ScoX1 = {
      .coordSys			= coordSysScoX1,
      .detMotionType		= DETMOTION_SPIN | DETMOTION_ORBIT,
      .segmentList		= segListScoX1,
      .multiIFO			= multiIFO,
      .multiNoiseFloor		= multiNoiseFloor,
      .signalParams		= { .Amp = Amp, .Doppler = dopScoX1 },
      .projectCoord		= - 1,	// -1==no projection
      .approxPhase		= 1,
    };
    pars_ScoX1.multiIFO.length = 1;	// truncate to first detector
    pars_ScoX1.multiNoiseFloor.length = 1;	// truncate to first detector

    // compute metric using modern UniversalDopplerMetric module: (used in lalapps_FstatMetric_v2)
    DopplerPhaseMetric *metric_ScoX1;
    XLAL_CHECK ( (metric_ScoX1 = XLALComputeDopplerPhaseMetric ( &pars_ScoX1, edat )) != NULL, XLAL_EFUNC );

    // compute analytic metric computed from Eq.(47) in Leaci,Prix PRD91, 102003 (2015):
    gsl_matrix *g0_ij;
    XLAL_CHECK ( (g0_ij = gsl_matrix_calloc ( 6, 6 )) != NULL, XLAL_ENOMEM, "Failed to gsl_calloc a 6x6 matrix\n");
    gsl_matrix_set ( g0_ij, 0, 0, pow ( LAL_PI * TspanScoX1, 2 ) / 3.0 );
    gsl_matrix_set ( g0_ij, 1, 1, 2.0 * pow ( LAL_PI * Freq, 2 ) );
    gsl_matrix_set ( g0_ij, 2, 2, 2.0 * pow ( LAL_PI * Freq * asini * Omega, 2 ) );
    gsl_matrix_set ( g0_ij, 3, 3, 0.5 * pow ( Omega, 4 ) * pow ( Freq * asini, 2 ) * ( pow ( TspanScoX1, 2 ) / 12.0 + pow ( DeltaMidAsc, 2 ) ) );
    REAL8 gPAsc = LAL_PI * pow ( Freq * asini, 2 ) * pow ( Omega, 3 ) * DeltaMidAsc;
    gsl_matrix_set ( g0_ij, 2, 3, gPAsc );
    gsl_matrix_set ( g0_ij, 3, 2, gPAsc );
    gsl_matrix_set ( g0_ij, 4, 4, 0.5 * pow ( LAL_PI * Freq * asini, 2 ) );
    gsl_matrix_set ( g0_ij, 5, 5, 0.5 * pow ( LAL_PI * Freq * asini, 2 ) );

    GPMAT ( metric_ScoX1->g_ij, "%0.4e" );
    GPMAT ( g0_ij, "%0.4e" );

    // compare metrics against each other
    REAL8 diff, tolScoX1 = 0.05;
    XLAL_CHECK ( (diff = XLALCompareMetrics ( metric_ScoX1->g_ij, g0_ij )) < tolScoX1, XLAL_ETOL, "Error(gNum,gAn)= %g exceeds tolerance of %g\n", diff, tolScoX1 );
    XLALPrintWarning ("diff_Num_An = %e\n", diff );

    gsl_matrix_free ( g0_ij );
    XLALDestroyDopplerPhaseMetric ( metric_ScoX1 );
    XLALSegListClear ( &segListScoX1 );
  }