/** * @file LTTB.h * @brief Largest Triangle Three Buckets (LTTB) time-series decimation. * * Direct C++ translation of the Go lttbDecimate function in wshub/hub.go. * Preserves visual fidelity when reducing a large time-series to a smaller * number of representative points. * * No external dependencies (uses only for fabs). */ #ifndef STREAMHUB_LTTB_H_ #define STREAMHUB_LTTB_H_ #include "CompilerTypes.h" #include namespace StreamHub { using MARTe::uint32; using MARTe::float64; /** * @brief Decimate time-series (tIn, vIn) of length nIn to at most threshold points. * * @param tIn Input time array (float64, monotonically increasing). * @param vIn Input value array (float64), same length as tIn. * @param nIn Number of input points. * @param tOut Output time array (pre-allocated, capacity >= threshold). * @param vOut Output value array (pre-allocated, capacity >= threshold). * @param threshold Target number of output points (must be >= 3 for LTTB to apply; * otherwise all points are copied). * @return Number of output points written (always <= threshold and <= nIn). */ inline uint32 LTTBDecimate(const float64 *tIn, const float64 *vIn, uint32 nIn, float64 *tOut, float64 *vOut, uint32 threshold) { if (nIn == 0u) { return 0u; } if (nIn <= threshold || threshold < 3u) { /* Fast path: no decimation needed */ for (uint32 i = 0u; i < nIn; i++) { tOut[i] = tIn[i]; vOut[i] = vIn[i]; } return nIn; } uint32 nOut = 0u; /* Always include the first point */ tOut[nOut] = tIn[0u]; vOut[nOut] = vIn[0u]; nOut++; float64 bucketSize = static_cast(nIn - 2u) / static_cast(threshold - 2u); uint32 a = 0u; /* index of last selected point */ for (uint32 i = 0u; i < threshold - 2u; i++) { /* Compute average point in the NEXT bucket (for area calculation) */ uint32 avgStart = static_cast((static_cast(i + 1u) * bucketSize)) + 1u; uint32 avgEnd = static_cast((static_cast(i + 2u) * bucketSize)) + 1u; if (avgEnd > nIn) { avgEnd = nIn; } float64 avgT = 0.0; float64 avgV = 0.0; uint32 avgCount = avgEnd - avgStart; if (avgCount > 0u) { for (uint32 j = avgStart; j < avgEnd; j++) { avgT += tIn[j]; avgV += vIn[j]; } avgT /= static_cast(avgCount); avgV /= static_cast(avgCount); } /* Find point in current bucket with maximum triangle area */ uint32 bStart = static_cast((static_cast(i) * bucketSize)) + 1u; uint32 bEnd = static_cast((static_cast(i + 1u) * bucketSize)) + 1u; if (bEnd > nIn) { bEnd = nIn; } float64 maxArea = -1.0; uint32 nextA = bStart; float64 aT = tIn[a]; float64 aV = vIn[a]; for (uint32 j = bStart; j < bEnd; j++) { float64 area = fabs((aT - avgT) * (vIn[j] - aV) - (aT - tIn[j]) * (avgV - aV)) * 0.5; if (area > maxArea) { maxArea = area; nextA = j; } } tOut[nOut] = tIn[nextA]; vOut[nOut] = vIn[nextA]; nOut++; a = nextA; } /* Always include the last point */ tOut[nOut] = tIn[nIn - 1u]; vOut[nOut] = vIn[nIn - 1u]; nOut++; return nOut; } } /* namespace StreamHub */ #endif /* STREAMHUB_LTTB_H_ */