Example #1
0
xmlNodePtr LALInferenceVariableItem2VOTParamNode(LALInferenceVariableItem *const varitem)
{
  VOTABLE_DATATYPE vo_type;
  CHAR *unitName={0};
  UINT4 bufsize=1024;
  UINT4 required_size=0;
  CHAR *valString=XLALCalloc(bufsize,sizeof(CHAR));
  CHAR arraysize[32]="";

  switch(varitem->type)
  {
		  /* Special case for matrix */
		  case LALINFERENCE_gslMatrix_t:
				  return(XLALgsl_matrix2VOTNode(*(gsl_matrix **)varitem->value, varitem->name, unitName));
				  break;

		  /* Special case for string */
		  case LALINFERENCE_string_t:
				  return(XLALCreateVOTParamNode(varitem->name,unitName,VOT_CHAR,"*",*(char **)varitem->value));
				  break;

		  case LALINFERENCE_REAL8Vector_t:
		  {
				  REAL8Vector *vec=*(REAL8Vector **)varitem->value;
				  snprintf(arraysize,32,"%i",vec->length);
				  vo_type=VOT_REAL8;
				  break;
		  }
		  case LALINFERENCE_INT4Vector_t:
		  {
				  INT4Vector *vec=*(INT4Vector **)varitem->value;
				  snprintf(arraysize,32,"%i",vec->length);
				  vo_type=VOT_INT4;
				  break;
		  }
		  default:
		  /* Check the type of the item */
		  vo_type=LALInferenceVariableType2VOT(varitem->type);
  }
  if(vo_type>=VOT_DATATYPE_LAST){
		  XLALPrintError("%s: Unsupported LALInferenceVariableType %i\n",__func__,(int)varitem->type);
		  return NULL;
  }
  required_size=LALInferencePrintNVariableItem(valString, bufsize, varitem);
  if(required_size>bufsize){
		  bufsize=required_size;
		  valString=XLALRealloc(valString,bufsize*sizeof(CHAR));
		  required_size=LALInferencePrintNVariableItem(valString, bufsize, varitem);
  }
  xmlNodePtr node = XLALCreateVOTParamNode(varitem->name,unitName,vo_type,arraysize,valString);
  XLALFree(valString);
  return(node);
}
/**
 * \brief Serializes a \c PulsarSpins array into a VOTable XML %node
 *
 * This function takes a \c PulsarSpins array and serializes it into a VOTable
 * \c PARAM %node identified by the given name. The returned \c xmlNode can then be
 * embedded into an existing %node hierarchy.
 *
 * \param spins [in] Pointer to the \c PulsarSpins array to be serialized
 * \param name [in] Unique identifier of this particular \c PulsarSpins array instance
 *
 * \return A pointer to a \c xmlNode that holds the VOTable fragment that represents
 * the \c PulsarSpins array. In case of an error, a null-pointer is returned.\n
 * \b Important: the caller is responsible to free the allocated memory (when the
 * fragment isn't needed anymore) using \c xmlFreeNode. Alternatively, \c xmlFreeDoc
 * can be used later on when the returned fragment has been embedded in a XML document.
 *
 * \sa XLALCreateVOTParamNode
 *
 * \author Oliver Bock\n
 * Albert-Einstein-Institute Hannover, Germany
 */
xmlNodePtr XLALPulsarSpins2VOTNode(const PulsarSpins *const spins, const char *name)
{
    /* set up local variables */
    xmlNodePtr xmlParamNode = NULL;
    int i;

    CHAR spinArraySize[PULSARSPINSTR_MAXLEN] = {0};
    CHAR spinArrayString[PULSAR_MAX_SPINS*REAL8STR_MAXLEN] = {0};
    CHAR spinArrayStringItem[REAL8STR_MAXLEN] = {0};

    /* sanity checks */
    if(!spins || !(*spins)) {
        XLALPrintError("Invalid input parameter: spins\n");
        XLAL_ERROR_NULL(XLAL_EINVAL);
    }
    if(sizeof(*spins)/sizeof(REAL8) != PULSAR_MAX_SPINS) {
        XLALPrintError("Invalid input parameter: spins (actual size %i differs from defined size %i)\n", sizeof(*spins)/sizeof(REAL8), PULSAR_MAX_SPINS);
        XLAL_ERROR_NULL(XLAL_EINVAL);
    }
    if(!name || strlen(name) <= 0) {
        XLALPrintError("Invalid input parameter: name\n");
        XLAL_ERROR_NULL(XLAL_EINVAL);
    }

    /* parse input array */
    for(i = 0; i < PULSAR_MAX_SPINS; ++i) {
        if(snprintf(spinArrayStringItem, REAL8STR_MAXLEN, "%g", (*spins)[i]) < 0) {
            XLALPrintError("Invalid input parameter: spins[%i]\n", i);
            XLAL_ERROR_NULL(XLAL_EINVAL);
        }
        if(!strncat(spinArrayString, spinArrayStringItem, REAL8STR_MAXLEN)) {
            XLALPrintError("Couldn't serialize parameter: spins[%i]\n", i);
            XLAL_ERROR_NULL(XLAL_EFAILED);
        }
        /* add delimiter (SPACE)*/
        if(i<PULSAR_MAX_SPINS-1 && !strncat(spinArrayString, " ", 1)) {
            XLALPrintError("Couldn't add delimiter to parameter: spins[%i]\n", i);
            XLAL_ERROR_NULL(XLAL_EFAILED);
        }
    }

    /* set array size attribute */
    if(snprintf(spinArraySize, PULSARSPINSTR_MAXLEN, "%i", PULSAR_MAX_SPINS) < 0) {
        XLALPrintError("Couldn't prepare attribute: arraysize\n");
        XLAL_ERROR_NULL(XLAL_EFAILED);
    }

    /* set up PARAM node */
    xmlParamNode = XLALCreateVOTParamNode(name,
                                          NULL,
                                          VOT_REAL8,
                                          spinArraySize,
                                          spinArrayString);
    if(!xmlParamNode) {
        XLALPrintError("Couldn't create PARAM node: %s\n", name);
        XLAL_ERROR_NULL(XLAL_EFAILED);
    }

    /* return PARAM node (needs to be xmlFreeNode'd or xmlFreeDoc'd by caller!!!) */
    return xmlParamNode;
}
/**
 * \brief Serializes a \c PulsarDopplerParams structure into a VOTable XML %node
 *
 * This function takes a \c PulsarDopplerParams structure and serializes it into a VOTable
 * \c RESOURCE %node identified by the given name. The returned \c xmlNode can then be
 * embedded into an existing %node hierarchy or turned into a full VOTable document.
 *
 * \param pdp [in] Pointer to the \c PulsarDopplerParams structure to be serialized
 * \param name [in] Unique identifier of this particular \c PulsarDopplerParams structure instance
 *
 * \return A pointer to a \c xmlNode that holds the VOTable fragment that represents
 * the \c PulsarDopplerParams structure.
 * In case of an error, a null-pointer is returned.\n
 * \b Important: the caller is responsible to free the allocated memory (when the
 * fragment isn't needed anymore) using \c xmlFreeNode. Alternatively, \c xmlFreeDoc
 * can be used later on when the returned fragment has been embedded in a XML document.
 *
 * \sa XLALCreateVOTParamNode
 * \sa XLALCreateVOTResourceNode
 * \sa XLALCreateVOTDocumentFromTree
 *
 * \author Oliver Bock\n
 * Albert-Einstein-Institute Hannover, Germany
 */
xmlNodePtr XLALPulsarDopplerParams2VOTNode(const PulsarDopplerParams *const pdp, const char *name)
{
    /* set up local variables */
    xmlNodePtr xmlParentNode = NULL;
    xmlNodePtr xmlChildNode = NULL;
    xmlNodePtr xmlChildNodeList = NULL;
    xmlNodePtr xmlCurrentChildNode = NULL;

    /* check and convert input parameters */
    XLAL_CHECK_NULL ( pdp != NULL, XLAL_EINVAL );

    CHAR Alpha[REAL8STR_MAXLEN] = {0};
    if( snprintf(Alpha, REAL8STR_MAXLEN, "%g", pdp->Alpha) < 0) {
        XLALPrintError("Invalid input parameter: PulsarDopplerParams->Alpha\n");
        XLAL_ERROR_NULL(XLAL_EINVAL);
    }
    CHAR Delta[REAL8STR_MAXLEN] = {0};
    if( snprintf(Delta, REAL8STR_MAXLEN, "%g", pdp->Delta) < 0) {
        XLALPrintError("Invalid input parameter: PulsarDopplerParams->Delta\n");
        XLAL_ERROR_NULL(XLAL_EINVAL);
    }
    CHAR argp[REAL8STR_MAXLEN] = {0};
    if( snprintf(argp, REAL8STR_MAXLEN, "%g", pdp->argp) < 0) {
        XLALPrintError("Invalid input parameter: PulsarDopplerParams->argp\n");
        XLAL_ERROR_NULL(XLAL_EINVAL);
    }
    CHAR asini[REAL8STR_MAXLEN] = {0};
    if( snprintf(asini, REAL8STR_MAXLEN, "%g", pdp->asini) < 0) {
        XLALPrintError("Invalid input parameter: PulsarDopplerParams->asini\n");
        XLAL_ERROR_NULL(XLAL_EINVAL);
    }
    CHAR ecc[REAL8STR_MAXLEN] = {0};
    if( snprintf(ecc, REAL8STR_MAXLEN, "%g", pdp->ecc) < 0) {
        XLALPrintError("Invalid input parameter: PulsarDopplerParams->ecc\n");
        XLAL_ERROR_NULL(XLAL_EINVAL);
    }
    CHAR period[REAL8STR_MAXLEN] = {0};
    if( snprintf(period, REAL8STR_MAXLEN, "%g", pdp->period) < 0) {
        XLALPrintError("Invalid input parameter: PulsarDopplerParams->period\n");
        XLAL_ERROR_NULL(XLAL_EINVAL);
    }

    if(!name || strlen(name) <= 0) {
        XLALPrintError("Invalid input parameter: name\n");
        XLAL_ERROR_NULL(XLAL_EINVAL);
    }

    /* ----- set up PARAM node (Alpha) */
    xmlChildNode = XLALCreateVOTParamNode("Alpha",
                                          "rad",
                                          VOT_REAL8,
                                          NULL,
                                          Alpha);
    if(!xmlChildNode) {
        XLALPrintError("Couldn't create PARAM node: %s.Alpha\n", name);
        XLAL_ERROR_NULL(XLAL_EFAILED);
    }

    /* initialize child node list with first child */
    xmlChildNodeList = xmlChildNode;
    xmlCurrentChildNode = xmlChildNodeList;

    /* ----- set up PARAM node (Delta) */
    xmlChildNode = XLALCreateVOTParamNode("Delta",
                                          "rad",
                                          VOT_REAL8,
                                          NULL,
                                          Delta);
    if(!xmlChildNode) {
        /* clean up */
        xmlFreeNodeList(xmlChildNodeList);
        XLALPrintError("Couldn't create PARAM node: %s.Delta\n", name);
        XLAL_ERROR_NULL(XLAL_EFAILED);
    }

    /* add child as next sibling to child node list */
    xmlCurrentChildNode->next = xmlChildNode;
    xmlCurrentChildNode = xmlCurrentChildNode->next;

    /* ----- set up PARAM node (fkdot) */
    xmlChildNode = XLALPulsarSpins2VOTNode(&pdp->fkdot, "fkdot");
    if(!xmlChildNode) {
        /* clean up */
        xmlFreeNodeList(xmlChildNodeList);
        XLALPrintError("Couldn't create PARAM node: %s.fkdot\n", name);
        XLAL_ERROR_NULL(XLAL_EFAILED);
    }

    /* add child as next sibling to child node list */
    xmlCurrentChildNode->next = xmlChildNode;
    xmlCurrentChildNode = xmlCurrentChildNode->next;

    // ---------- handle binary-orbital parameters ----------
    /* ----- set up PARAM node (argp) */
    xmlChildNode = XLALCreateVOTParamNode("argp",
                                          "rad",
                                          VOT_REAL8,
                                          NULL,
                                          argp);
    if(!xmlChildNode) {
        /* clean up */
        xmlFreeNodeList(xmlChildNodeList);
        XLALPrintError("Couldn't create PARAM node: %s.argp\n", name);
        XLAL_ERROR_NULL(XLAL_EFAILED);
    }

    /* add child as next sibling to child node list */
    xmlCurrentChildNode->next = xmlChildNode;
    xmlCurrentChildNode = xmlCurrentChildNode->next;

    /* ----- set up PARAM node (asini) */
    xmlChildNode = XLALCreateVOTParamNode("asini",
                                          "s",
                                          VOT_REAL8,
                                          NULL,
                                          asini);
    if(!xmlChildNode) {
        /* clean up */
        xmlFreeNodeList(xmlChildNodeList);
        XLALPrintError("Couldn't create PARAM node: %s.asini\n", name);
        XLAL_ERROR_NULL(XLAL_EFAILED);
    }


    /* add child as next sibling to child node list */
    xmlCurrentChildNode->next = xmlChildNode;
    xmlCurrentChildNode = xmlCurrentChildNode->next;

    /* ----- set up PARAM node (ecc) */
    xmlChildNode = XLALCreateVOTParamNode("ecc",
                                          NULL,
                                          VOT_REAL8,
                                          NULL,
                                          ecc);
    if(!xmlChildNode) {
        /* clean up */
        xmlFreeNodeList(xmlChildNodeList);
        XLALPrintError("Couldn't create PARAM node: %s.ecc\n", name);
        XLAL_ERROR_NULL(XLAL_EFAILED);
    }

    /* add child as next sibling to child node list */
    xmlCurrentChildNode->next = xmlChildNode;
    xmlCurrentChildNode = xmlCurrentChildNode->next;

    /* ----- set up PARAM node (period) */
    xmlChildNode = XLALCreateVOTParamNode("period",
                                          "s",
                                          VOT_REAL8,
                                          NULL,
                                          period);
    if(!xmlChildNode) {
        /* clean up */
        xmlFreeNodeList(xmlChildNodeList);
        XLALPrintError("Couldn't create PARAM node: %s.period\n", name);
        XLAL_ERROR_NULL(XLAL_EFAILED);
    }

    /* add child as next sibling to child node list */
    xmlCurrentChildNode->next = xmlChildNode;
    xmlCurrentChildNode = xmlCurrentChildNode->next;

    /* ----- set up RESOURCE node (refTime)*/
    xmlChildNode = XLALLIGOTimeGPS2VOTNode(&pdp->refTime, "refTime" );
    if(!xmlChildNode) {
        /* clean up */
        xmlFreeNodeList(xmlChildNodeList);
        XLALPrintError("Couldn't create RESOURCE node: %s.refTime\n", name );
        XLAL_ERROR_NULL(XLAL_EFAILED);
    }

    /* add child as next sibling to child node list */
    xmlCurrentChildNode->next = xmlChildNode;
    xmlCurrentChildNode = xmlCurrentChildNode->next;


     /* ----- set up RESOURCE node (tp)*/
    xmlChildNode = XLALLIGOTimeGPS2VOTNode(&pdp->tp, "tp");
    if(!xmlChildNode) {
        /* clean up */
        xmlFreeNodeList(xmlChildNodeList);
        XLALPrintError("Couldn't create RESOURCE node: tp\n");
        XLAL_ERROR_NULL(XLAL_EFAILED);
    }

    /* add child as next sibling to child node list */
    xmlCurrentChildNode->next = xmlChildNode;
    xmlCurrentChildNode = xmlCurrentChildNode->next;
    // ---------- END: binary-orbital parameters ----------

    /* set up parent RESOURCE node*/
    xmlParentNode = XLALCreateVOTResourceNode("PulsarDopplerParams", name, xmlChildNodeList);
    if(!xmlParentNode) {
        /* clean up */
        xmlFreeNodeList(xmlChildNodeList);
        XLALPrintError("Couldn't create RESOURCE node: %s\n", name);
        XLAL_ERROR_NULL(XLAL_EFAILED);
    }

    /* return RESOURCE node (needs to be xmlFreeNode'd or xmlFreeDoc'd by caller!!!) */
    return xmlParentNode;

} // XLALPulsarDopplerParams2VOTNode()
/**
 * \brief Serializes a \c LIGOTimeGPS structure into a VOTable XML %node
 *
 * This function takes a \c LIGOTimeGPS structure and serializes it into a VOTable
 * \c RESOURCE %node identified by the given name. The returned \c xmlNode can then be
 * embedded into an existing %node hierarchy or turned into a full VOTable document.
 *
 * \param ltg [in] Pointer to the \c LIGOTimeGPS structure to be serialized
 * \param name [in] Unique identifier of this particular \c LIGOTimeGPS structure instance
 *
 * \return A pointer to a \c xmlNode that holds the VOTable fragment that represents
 * the \c LIGOTimeGPS structure.
 * In case of an error, a null-pointer is returned.\n
 * \b Important: the caller is responsible to free the allocated memory (when the
 * fragment isn't needed anymore) using \c xmlFreeNode. Alternatively, \c xmlFreeDoc
 * can be used later on when the returned fragment has been embedded in a XML document.
 *
 * \sa XLALCreateVOTParamNode
 * \sa XLALCreateVOTResourceNode
 * \sa XLALCreateVOTDocumentFromTree
 *
 * \author Oliver Bock\n
 * Albert-Einstein-Institute Hannover, Germany
 */
xmlNodePtr XLALLIGOTimeGPS2VOTNode(const LIGOTimeGPS *const ltg, const char *name)
{
    /* set up local variables */
    xmlNodePtr xmlParentNode = NULL;
    xmlNodePtr xmlChildNode = NULL;
    xmlNodePtr xmlChildNodeList = NULL;

    CHAR gpsSecondsBuffer[INT4STR_MAXLEN] = {0};
    CHAR gpsNanoSecondsBuffer[INT4STR_MAXLEN] = {0};

    /* check and prepare input parameters */
    if(!ltg || snprintf(gpsSecondsBuffer, INT4STR_MAXLEN, "%i", ltg->gpsSeconds) < 0) {
        XLALPrintError("Invalid input parameter: LIGOTimeGPS->gpsSeconds\n");
        XLAL_ERROR_NULL(XLAL_EINVAL);
    }
    if(!ltg || snprintf(gpsNanoSecondsBuffer, INT4STR_MAXLEN, "%i", ltg->gpsNanoSeconds) < 0) {
        XLALPrintError("Invalid input parameter: LIGOTimeGPS->gpsNanoSeconds\n");
        XLAL_ERROR_NULL(XLAL_EINVAL);
    }
    if(!name || strlen(name) <= 0) {
        XLALPrintError("Invalid input parameter: name\n");
        XLAL_ERROR_NULL(XLAL_EINVAL);
    }

    /* set up RESOURCE node child (first PARAM) */
    xmlChildNode = XLALCreateVOTParamNode("gpsSeconds",
                                          "s",
                                          VOT_INT4,
                                          NULL,
                                          gpsSecondsBuffer);
    if(!xmlChildNode) {
        XLALPrintError("Couldn't create PARAM node: , %s.gpsSeconds\n", name);
        XLAL_ERROR_NULL(XLAL_EFAILED);
    }

    /* initialize child node list with first child */
    xmlChildNodeList = xmlChildNode;

    /* set up RESOURCE node child (second PARAM) */
    xmlChildNode = XLALCreateVOTParamNode("gpsNanoSeconds",
                                          "ns",
                                          VOT_INT4,
                                          NULL,
                                          gpsNanoSecondsBuffer);
    if(!xmlChildNode) {
        /* clean up */
        xmlFreeNodeList(xmlChildNodeList);
        XLALPrintError("Couldn't create PARAM node: %s.gpsNanoSeconds\n", name);
        XLAL_ERROR_NULL(XLAL_EFAILED);
    }

    /* add child as first sibling to child node list */
    xmlChildNodeList->next = xmlChildNode;

    /* set up RESOURCE node*/
    xmlParentNode = XLALCreateVOTResourceNode("LIGOTimeGPS", name, xmlChildNodeList);
    if(!xmlParentNode) {
        /* clean up */
        xmlFreeNodeList(xmlChildNodeList);
        XLALPrintError("Couldn't create RESOURCE node: %s\n", name);
        XLAL_ERROR_NULL(XLAL_EFAILED);
    }

    /* return RESOURCE node (needs to be xmlFreeNode'd or xmlFreeDoc'd by caller!!!) */
    return xmlParentNode;
}
/**
 * \brief Serializes a \c gsl_matrix into a VOTable XML %node
 *
 * This function takes a \c gsl_matrix pointer and serializes it into a VOTable
 * \c PARAM %node identified by the given name. The returned \c xmlNode can then be
 * embedded into an existing %node hierarchy.
 *
 * \return A pointer to a \c xmlNode that holds the VOTable fragment that represents
 * the \c gsl_matrix. In case of an error, a null-pointer is returned.\n
 *
 * \note All matrix elements are written with maximal precision "%.16g" for a double
 *
 * \note We use a VOTable \<PARAM\> element with arraysize=dim1xdim2. The arraysize-convention
 * is that the the first index varies fastest, while the last index is the slowest-varying.
 * We therefore write the matrix in *transposed* order, ie. "cols x rows", such that the
 * fastest-varying index is the column-index!
 *
 * \b Important: the caller is responsible to free the allocated memory (when the
 * fragment isn't needed anymore) using \c xmlFreeNode. Alternatively, \c xmlFreeDoc
 * can be used later on when the returned fragment has been embedded in a XML document.
 *
 * \sa XLALCreateVOTParamNode
 *
 * \author Reinhard Prix\n
 * Albert-Einstein-Institute Hannover, Germany
 */
xmlNodePtr
XLALgsl_matrix2VOTNode(const gsl_matrix *matrix,	/**< [in] input gsl_matrix to serialize */
                       const CHAR *name,		/**< [in] Unique identifier for this gsl_matrix */
                       const CHAR *unitName		/**< [in] optional unit-name (can be NULL) */
                       )
{
  xmlNodePtr xmlParamNode = NULL;
  CHAR arraySizeStr[2*INT4STR_MAXLEN+1] = {0}; 	/* buffer to hold argument to "arraysize" attribute */
  CHAR REAL8Str[REAL8STR_MAXLEN] = {0};		/* buffer to hold string representations of ONE REAL8 element */
  int arrayStrLen;
  CHAR *arrayStr = NULL;			/* holds full list of dim1 x dim2 REAL8 strings */

  int row, col, numRows, numCols;

  /* sanity checks */
  if ( !matrix || !matrix->size1 || !matrix->size2 ) {
    XLALPrintError("%s: Invalid NULL or empty/malformed input: matrix\n\n", __func__);
    XLAL_ERROR_NULL (XLAL_EINVAL);
  }
  if ( !name || strlen(name) <= 0) {
    XLALPrintError("%s: Invalid NULL or empty input parameter: name\n\n", __func__);
    XLAL_ERROR_NULL(XLAL_EINVAL);
  }

  /* get input matrix dimensions */
  numRows = matrix->size1;	/* guaranteed to be >= 1 */
  numCols = matrix->size2;	/* guaranteed to be >= 1 */

  /* prepare array size attribute.
   * As explained in the header, we write the step through
   * the matrix with rows varying slowest, columns fastest, which corresponds to
   * arraysize="cols x rows" !
   */
  if ( snprintf ( arraySizeStr, sizeof(arraySizeStr), "%dx%d", numCols, numRows ) < 0 ) {
    XLALPrintError("%s: snprintf() failed for arraySizeStr (%dx%d).\n\n", __func__, numCols, numRows );
    XLAL_ERROR_NULL (XLAL_EFAILED);
  }

  /* prepare string to hold array of numbers */
  arrayStrLen = numRows * numCols * REAL8STR_MAXLEN;
  if ( (arrayStr = XLALCalloc( 1, arrayStrLen )) == NULL ) {
    XLALPrintError ("%s: Failed to XLALCalloc(1, %d).\n\n", __func__, arrayStrLen );
    XLAL_ERROR_NULL(XLAL_ENOMEM );
  }

  /* convert all matrix elements to strings and append to arrayStr */
  for ( row = 0; row < numRows; row++ )
    {
      for ( col = 0; col < numCols; col ++ )
	{
	  /* add matrix element [row,col] to arrayStr */
	  if( snprintf ( REAL8Str, sizeof(REAL8Str), "%.16g", gsl_matrix_get ( matrix, row, col ) ) < 0 ) {
	    XLALFree ( arrayStr );
	    XLALPrintError("%s: failed to convert matrix element to string: vect[%d,%d]\n", __func__, row, col);
	    XLAL_ERROR_NULL ( XLAL_EINVAL );
	  }
	  strcat ( arrayStr, REAL8Str );

	  /* add delimiter (SPACE)*/
	  if ( col < numCols-1 ) {
	    strcat ( arrayStr, " ");
	  }

	} /* for col < numCols */

      /* add row-delimiter (newline!) */
      if ( row < numRows-1 ) {
	strcat ( arrayStr, "; ");
      }

    } /* for rows < numRows */

  /* assemble PARAM node */
  if ( (xmlParamNode = XLALCreateVOTParamNode(name, unitName, VOT_REAL8, arraySizeStr, arrayStr )) == NULL ){
    XLALFree ( arrayStr );
    XLALPrintError("%s: XLALCreateVOTParamNode() failed to create PARAM node: '%s'. xlalErrno = %d\n", __func__, name, xlalErrno);
    XLAL_ERROR_NULL (XLAL_EFUNC);
  }

  XLALFree ( arrayStr );

  /* return PARAM node (needs to be xmlFreeNode'd or xmlFreeDoc'd by caller!!!) */
  return xmlParamNode;

} /* XLALgsl_matrix2VOTNode() */
/**
 * \brief Serializes a \c gsl_vector into a VOTable XML %node
 *
 * This function takes a \c gsl_vector pointer and serializes it into a VOTable
 * \c PARAM %node identified by the given name. The returned \c xmlNode can then be
 * embedded into an existing %node hierarchy.
 *
 * \return A pointer to a \c xmlNode that holds the VOTable fragment that represents
 * the \c gsl_vector. In case of an error, a null-pointer is returned.\n
 *
 * \note All matrix elements are written with maximal precision "%.16g" for a double
 *
 * \b Important: the caller is responsible to free the allocated memory (when the
 * fragment isn't needed anymore) using \c xmlFreeNode. Alternatively, \c xmlFreeDoc
 * can be used later on when the returned fragment has been embedded in a XML document.
 *
 * \sa XLALCreateVOTParamNode
 *
 * \author Reinhard Prix\n
 * Albert-Einstein-Institute Hannover, Germany
 */
xmlNodePtr
XLALgsl_vector2VOTNode(const gsl_vector *vect,	/**< [in] input gsl_vector to serialize */
                       const CHAR *name,	/**< [in] Unique identifier for this gsl_vector */
                       const CHAR *unitName	/**< [in] optional unit-name (can be NULL) */
                       )
{
  xmlNodePtr xmlParamNode = NULL;
  CHAR arraySizeStr[INT4STR_MAXLEN] = {0}; 	/* buffer to hold argument to "arraysize" attribute */
  CHAR REAL8Str[REAL8STR_MAXLEN] = {0};		/* buffer to hold string representations of ONE REAL8 element */
  int arrayStrLen;
  CHAR *arrayStr = NULL;			/* holds full list of dim REAL8 strings */
  int i, dim;

  /* sanity checks */
  if ( !vect || !vect->size ) {
    XLALPrintError("%s: Invalid NULL or empty input: vect\n\n", __func__);
    XLAL_ERROR_NULL (XLAL_EINVAL);
  }
  if ( !name || strlen(name) <= 0) {
    XLALPrintError("%s: Invalid NULL or empty input parameter: name\n\n", __func__);
    XLAL_ERROR_NULL(XLAL_EINVAL);
  }

  /* get input vector dimension */
  dim = vect->size;	/* guaranteed to be >= 1 */

  /* prepare array size attribute */
  if ( snprintf ( arraySizeStr, sizeof(arraySizeStr), "%d", dim ) < 0 ) {
    XLALPrintError("%s: snprintf() failed for arraySizeStr.\n\n", __func__ );
    XLAL_ERROR_NULL (XLAL_EFAILED);
  }

  /* prepare string to hold array of numbers */
  arrayStrLen = dim * REAL8STR_MAXLEN;
  if ( (arrayStr = XLALCalloc( 1, arrayStrLen )) == NULL ) {
    XLALPrintError ("%s: Failed to XLALCalloc(1, %d).\n\n", __func__, arrayStrLen );
    XLAL_ERROR_NULL(XLAL_ENOMEM );
  }

  for ( i = 0; i < dim; i++ )
    {
      /* add vector element [i] to arrayStr */
      if( snprintf ( REAL8Str, sizeof(REAL8Str), "%.16g", gsl_vector_get ( vect, i ) ) < 0 ) {
	XLALFree ( arrayStr );
	XLALPrintError("%s: failed to convert vector element to string: vect[%d]\n", __func__, i);
	XLAL_ERROR_NULL ( XLAL_EINVAL );
      }
      strcat ( arrayStr, REAL8Str );

      /* add delimiter (SPACE)*/
      if ( i < dim-1 )
	strcat ( arrayStr, " ");

    } /* for i < dim */

  /* set up PARAM node */
  if ( (xmlParamNode = XLALCreateVOTParamNode(name, unitName, VOT_REAL8, arraySizeStr, arrayStr )) == NULL ){
    XLALFree ( arrayStr );
    XLALPrintError("%s: XLALCreateVOTParamNode() failed to create PARAM node: '%s'. xlalErrno = %d\n", __func__, name, xlalErrno);
    XLAL_ERROR_NULL (XLAL_EFUNC);
  }

  XLALFree ( arrayStr );

  /* return PARAM node (needs to be xmlFreeNode'd or xmlFreeDoc'd by caller!!!) */
  return xmlParamNode;

} /* XLALgsl_vector2VOTNode() */