Ну, это как сказать. Если все- таки делить, то лучше делить не сумму, а каждое значение уже при измерении. Тогда для буфера можно будет выбрать тип данных меньшей размерности.
А вообще, скользящих средних много. Взвешенное, экспоненциальное, модифицированное. И для некоторых хранить буфер из N значений вообще не нужно - новое значение среднего вычисляется из предыдущего среднего и одного последнего значения.
Это все наверно Дракула мог бы рассказать, если заглянет на огонек.
Ну не совсем так - надо ещё исключать случайные значения, которые не вписываются в установленную модель. Или, если позволяет концепция экперимента, создавать новую модель.
А вообще - тут есть 2 варианта:
1 - Мы отслеживаем процесс по известной модели. Тогда нам известны доступные отклонения в результатах и тогда отклонения - это или ошибки средств измерения или ошибки исходных факторов.
2 - Мы создаем модель на основе экспериментальных данных. Тут всё сложнее. Скорее всего “выбивающиеся” данные тоже надо хранить и в последующем обрабатывать т.к. никто не исключает интерференции, когда вроде ровненькая, в основном, функция развалится на 2, 3, 4… циклических функций и имеет непонятные экстримы.
не зная входных данных, их шумовую составляющую, сложно что-то рекомендовать, но если данные скачут в тех пределах что озвучено, скользящее среднее видимо не самый лучший алгоритм
Да, какие тут нахрен специфики, всё тривиально. У меня сделано шаблоном, чтобы как можно больше всякой фигни (типа размера буфера и типов) определялось на этапе компиляции - это сильно поможет оптимизатору. Кстати, здесь уже с Вашей суммой и со средним.
Файл RingBuffer.h
#ifndef RINGBUFFER_H
#define RINGBUFFER_H
//////////////////////////////////////////////
//
// Кольцевой буфер с поддеркой суммы элементов и среднего значения.
//
// Параметры шаблона:
//
// bufSize - размер буфера
// TBase - тип элемента буфера (по умолчанию uint16_t)
// TSum - тип переменной для суммы элементов (по умолчанию тот же, что тип элемента буфера)
//
// TSum getSum(void) - возвращает 0, если буффер недозаполнен или текущую сумму
// TBase getAverage(void) - возвращает 0, если буффер недозаполнен или среднее элементов буфера
// TSum add(const TBase newElement) - добавляет новый элемент и возвращает текущую сумму
//
template <const uint8_t bufSize, typename TBase = uint16_t, typename TSum = TBase>
class RingBuffer {
protected:
TBase buffer[bufSize]; // Буфер для последних bufSize элементов
TSum m_sum; // Текущая сумма
uint8_t m_index; // Индекс в массиве buffer, куда будем писать следующий элемент
uint8_t m_total; // Количество элементов записанных в массив buffer (после bufSize не растёт)
public:
//
// Конструктор и функции очистки, возврата суммы и возврата среднего значения
// достаточно тривиальны
//
inline RingBuffer(void) : m_sum(0), m_index(0), m_total(0) {}
inline void clear(void) { return m_sum = m_index = m_total = 0; }
inline TSum getSum(void) { return m_total == bufSize ? m_sum : 0; }
inline TBase getAverage(void) { return (getSum() + bufSize / 2) / bufSize; }
//
// Функция добавления нового элемента
// Поддерживает актуальной сумму элементов, находящихся в буфере
//
inline TSum add(const TBase newElement) {
//
// Если buffer уже полон, то вычтем из суммы значение, которое
// собираемся стирать, иначе увеличим на 1 количество значений в буфере
//
if (m_total == bufSize) m_sum -= buffer[m_index]; else m_total++;
//
// Дальше всё линейно:
// 1. увеличим суммы на "новый элемент"
// 2. запишем новый элемент в буфер
// 3. продвинем индекс на одну позицию вперёд
// 4. и вернёт накопленную сумму
//
m_sum += newElement;
buffer[m_index] = newElement;
m_index =(m_index + 1) % bufSize;
return getSum();
}
};
#endif // RINGBUFFER_H
Не, ну я имел в виду тривиальную вещь. Вот смотрите: допустим этот Ваш сигнал – обратная связь и конечная задача стоит управлять неким процессом и “поддерживать значение среднего по 10-ти измерениям на уровне 800”. Но ведь эта задача полностью эквивалентна задаче “поддерживать сумму 10-ти измерений на уровне 8000”. Разве нет? Только во втором случае вычислений меньше.
Потому, что я так сделал. Хотите - выдавайте текущую (маленькую) сумму и среднее, деля на количество уже полученных данных. Но Вы при этом попадаете на настоящее деление, а если делить всегда на размер буфера, то можно обойтись сдвигами, если размер буфера – степень двойки.
Я думаю Вы правы. Меня смущало вот что - Не возникнет ли «ненужного» округления при малых значениях (то есть - не пострадает ли точность при таком подходе)?
Ну вот для дозиметра тоже не подходит. Если по 10 сек замер, не ждать же 160 сек при массиве х16. А вообще, хочется сменить алгоритм на другой, более отзывчивый на амплитуду. Но это другая тема.
Ну, это забота компилятора, ты пишешь как красиво, а он уже обходится сдвигами. Но не каждый! Столкнулся с этим с ПИК семейством. Причём, у бесплатного код воооообще раздут. Типа, без оптимизации.)