diff --git a/buildfiles/Linux.mak b/buildfiles/Linux.mak index e447a30..a1dce2c 100644 --- a/buildfiles/Linux.mak +++ b/buildfiles/Linux.mak @@ -10,15 +10,16 @@ export MD5SUM TARG=libdas3.0 SRCS:=das1.c array.c buffer.c builder.c cli.c codec.c credentials.c dataset.c \ -datum.c descriptor.c dft.c dimension.c dsdf.c encoding.c frame.c http.c io.c json.c \ -log.c node.c oob.c operator.c packet.c plane.c processor.c property.c serial.c \ -send.c stream.c time.c tt2000.c units.c utf8.c util.c value.c variable.c vector.c +datum.c descriptor.c dft.c dimension.c dsdf.c encoding.c frame.c http.c io.c \ +iterator.c json.c log.c node.c oob.c operator.c packet.c plane.c processor.c \ +property.c serial.c send.c stream.c time.c tt2000.c units.c utf8.c util.c \ +value.c variable.c vector.c HDRS:=defs.h time.h das1.h util.h log.h buffer.h utf8.h value.h units.h \ tt2000.h operator.h datum.h frame.h array.h encoding.h variable.h descriptor.h \ dimension.h dataset.h plane.h packet.h stream.h processor.h property.h oob.h \ - io.h builder.h dsdf.h credentials.h http.h dft.h json.h node.h cli.h send.h \ - vector.h serial.h codec.h core.h + io.h iterator.h builder.h dsdf.h credentials.h http.h dft.h json.h node.h cli.h \ + send.h vector.h serial.h codec.h core.h ifeq ($(SPICE),yes) SRCS:=$(SRCS) spice.c diff --git a/das2/core.h b/das2/core.h index 00698cd..428c8f3 100644 --- a/das2/core.h +++ b/das2/core.h @@ -221,6 +221,7 @@ #include #include #include +#include #include #include #include diff --git a/das2/dataset.c b/das2/dataset.c index 2f6ef0d..8192448 100644 --- a/das2/dataset.c +++ b/das2/dataset.c @@ -1,18 +1,18 @@ -/* Copyright (C) 2017-2018 Chris Piker +/* Copyright (C) 2017-2024 Chris Piker * - * This file is part of libdas2, the Core Das2 C Library. + * This file is part of das2C, the Core Das C Library. * - * Libdas2 is free software; you can redistribute it and/or modify it under + * Das2C is free software; you can redistribute it and/or modify it under * the terms of the GNU Lesser General Public License version 2.1 as published * by the Free Software Foundation. * - * Libdas2 is distributed in the hope that it will be useful, but WITHOUT ANY + * Das2C is distributed in the hope that it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for * more details. * * You should have received a copy of the GNU Lesser General Public License - * version 2.1 along with libdas2; if not, see . + * version 2.1 along with das2C; if not, see . */ #define _POSIX_C_SOURCE 200112L @@ -42,11 +42,25 @@ size_t DasDs_numDims(const DasDs* pThis, enum dim_type vt){ return uDims; } -const DasDim* DasDs_getDimByIdx(const DasDs* pThis, size_t idx, enum dim_type vt) +const DasDim* DasDs_getDim(const DasDs* pThis, const char* sId, enum dim_type dmt) +{ + const char* sDimId = NULL; + for(size_t u = 0; u < pThis->uDims; ++u){ + if(pThis->lDims[u]->dtype != dmt) + continue; + + sDimId = DasDim_id(pThis->lDims[u]); + if(strcasecmp(sId, sDimId) == 0) + return pThis->lDims[u]; + } + return NULL; +} + +const DasDim* DasDs_getDimByIdx(const DasDs* pThis, size_t idx, enum dim_type dmt) { size_t uTypeIdx = 0; for(size_t u = 0; u < pThis->uDims; ++u){ - if(pThis->lDims[u]->dtype == vt){ + if(pThis->lDims[u]->dtype == dmt){ if(uTypeIdx == idx) return pThis->lDims[u]; else ++uTypeIdx; } @@ -130,80 +144,51 @@ ptrdiff_t DasDs_lengthIn(const DasDs* pThis, int nIdx, ptrdiff_t* pLoc) return nLengthIn; } -/* ************************************************************************* */ -/* The iteration support */ +bool DasDs_cubicCoords(const DasDs* pThis, const DasDim** pCoords) +{ + ptrdiff_t aDsShape[DASIDX_MAX] = DASIDX_INIT_UNUSED; + int nRank = DasDs_shape(pThis, aDsShape); -void dasds_iter_init(dasds_iterator* pIter, const DasDs* pDs){ - - memset(pIter, 0, sizeof(dasds_iterator)); - - pIter->rank = DasDs_shape(pDs, pIter->shape); - pIter->pDs = pDs; - - pIter->ragged = false; - for(int i = 0; i < pIter->rank; ++i){ - if((pIter->shape[i] == DASIDX_RAGGED)||(pIter->shape[i] == DASIDX_RAGGED)){ - pIter->ragged = true; - break; - } - } - - /* Start off index at all zeros, which memset insures above */ - - /* If I'm ragged I'm going to need the size of the last index at the - * lowest point of all previous indexes, get that. */ - if(pIter->ragged){ - pIter->nLenIn = DasDs_lengthIn(pDs, pIter->rank - 1, pIter->index); - if(pIter->nLenIn < 0) pIter->done = true; - } -} + for(int i = 0; i < nRank; ++i){ /* For each index... */ -bool dasds_iter_next(dasds_iterator* pIter){ - - if(! pIter->ragged){ - /* Quicker function for CUBIC datasets */ - for(int iDim = pIter->rank - 1; iDim >= 0; --iDim){ - if(pIter->index[iDim] < (pIter->shape[iDim] - 1)){ - pIter->index[iDim] += 1; - return true; + bool bGotIt = false; + for(int c = 0; c < pThis->uDims; ++c){ /* for each coordinate */ + + const DasDim* pCoord = pThis->lDims[c]; + + if(pCoord->dtype != DASDIM_COORD) continue; + + bool bUsed = false; /* See if already used for lower index*/ + for(int j = 0; j < i; ++j){ + if(pCoords[j] == pCoord) + bUsed = true; } - else{ - pIter->index[iDim] = 0; + if(bUsed) continue; + + if(DasDim_degenerate(pCoord, i)) + continue; /* Doesn't depend on this idx */ + + /* make sure it depends only on this index */ + bool bOnlyMe = true; + for(int j = 0; j < nRank; ++j){ + if(j == i) continue; + + if(! DasDim_degenerate(pCoord, j)){ + bOnlyMe = false; + break; + } } - } - - pIter->done = true; - return false; - } - - /* I'm ragged so I can't use the generic shape function, but I can - * at least save off the length of the last index at this point - * and only change it when a roll occurs */ - ptrdiff_t nLenInIdx = 0; - for(int iDim = pIter->rank - 1; iDim >= 0; --iDim){ + if(!bOnlyMe) + continue; - if(iDim == (pIter->rank - 1)) - nLenInIdx = pIter->nLenIn; - else - nLenInIdx = DasDs_lengthIn(pIter->pDs, iDim, pIter->index); - - if(pIter->index[iDim] < (nLenInIdx - 1)){ - pIter->index[iDim] += 1; - - /* If bumping an index that's not the last, recompute the length - * of the last run */ - if(iDim < (pIter->rank - 1)) - pIter->nLenIn = DasDs_lengthIn(pIter->pDs, pIter->rank - 1, pIter->index); - - return true; - } - else{ - pIter->index[iDim] = 0; + pCoords[i] = pCoord; + bGotIt = true; + break; } + + if(!bGotIt) return false; } - - pIter->done = true; - return false; + return true; } /* ************************************************************************* */ diff --git a/das2/dataset.h b/das2/dataset.h index 6293456..81929c0 100644 --- a/das2/dataset.h +++ b/das2/dataset.h @@ -1,4 +1,4 @@ -/* Copyright (C) 2017-2018 Chris Piker +/* Copyright (C) 2017-2024 Chris Piker * * This file is part of das2C, the Core Das2 C Library. * @@ -16,7 +16,7 @@ */ -/** @file dataset.h Objects which define a iteration space */ +/** @file dataset.h Objects which correlate arrays in index space */ #ifndef _das_dataset_h_ #define _das_dataset_h_ @@ -343,96 +343,6 @@ DAS_API int DasDs_shape(const DasDs* pThis, ptrdiff_t* pShape); */ DAS_API ptrdiff_t DasDs_lengthIn(const DasDs* pThis, int nIdx, ptrdiff_t* pLoc); -/** Dataset iterator structure. - * - * Since dataset rank and shape is a union of the shape of it's components - * iterating over datasets can be tricky. This structure and it's associated - * functions are provided to simplify this task. Usage is demonstrated by - * the example below: - * - * @code - * // Assume a dataset with time, amplitude and frequency dimensions but with - * // arbitrary shape in index space. - * - * // pDs is a pointer to a das dataset - * - * DasDim* pDimTime = DasDs_getDimById(pDs, "time"); - * DasVar* pVarTime = DasDim_getPointVar(pDimTime); - * - * DasDim* pDimFreq = DasDs_getDimById(pDs, "frequency"); - * DasVar* pVarFreq = DasDim_getPointVar(pDimFreq); - * - * DasDim* pDimAmp = DasDs_getDimById(pDs, "e_spec_dens"); - * DasVar* pVarAmp = DasDim_getPointVar(pDimAmp); - * - * dasds_iterator iter; - * das_datum set[3]; - * - * for(dasds_iter_init(&iter, pDs); !iter.done; dasds_iter_next(&iter)){ - * - * DasVar_getDatum(pVarTime, iter.index, set); - * DasVar_getDatum(pVarFreq, iter.index, set + 1); - * DasVar_getDatum(pVarAmp, iter.index, set + 2); - * - * // Plot, or bin, or what-have-you, the triplet here. - * // Plot() is not a real function in the libdas2 C API - * Plot(set); - * } - * - * @endcode - */ -typedef struct dasds_iterator_t{ - - /** If true the value in index is valid, false otherwise */ - bool done; - - /** A dataset bulk iteration index suitable for use in DasVar functions like - * ::DasVar_getDatum */ - ptrdiff_t index[DASIDX_MAX]; - - int rank; - ptrdiff_t shape[DASIDX_MAX]; /* Used for CUBIC datasets */ - ptrdiff_t nLenIn; /* Used for ragged datasets */ - bool ragged; - const DasDs* pDs; -} dasds_iterator; - -/** Initialize a const dataset iterator - * - * The initialized iterator is safe to use for datasets that are growing - * as it will not exceed the valid index range of the dataset at the time - * this function was called. However, if the dataset shrinks during iteration - * das_iter_next() could overstep the array bounds. - * - * For usage see the example in ::das_iterator - * - * @param pIter A pointer to an iterator, will be initialize to index 0 - * - * @param pDs A pointer to a dataset. If the dataset changes while the - * iterator is in use invalid memory access could occur - * - * @memberof dasds_iterator - */ -DAS_API void dasds_iter_init(dasds_iterator* pIter, const DasDs* pDs); - -/** Increment the iterator's index by one position, rolling as needed at - * data boundaries. - * - * For efficiency this function does not re-check array bounds on each call - * a slower but safer version of this function could be created if needed. - * - * For usage see the example in ::das_iterator - * - * @param pIter A pointer to an iterator. The index member of the iterator - * will be incremented. - * - * @return true if the new index is within range, false if the index could not - * be incremented without producing an invalid location. - * - * @memberof dasds_iterator - */ -DAS_API bool dasds_iter_next(dasds_iterator* pIter); - /** Get the data set group id * @@ -463,7 +373,7 @@ DAS_API bool dasds_iter_next(dasds_iterator* pIter); /** Get the rank of a dataset * - * A dataset's rank is one of it's key immutable properties. It defines the + * A dataset's rank is one of it's key properties. It defines the * maximum number of valid external indicies for all included variables. * Any physical dimension included in the dataset will have the same rank * as the dataset. Any variable includid in those physical dimensions will @@ -746,16 +656,19 @@ DAS_API DasErrCode DasDs_addDim(DasDs* pThis, DasDim* pDim); * @return The number of data functions provided for a dataset. * @memberof DasDs */ -DAS_API size_t DasDs_numDims(const DasDs* pThis, enum dim_type vt); +DAS_API size_t DasDs_numDims(const DasDs* pThis, enum dim_type dmt); /** Get a dimension by it's basic kind * * @param sDim The general dimension type, like time, position, voltage, etc. + * The comparison to Dimension IDs is not case sensitive. * * @memberof DasDs */ -DAS_API const DasDim* DasDs_getDim(const DasDs* pThis, const char* sDim); +DAS_API const DasDim* DasDs_getDim( + const DasDs* pThis, const char* sDim, enum dim_type dmt +); /** Get a dimension by index * @param pThis a pointer to a dataset structure @@ -789,13 +702,37 @@ DAS_API const DasDim* DasDs_getDimById(const DasDs* pThis, const char* sId); DAS_API char* DasDs_toStr(const DasDs* pThis, char* sBuf, int nLen); +/** Get coordinate dimensions that satisfy the cubic dataset condition. + * + * Cubic datasets have one coordinate physical dimension for each dataset + * array dimension *and* all coordinate variables are rank 1. This is a + * very common condition, in fact whole libraries are based on the + * assumption that it's always satisfied. Das2C does not make this + * assumption up front. + * + * @param pThis A das dataset object + + * @param[out] pCoords a pointer to a buffer to hold coordinate dimension + * object pointers, at least dataset Rank elements long. See + * DasDs_shape() to get the dataset rank. On a successful + * call, there will be one coordinate dimension pointer + * in each location in pCoords. + * + * @return True if a set of coordinates that are orthogonal in index space + * exist for this dataset. False otherwise. + */ +bool DasDs_cubicCoords(const DasDs* pThis, const DasDim** pCoords); /* Ideas I'm still working on... +bool DataGen_grid(const DataSet* pDataset); + +const DataSet** Dg_griddedIn(const DataSet* pDataset); + / * The two functions below are really useful but I'll need to crack open a double pack of Flex and Bison to get it done so I'm punting for now. * / - + / * Ex Expression: $spec_dens[i][j][k] * / const Function* Dataset_evalDataExp(Dataset* pThis, const char* sExpression); @@ -803,37 +740,6 @@ const Function* Dataset_evalDataExp(Dataset* pThis, const char* sExpression); const Function* Dataset_evalCoordExp(Dataset* pThis, const char* sExpression); -/ ** - * - * This function answers the question by either provided the spanning set of - * coordinates or returning nothing. - * For a dataset to be defined - * on a coordinate grid there must exist one coordinate set for each index in - * the data set and each coordinate must be a function of only one index. - * - * Non-gridded data can still be sliced but coordintate slices will need to be - * produced as well in order to plot the slice. See Dataset_orthogonal() - * - * @param pThis A correlated dataset object - * @param sDs The string id of the dataset in question - * @param[out] psCoords a pointer to a const char* array to recived the - * coordinate ID's forming the spanning set. Note that every - * combination of returned coordinates satisfies the orthogonal - * condition and would return true from Dataset_orthogonal(). - * - * @return The number of spanning coordinates. Will be equal to the - * rank of the dataset. - * / -size_t Dataset_gridCoords( - const Dataset* pThis, const char* sDs, const char** psCoords -); - -bool DataGen_grid(const DataSet* pDataset); - -const DataSet** Dg_griddedIn(const DataSet* pDataset); - - - / ** Get the coefficients for iterating over a 1-D slice of a regular (i.e. * non-ragged) dataset. * diff --git a/das2/datum.c b/das2/datum.c index c5a1cd8..c5546bc 100644 --- a/das2/datum.c +++ b/das2/datum.c @@ -28,6 +28,10 @@ #include "util.h" #include "vector.h" +/* If this were D code it would use SumType and be about 10 lines long :-) + ...and have so many automatic features it would be hard to understand :-( + */ + /* ************************************************************************* */ /* Datum functions and structures */ @@ -170,6 +174,7 @@ double das_datum_toDbl(const das_datum* pThis) case vtUByte: rRet = *((ubyte*)pThis); break; case vtUShort: rRet = *((uint16_t*)pThis); break; case vtShort: rRet = *((int16_t*)pThis); break; + case vtUInt: rRet = *((uint32_t*)pThis); break; case vtInt: rRet = *((int32_t*)pThis); break; case vtFloat: rRet = *((float*)pThis); break; case vtDouble: rRet = *((double*)pThis); break; @@ -228,12 +233,16 @@ bool das_datum_toEpoch( know where zero is at */ if(!Units_haveCalRep(pThis->units) || (pThis->units == UNIT_UTC)) return false; - + switch(pThis->vt){ - case vtUByte: rDbl = *((ubyte*)pThis); break; + case vtUByte: rDbl = *((uint8_t*)pThis); break; + case vtByte: rDbl = *((int8_t*)pThis); break; case vtUShort: rDbl = *((uint16_t*)pThis); break; case vtShort: rDbl = *((int16_t*)pThis); break; + case vtUInt: rDbl = *((uint32_t*)pThis); break; case vtInt: rDbl = *((int32_t*)pThis); break; + case vtULong: rDbl = *((uint64_t*)pThis); break; + case vtLong: rDbl = *((int64_t*)pThis); break; case vtFloat: rDbl = *((float*)pThis); break; case vtDouble: rDbl = *((double*)pThis); break; default: @@ -245,6 +254,48 @@ bool das_datum_toEpoch( *pResult = Units_convertTo(epoch, rDbl, pThis->units); return (*pResult != DAS_FILL_VALUE); } + +bool das_datum_toTime(const das_datum* pThis, das_time* pDt) +{ + + + if(pThis->vt == vtTime){ + memcpy(pDt, pThis, sizeof(das_time)); + return true; + } + if(pThis->vt == vtText) + return dt_parsetime((const char*)pThis, pDt); + + if(!Units_haveCalRep(pThis->units) || (pThis->units == UNIT_UTC)) + return false; + + /* Special case for TT2000 long integers, need to preserve resolution */ + if((pThis->vt == vtLong)&&(pThis->units == UNIT_TT2000)){ + dt_from_tt2k(pDt, *((int64_t*)pThis) ); + return true; + } + + double rDbl = 0.0; + switch(pThis->vt){ + case vtUByte: rDbl = *((uint8_t*)pThis); break; + case vtByte: rDbl = *((int8_t*)pThis); break; + case vtUShort: rDbl = *((uint16_t*)pThis); break; + case vtShort: rDbl = *((int16_t*)pThis); break; + case vtUInt: rDbl = *((uint32_t*)pThis); break; + case vtInt: rDbl = *((int32_t*)pThis); break; + case vtULong: rDbl = *((uint64_t*)pThis); break; + case vtLong: rDbl = *((int64_t*)pThis); break; + case vtFloat: rDbl = *((float*)pThis); break; + case vtDouble: rDbl = *((double*)pThis); break; + default: + das_error(DASERR_DATUM, "Don't know how to convert items of type %s" + " to epoch times", das_vt_toStr(pThis->vt)); + return false; + } + + return (Units_convertToDt(pDt, rDbl, pThis->units) == DAS_OKAY); +} + /** Write a datum out as a string */ char* _das_datum_toStr( @@ -257,11 +308,18 @@ char* _das_datum_toStr( numeric type. Convert this to a broken down time then print it */ das_datum dm; if((pThis->vt != vtTime) && Units_haveCalRep(pThis->units)){ - Units_convertToDt((das_time*)&dm, das_datum_toDbl(pThis), pThis->units); + + /* Carve out for TT2000 */ + if((pThis->vt == vtLong)&&(pThis->units == UNIT_TT2000)){ + dt_from_tt2k((das_time*)&dm, *((uint64_t*)pThis) ); + } + else{ + Units_convertToDt((das_time*)&dm, das_datum_toDbl(pThis), pThis->units); + } dm.vt = vtTime; dm.vsize = sizeof(das_time); dm.units = UNIT_UTC; - pThis = &dm; /* Pull a switcheroo*/ + pThis = &dm; /* Pull a switcheroo */ } /* Write the value... */ diff --git a/das2/datum.h b/das2/datum.h index 413285e..6bfa838 100644 --- a/das2/datum.h +++ b/das2/datum.h @@ -210,14 +210,30 @@ DAS_API double das_datum_toDbl(const das_datum* pThis); * down time values. * @param pResult Pointer to location to store the converted value. * - * @returns true if the conversion was successful, false otherwise - * and das_error is called. + * @returns true if the conversion was successful, false otherwise. + * das_error is called if the value type of the datum + * makes no sense in the contex of a datetime. + * * @memberof das_datum */ DAS_API bool das_datum_toEpoch( const das_datum* pThis, das_units epoch, double* pResult ); +/** Get a time datum value as a das_time + * + * @param pThis a pointer to the datum to convert + * @param pDt a pointer to a das_time to hold the result + * + * @returns true if the conversion was successful, false otherwise. + * das_error is called if the value type of the datum + * makes no sense in the contex of a datetime. + * + * @memberof das_datum + */ +DAS_API bool das_datum_toTime(const das_datum* pThis, das_time* pDt); + + /** Get the value form a string datum * * @memberof das_datum diff --git a/das2/defs.h b/das2/defs.h index c0f6a6f..0e5f688 100644 --- a/das2/defs.h +++ b/das2/defs.h @@ -207,7 +207,8 @@ typedef int DasErrCode; #define DASERR_FRM 39 #define DASERR_VEC 40 #define DASERR_SERIAL 41 -#define DASERR_MAX 41 +#define DASERR_ITER 42 +#define DASERR_MAX 42 #ifdef __cplusplus } diff --git a/das2/descriptor.h b/das2/descriptor.h index 303bbb6..9c599a1 100644 --- a/das2/descriptor.h +++ b/das2/descriptor.h @@ -48,7 +48,7 @@ typedef enum DescriptorType { } desc_type_t; -const char* das_desc_type_str(desc_type_t dt); +DAS_API const char* das_desc_type_str(desc_type_t dt); /** Base structure for Stream Header Items. * diff --git a/das2/dimension.c b/das2/dimension.c index 2b4dd83..c59db0b 100644 --- a/das2/dimension.c +++ b/das2/dimension.c @@ -94,6 +94,16 @@ int DasDim_shape(const DasDim* pThis, ptrdiff_t* pShape) return nUsed; } +/* If I only have degenerate variables in this index, whole dim is degenerate */ +bool DasDim_degenerate(const DasDim* pThis, int iIndex) +{ + for(size_t u = 0; u < pThis->uVars; ++u){ + if(! DasVar_degenerate(pThis->aVars[u], iIndex)) + return false; + } + return true; +} + ptrdiff_t DasDim_lengthIn(const DasDim* pThis, int nIdx, ptrdiff_t* pLoc) { int nLengthIn = DASIDX_UNUSED; diff --git a/das2/dimension.h b/das2/dimension.h index 7b04d10..e4a2404 100644 --- a/das2/dimension.h +++ b/das2/dimension.h @@ -446,6 +446,20 @@ DAS_API int DasDim_shape(const DasDim* pThis, ptrdiff_t* pShape); DAS_API ptrdiff_t DasDim_lengthIn(const DasDim* pThis, int nIdx, ptrdiff_t* pLoc); +/** Does a given extern index even matter to data in this phsical dimension? + * + * @param pThis A pointer to a DasDim structure + * + * @param int nIdx - The index in question, from 0 to DASIDX_MAX - 1 + * + * @return true if varying this index could cause any variable's output + * to change, false if it would have no effect. + * + * @membefof DasVar + */ +DAS_API bool DasDim_degenerate(const DasDim* pThis, int iIndex); + + #ifdef __cplusplus } #endif diff --git a/das2/iterator.c b/das2/iterator.c new file mode 100644 index 0000000..157b08f --- /dev/null +++ b/das2/iterator.c @@ -0,0 +1,140 @@ +/* Copyright (C) 2024 Chris Piker + * + * This file is part of das2C, the core das C Library. + * + * Das2C is free software; you can redistribute it and/or modify it under + * the terms of the GNU Lesser General Public License version 2.1 as published + * by the Free Software Foundation. + * + * Das2C is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for + * more details. + * + * You should have received a copy of the GNU Lesser General Public License + * version 2.1 along with das2C; if not, see . + */ + +#define _POSIX_C_SOURCE 200112L + +#include + +#include "iterator.h" + +/* ************************************************************************* */ +/* The iteration support */ + +void dasds_iter_init(dasds_iterator* pIter, const DasDs* pDs){ + + memset(pIter, 0, sizeof(dasds_iterator)); + + pIter->rank = DasDs_shape(pDs, pIter->shape); + pIter->pDs = pDs; + + pIter->ragged = false; + for(int i = 0; i < pIter->rank; ++i){ + if((pIter->shape[i] == DASIDX_RAGGED)||(pIter->shape[i] == DASIDX_RAGGED)){ + pIter->ragged = true; + break; + } + } + + /* Start off index at all zeros, which memset insures above */ + + /* If I'm ragged I'm going to need the size of the last index at the + * lowest point of all previous indexes, get that. */ + if(pIter->ragged){ + pIter->nLenIn = DasDs_lengthIn(pDs, pIter->rank - 1, pIter->index); + if(pIter->nLenIn < 0) pIter->done = true; + } +} + +bool dasds_iter_next(dasds_iterator* pIter){ + + if(! pIter->ragged){ + /* Quicker function for CUBIC datasets */ + for(int iDim = pIter->rank - 1; iDim >= 0; --iDim){ + if(pIter->index[iDim] < (pIter->shape[iDim] - 1)){ + pIter->index[iDim] += 1; + return true; + } + else{ + pIter->index[iDim] = 0; + } + } + + pIter->done = true; + return false; + } + + /* I'm ragged so I can't use the generic shape function, but I can + * at least save off the length of the last index at this point + * and only change it when a roll occurs */ + ptrdiff_t nLenInIdx = 0; + for(int iDim = pIter->rank - 1; iDim >= 0; --iDim){ + + if(iDim == (pIter->rank - 1)) + nLenInIdx = pIter->nLenIn; + else + nLenInIdx = DasDs_lengthIn(pIter->pDs, iDim, pIter->index); + + if(pIter->index[iDim] < (nLenInIdx - 1)){ + pIter->index[iDim] += 1; + + /* If bumping an index that's not the last, recompute the length + * of the last run */ + if(iDim < (pIter->rank - 1)) + pIter->nLenIn = DasDs_lengthIn(pIter->pDs, pIter->rank - 1, pIter->index); + + return true; + } + else{ + pIter->index[iDim] = 0; + } + } + + pIter->done = true; + return false; +} + +void das_cube_iter_init( + das_cube_iter* pIter, int nRank, ptrdiff_t* pMin, ptrdiff_t* pMax +){ + pIter->done = true; + + if((nRank < 1)||(nRank > DASIDX_MAX)) + das_error(DASERR_ITER, "Invalid array rank %d", nRank); + + memcpy(pIter->idxmin, pMin, sizeof(ptrdiff_t)*nRank); + memcpy(pIter->index, pMin, sizeof(ptrdiff_t)*nRank); + memcpy(pIter->idxmax, pMax, sizeof(ptrdiff_t)*nRank); + + pIter->rank = nRank; + + /* Check to see if any max values are greater then the corresponding min */ + for(int i = 0; i < nRank; ++i){ + if(pMax[i] > pMin[i]){ + pIter->done = false; + return; + } + } +} + +bool das_cube_iter_next(das_cube_iter* pIter) +{ + for(int i = (pIter->rank - 1); i > -1; --i){ + + /* Increment this index, if you can */ + if((pIter->index[i] + 1) < pIter->idxmax[i]){ + pIter->index[i] += 1; + return true; + } + else{ + /* Need to bump next lower index, if no lower index, we're done */ + if(i != 0) + pIter->index[i] = pIter->idxmin[i]; + } + } + pIter->done = true; /* no next loop, no need for a break */ + return false; +} \ No newline at end of file diff --git a/das2/iterator.h b/das2/iterator.h new file mode 100644 index 0000000..f52c15c --- /dev/null +++ b/das2/iterator.h @@ -0,0 +1,161 @@ +/* Copyright (C) 2024 Chris Piker + * + * This file is part of das2C, the Core Das2 C Library. + * + * Das2C is free software; you can redistribute it and/or modify it under + * the terms of the GNU Lesser General Public License version 2.1 as published + * by the Free Software Foundation. + * + * Das2C is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for + * more details. + * + * You should have received a copy of the GNU Lesser General Public License + * version 2.1 along with das2C; if not, see . + */ + +/** @file iterator.h Objects which automatically increment indexes */ + +#ifndef _das_iterator_h_ +#define _das_iterator_h_ + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** Dataset iterator structure. + * + * Since dataset rank and shape is a union of the shape of it's components + * iterating over datasets can be tricky. This structure and it's associated + * functions are provided to simplify this task. Usage is demonstrated by + * the example below: + * + * @code + * // Assume a dataset with time, amplitude and frequency dimensions but with + * // arbitrary shape in index space. + * + * // pDs is a pointer to a das dataset + * + * DasDim* pDimTime = DasDs_getDimById(pDs, "time"); + * DasVar* pVarTime = DasDim_getPointVar(pDimTime); + * + * DasDim* pDimFreq = DasDs_getDimById(pDs, "frequency"); + * DasVar* pVarFreq = DasDim_getPointVar(pDimFreq); + * + * DasDim* pDimAmp = DasDs_getDimById(pDs, "e_spec_dens"); + * DasVar* pVarAmp = DasDim_getPointVar(pDimAmp); + * + * dasds_iterator iter; + * das_datum set[3]; + * + * for(dasds_iter_init(&iter, pDs); !iter.done; dasds_iter_next(&iter)){ + * + * DasVar_get(pVarTime, iter.index, set); + * DasVar_get(pVarFreq, iter.index, set + 1); + * DasVar_get(pVarAmp, iter.index, set + 2); + * + * // Plot, or bin, or what-have-you, the triplet here. + * // Plot() is not a real function in the das2C API + * Plot(set); + * } + * + * @endcode + */ +typedef struct dasds_iterator_t{ + + /** If true the value in index is valid, false otherwise */ + bool done; + + /** A dataset bulk iteration index suitable for use in DasVar functions like + * ::DasVar_getDatum */ + ptrdiff_t index[DASIDX_MAX]; + + int rank; + ptrdiff_t shape[DASIDX_MAX]; /* Used for CUBIC datasets */ + ptrdiff_t nLenIn; /* Used for ragged datasets */ + bool ragged; + const DasDs* pDs; +} dasds_iterator; + +/** Initialize a const dataset iterator + * + * The initialized iterator is safe to use for datasets that are growing + * as it will not exceed the valid index range of the dataset at the time + * this function was called. However, if the dataset shrinks during iteration + * das_iter_next() could overstep the array bounds. + * + * For usage see the example in ::das_iterator + * + * @param pIter A pointer to an iterator, will be initialize to index 0 + * + * @param pDs A pointer to a dataset. If the dataset changes while the + * iterator is in use invalid memory access could occur + * + * @memberof dasds_iterator + */ +DAS_API void dasds_iter_init(dasds_iterator* pIter, const DasDs* pDs); + +/** Increment the iterator's index by one position, rolling as needed at + * data boundaries. + * + * For efficiency this function does not re-check array bounds on each call + * a slower but safer version of this function could be created if needed. + * + * For usage see the example in ::das_iterator + * + * @param pIter A pointer to an iterator. The index member of the iterator + * will be incremented. + * + * @return true if the new index is within range, false if the index could not + * be incremented without producing an invalid location. + * + * @memberof dasds_iterator + */ +DAS_API bool dasds_iter_next(dasds_iterator* pIter); + +/** Simple cubic iterator + */ +typedef struct das_cube_inter_t{ + + /** If true the value in index is valid, false otherwise */ + bool done; + + /** A dataset bulk iteration index suitable for use in DasVar functions like + * ::DasVar_getDatum */ + ptrdiff_t index[DASIDX_MAX]; + + int rank; + ptrdiff_t idxmin[DASIDX_MAX]; + ptrdiff_t idxmax[DASIDX_MAX]; + +} das_cube_iter; + + +/** Initialize an iterator to cubic section in index space + * @memberof das_cube_iterator + */ +DAS_API void das_cube_iter_init( + das_cube_iter* pIter, int nRank, ptrdiff_t* pMin, ptrdiff_t* pMax +); + +/** Increment a cubic iterator by one position, rolling as needed + * + * @param pIter A pointer to an iterator. The index member of the iterator + * will be incremented. + * + * @return true if the new index is within range, false if the index could not + * be incremented without producing an invalid location. + * + * @memberof das_cube_iterator + */ +DAS_API bool das_cube_iter_next(das_cube_iter* pIter); + + +#ifdef __cplusplus +} +#endif + +#endif /* _das_dataset_h */ diff --git a/das2/time.c b/das2/time.c index 35e59db..6c85ead 100644 --- a/das2/time.c +++ b/das2/time.c @@ -875,7 +875,7 @@ void dt_from_tt2k(das_time* pThis, int64_t nTime) if(sc > 59.0) sc = 59.0; - pThis->second = sc + ms*1.0e-33 + us*1.0e-6 + ns*1.0e-9; + pThis->second = sc + ms*1.0e-3 + us*1.0e-6 + ns*1.0e-9; dt_tnorm(pThis); } diff --git a/das2/variable.h b/das2/variable.h index ecc285a..f8e69d2 100644 --- a/das2/variable.h +++ b/das2/variable.h @@ -747,7 +747,7 @@ DAS_API bool DasVar_orthoginal(const DasVar* pThis, const DasVar* pOther); * * @param pThis A pointer to a variable * - * @param int nIdx - The index in question, form 0 to DASIDX_MAX - 1 + * @param int nIdx - The index in question, from 0 to DASIDX_MAX - 1 * * @return true if varying this index could cause the variable's output * to change, false if it would have no effect. diff --git a/utilities/das3_cdf.c b/utilities/das3_cdf.c index 0f0b744..6ca8351 100644 --- a/utilities/das3_cdf.c +++ b/utilities/das3_cdf.c @@ -1618,7 +1618,7 @@ DasErrCode makeCompLabels(struct context* pCtx, DasDim* pDim, DasVar* pVar) nMaxCompLen = nCompLen; } - /* Get the primary variables name */ + /* Get the primary variable's name */ char sVarName[CDF_VAR_NAME_LEN256] = {'\0'}; if(CDF_MAD(CDFgetzVarName(pCtx->nCdfId, DasVar_cdfId(pVar), sVarName))) return PERR;