GIT repositories

Index page of all the GIT repositories that are clonable form this server via HTTPS. Übersichtsseite aller GIT-Repositories, die von diesem Server aus über git clone (HTTPS) erreichbar sind.

Services

A bunch of service scripts to convert, analyse and generate data. Ein paar Services zum Konvertieren, Analysieren und Generieren von Daten.

GNU octave web interface

A web interface for GNU Octave, which allows to run scientific calculations from netbooks, tables or smartphones. The interface provides a web form generator for Octave script parameters with pre-validation, automatic script list generation, as well presenting of output text, figures and files in a output HTML page. Ein Webinterface für GNU-Octave, mit dem wissenschaftliche Berechnungen von Netbooks, Tablets oder Smartphones aus durchgeführt werden können. Die Schnittstelle beinhaltet einen Formulargenerator für Octave-Scriptparameter, mit Einheiten und Einfabevalidierung. Textausgabe, Abbildungen und generierte Dateien werden abgefangen und in einer HTML-Seite dem Nutzer als Ergebnis zur Verfügung gestellt.

Digitale Filter in C für Embedded-Anwendungen

Digital filters in C for use in embedded applications

IIR lowpass example

Here you see a frst order low pass. It is has very similar behavior to an analog RC lowpass (resistor and capacitor). It is very quickly calculated and good for applications where you want to remove noise from your input signal. It needs two configuration values, where the sum of both must be 1. The higher coeff[1] the more noise is removed, but the slower is the reaction of your filtered output. The higher coeff[0] the faster your output, but more noise can pass through. The example implementation uses floating point numbers, on a microcontroller it is often recommenable to replace these with integers.

IIR Tiefpass-Beispiel

Hier eine Beispielimplementation für einen Tiefpass erster Ordnung. Er verhält sich (mit Ausnahme dass er digital ist) wie ein analoger RC-Tiefpass (aus Widerstand und Kondensator). Es ist sehr schnell berechnet und gut wenn hochfrequente Störungen aus einem Eingabesignal entfernt werden sollen. Zwei Parameter werden benötigt, deren Summe aber 1 ergeben muss. Je höher coeff[1], desto stärker werden Störungen entfernt, desto langsamer ändert sich aber auch die Ausgabe des Filters. Je höher coeffs[0], desto schneller reagiert die Ausgabe, aber es kommt auch mehr Rauschen durch. Die Beispiel-Implementation nutzt Fließkommazahlen, für Microcontroller sollten stattdessen Integer-Variablen benutzt werden.

/**
 * @file iir_filter_example.h
 * @author stfwi
 *
 * IIR FILTER EXAMPLE
 *
 *
**/
 
#include <stdlib.h>
 
/**
 * This is the only variable that this filter needs, the coefficients below are
 * constant.
 */
real_t old_value;
 
/**
 * The coefficients of our filter. We use a simple 1st order low pass here,
 * so we have two values. The sum of both must be 1. You can add more weight
 * to the new value or to the old value (which includes the all the previously
 * calculated filter results).
 */
real_t coeffs[2] = {
  0.5,  // The weight of the new value
  0.5   // The weight of the old value
};
 
/**
 * Here we initialise our IIR filter.
 */
void filter_init() {
  old_value = 0.0;
}
 
/**
 * This function is called every cycle. Calculates the new output value and
 * saves it in last_value for the next cycle.
 *
 * @param double new_value
 * @return double
 */
real_t filter(real_t new_value) {
  // Calculate y = a0 * x(k-0) + a1 * x(k-1)
  return old_value = coeffs[0] * new_value + coeffs[1] * old_value;
}

Moving average

A moving average filter (or sliding window filter) is a special FIR filter that can be quickly calculated. You simple build the average over the last N input values. This filter has a better reaction time as the IIR lowpass shown before, and it cancels noise quite moderately. Disadvantage: You need more memory. We use a ring buffer to store the value history and one variable where we store the sum of these values. When we get a new value, we simply substract the oldest value in the history, add the new value, and overwrite the oldest value with the new one. Having the sum we only need to divide by the number of values we have - and there is our filtered output.

First the floating point variant:

Moving Average

Ein Moving Average Filter (oder Sliding Window Filter) bildet den Mittelwert über die letzten N Eingabewerte. Wie der IIR Filter oben ist er schnell berechnet, reagiert aber schneller bei moderater Rauschunterdrückung. Nachteil: Er braucht Speicher, was auf kleinen Mikrocontrollern ein Problem sein könnte. Der Algorithmus berechnet einfach die Summe der Vergangenheitswerte geteilt durch die Anzahl an Vergangenheitswerten (Mittelwert eben). Mit einem kleinen Trick rechnen wir das nicht immer wieder aus, stattdessen speichern wir die Summe in einer Variable. Wenn ein neuer Eingabewert kommt, so subtrahieren wir den ältesten Wert und addieren den neuen. Danach müssen wir diese Summe noch durch die Anzahl an Elementen teilen und haben das neue Eergebnis.

Erstmal die Floating Point Variante:

/**
 * @file fir_filter_moving_avg8_uint.h
 * @author stfwi
 *
 * FIR FILTER EXAMPLE, MOVING AVERAGE IMPLEMENTATION
 *
 *
**/
 
/**
 * The size of our filter. We specify it in bits
 */
#define FILTER_SIZE (8)
 
/**
 *
 */
real_t filter_buffer[FILTER_SIZE];
 
/**
 * Here we store the sum of all history values
 */
real_t filter_sum;
 
 
/**
 * This is the actual position of the ring buffer.
 */
real_t *filter_position;
 
/**
 * Here we initialise our filter. In this case we only set the
 * initial position and set all buffer values to zero.
 */
void filter_init() {
  register int i;
  filter_sum = 0;
  filter_position = filter_buffer;
  // alternatively memset(filter_buffer, 0, sizeof(filter_buffer) * sizeof(unsigned int))
  for(i=0; i<FILTER_SIZE; i++) filter_buffer[i] = 0;
}
 
/**
 * This function is called every cycle. It adds substracts the oldest value from
 * the sum, adds the new value to the sum, overwrites the oldest value with the
 * new value and increments the ring buffer pointer (with rewind on overflow).
 *
 * @param double new_value
 * @return double
 */
real_t filter(real_t new_value) {
  // Substract oldest value from the sum
  filter_sum -= *filter_position;
 
  // Update ring buffer (overwrite oldest value with new one)
  *filter_position = new_value;
 
  // Add new value to the sum
  filter_sum += new_value;
 
  // Advance the buffer write position, rewind to the beginning if needed.
  if(++filter_position >= filter_buffer + FILTER_SIZE) {
    filter_position = filter_buffer;
  }
 
  // Return sum divided by FILTER_SIZE, which is faster done by right shifting
  // The size of the ring buffer in bits. ( filter_sum / 2^bits ).
  return filter_sum / FILTER_SIZE;
}

Now the same with integer variables. If we don't have a floating point unit we can save some time and divide by "powers of two" right shifting a few bits. But then you need to take care that your ring buffer has 2^BITS values.

Nun das Selbe mit Integern. Vor allem wenn keine Floating Point Einheit im Controller ist spart das viel Zeit. Beim Dividieren kann ebenfalls eingespart werden, indem man statt /2 zu rechnen jeweils ein mal die Bits der Zahl nach rechts schiebt. Das lässt sich also für alle Puffergrößen von 2^BITS machen. Der Ringbuffer im Beispiel hat 8 Werte, und mit summe >> 3 haben wir die Summe durch 8 geteilt.

/**
 * @file fir_filter_moving_avg8_uint.h
 * @author stfwi
 *
 * FIR FILTER EXAMPLE, MOVING AVERAGE IMPLEMENTATION
 *
 *
**/
 
/**
 * The size of our filter. We specify it in bits
 */
#define FILTER_SIZE_IN_BITS (3)
#define FILTER_SIZE ( 1 << (FILTER_SIZE_IN_BITS) )
 
/**
 *
 */
uint16_t filter_buffer[FILTER_SIZE];
 
/**
 * Here we store the sum of all history values
 */
uint32_t filter_sum;
 
 
/**
 * This is the actual position of the ring buffer.
 */
uint16_t *filter_position;
 
/**
 * Here we initialise our filter. In this case we only set the
 * initial position and set all buffer values to zero.
 */
void filter_init() {
  register int i;
  filter_sum = 0;
  filter_position = filter_buffer;
  // alternatively memset(filter_buffer, 0, sizeof(filter_buffer) * sizeof(unsigned int))
  for(i=0; i<FILTER_SIZE; i++) filter_buffer[i] = 0;
}
 
/**
 * This function is called every cycle. It adds substracts the oldest value from
 * the sum, adds the new value to the sum, overwrites the oldest value with the
 * new value and increments the ring buffer pointer (with rewind on overflow).
 *
 * @param double new_value
 * @return double
 */
real_t filter(real_t new_value) {
  // Substract oldest value from the sum
  filter_sum -= *filter_position;
 
  // Update ring buffer (overwrite oldest value with new one)
  *filter_position = (uint16_t) new_value;
 
  // Add new value to the sum
  filter_sum += (uint16_t) new_value;
 
  // Advance the buffer write position, rewind to the beginning if needed.
  if(++filter_position >= filter_buffer + FILTER_SIZE) {
    filter_position = filter_buffer;
  }
 
  // Return sum divided by FILTER_SIZE, which is faster done by right shifting
  // The size of the ring buffer in bits. ( filter_sum / 2^bits ).
  return (real_t) (filter_sum >> FILTER_SIZE_IN_BITS);
}

Common FIR filter

FIR Filter

Die folgenden Quelltexte sind Implementationen eines zyklisch aufgerufenen FIR Filters. Wie beim Moving Average Filter (der ein FIR-Filter ist) müssen auch hier die Vergangenheitswerte gespeichert werden. Zudem gibt es einen Speicherbereich mit Koeffizienten, der genau so groß ist wie der Wertepuffer. Alles was der Filteralgorithmus tun muss, ist jeden gespeicherten Vergangenheitswert mit dem dazugehörigen Koeffizienten zu multiplizieren und all diese Produkte zum Endergebnis aufzuaddieren. Die Anzahl und Werte der Koeffizienten bestimmen dabei, was der Filter tut. Er kann Tiefpass, Hochpass, Bandpass, Bandsperre und vieles mehr sein. FIR Filter sind ein derart scharfes Messer im Schrank der Signalverarbeitung, dass Prozessoren darauf ausgelegt werden sie schnell berechnen zu können. Vor allem in DSPs sieht man daher MAC-Operationen (Multiply and Accumulate), d.h. in einem Schritt multiplizieren sie zwei Zahlen und addieren das Ergebnis zu einer Summe hinzu. Abhängig von den Features eines Controllers sollte dann auch besser von C auf Assembly ausgewichen werden (z.B. gibt es auch Prozessoren, die die MAC-Operation rechnen und im selben Schritt das nächste Wert-Koeffizient-Paar anvisieren, usw. usw.). Besser lesbar ist jedoch dieses Beispiel in C. Die Koeffizienten habe ich so gewählt, dass dieser FIR identisch mit dem Moving Average Filter ist. Es ist auch ein leicht nachvollziehbares Beispiel: Statt zum Schluss Summe / N zu rechnen wird hier jeder Vergangenheitswert mal 1/N gerechnet. "Da kommt das Selbe raus." Für andere Filterkoeffizienten lohnt sich ein Blick in die Signal Toolbox von GNU Octave. (Die Software ist kostenlos).

Erstmal mit Fließkommazahlen: Der Algorithmus ist recht selbsterklärend, zwei Sachen seien aber noch angemerkt:

  • Wir füllen den Ringpuffer rückwärts, denn dann sind die Vergangenheitswerte zum Rechnen bereits richtig geordnet. D.h. der vorherige Wert ist eins nach vorn, der davor zwei nach vorn usw.

  • Wir rechnen in zwei Schleifen, damit wir uns die Abfrage, ob wir am Ende des Ringpuffer-Speichers angekommen sind, in der Schleife erspart bleibt. Es kommt richtig raus wenn wir erst bis zum Ende des Puffers arbeiten, dann die Pufferposition rücksetzen und bis zum Ende der Koeffizienten rechnen.

/**
 * @file fir_filter_example.h
 * @author stfwi
 *
 * FIR FILTER EXAMPLE
 *
 * Here we use a Finite Impulse Response Filter, where we store the last
 * FIR_FILTER_SIZE values. Every time the function is called the oldest
 * value is overwritten with the new (given as parameter). Each stored
 * history value is weighted with a factor depending on its age. The
 * newest value with the 1st coefficient, the last value with 2nd coeff,
 * the value before the last one with the 3td etc. To calculate the FIR
 * filter result we need build the sum of all the weighted history values,
 * means we accumulate the multiplications of history[i] * coeff[i].
 *
 * The implementation of the filter function is not optimised for execution speed
 * but for readability. Depending on the hardware platform, the processor
 * allows fast MAC (multiply and accumulate) operation, some automatic address
 * incrementation as well. Additionally using double values is often not required
 * and slows down the filter calculation.
 *
 * The coefficient chosen here result in a moving average (sliding window)
 * filter for comparison the other implementation of the moving average filter.
 *
**/
 
#include <stdlib.h>
 
/**
 * The size of our filter.
 */
#define FIR_FILTER_SIZE (8)
 
/**
 * These are coefficients of the FIR filter. In this case the size is 8 and
 * all values are 1/8. This means it works exactly like a sliding window
 * (moving average) filter.
 */
real_t fir_coeffs[FIR_FILTER_SIZE] = {
  (1.0/8), (1.0/8), (1.0/8), (1.0/8),
  (1.0/8), (1.0/8), (1.0/8), (1.0/8)
};
 
/**
 * This is the history buffer of the values. It is used as a ring buffer.
 */
real_t fir_buffer[FIR_FILTER_SIZE];
 
/**
 * This is the actual position of the ring buffer.
 */
real_t *fir_position = NULL;
 
/**
 * Here we initialise our FIR filter. In this case we only set the
 * initial position and set all buffer values to zero.
 */
void filter_init() {
  fir_position = fir_buffer;
  int i;
  for(i=0; i<FIR_FILTER_SIZE; i++) {
    fir_buffer[i] = 0;
  }
}
 
/**
 * This function is called every cycle. It adds the new value (e.g. read from
 * an analog input) and accumulates the products of all history values with
 * their corresponding coefficient.
 *
 * @param double new_value
 * @return double
 */
real_t filter(real_t new_value) {
  // 1st coeff position is at pointer position fir_coeffs
  real_t *p_coeff  = fir_coeffs;
 
  // 1st buffer position is at the actual write position
  real_t *p_buffer = fir_position;
 
  // This is our function result
  real_t result = 0;
 
  // We overwrite the oldest value with the new value. This is now our 1st
  // position to start.
  *fir_position = new_value;
 
  // As the buffer has the same size as the coefficients, we can iterate to
  // the end of the buffer. The p_coeff pointer cannot overflow here.
  while(p_buffer < fir_buffer + FIR_FILTER_SIZE) {
    // Multiply and accumulate
    result += (*p_coeff) * (*p_buffer);
 
    // Next coeff, next buffer position
    p_coeff++;
    p_buffer++;
  }
 
  // Reset the pointer to the start of the ring buffer and iterate until the
  // p_coeff pointer reaches its end. After this we have MAC'ed all history
  // values with their corresponding coefficients.
  p_buffer = fir_buffer;
  while(p_coeff < fir_coeffs + FIR_FILTER_SIZE) {
    // MAC
    result += (*p_coeff) * (*p_buffer);
    // Increment buffer and coefficient position.
    p_coeff++;
    p_buffer++;
  }
 
  // To finish this, we advance our ring buffer write position and roll over
  // to the beginning if we reached the end of the memory block.
  // Note: We write backwards so that our history time is ascending. 
  if(--fir_position < fir_buffer) {
    fir_position = fir_buffer + FIR_FILTER_SIZE - 1;
  }
 
  // Return our filter result.
  return result;
}

Und hier das ganze nochmals mit Festkomma-Arithmetik. Wir verwenden im Filter Zahlen zwischen -1 und 1. Diese stellen wir als vorzeichenbehaftete 16 Bit Zahlen dar und denken uns das Komma direkt nach dem Vorzeichenbit, also beim fünfzehnten - genannt Q15-Zahl. Die Integerwerte gehen von -35768 bis 35767, d.h. der Integerwert 0x0001 entspricht dann 1/35768. Die 8 Koeffizienten geben wir also nicht mit 1.0/8 an, sondern mit 35768/8 oder (1<<15)/8. Weiterhin müssen Wir beachten, dass beim Multiplizieren sich das Komma an das 30te bit heftet (gemäß "ein Kilometer mal ein Kilometer sind nicht 1000 Quadratmeter, sondern 1000000"). Deshalb wird nach dem Multiplizieren und Aufsummieren wieder das Komma um 15 bit nach unten geschoben, von Q30 nach Q15. Die dritte Sache: Wir müssen beim Multiply-And-Accumulate auf Überlauf Prüfen, das passiert im MAC Makro. Vierte Sache: Wir wollen runden. Daher laden wir 0.5 in die Summenvariable (den Akkumulator) und zwar in Q30, das entspricht 1<<14. Der Rest ist wie bei der Festkomma-Rechnung.

/**
 * @author stfwi
 *
 * FIR FIXED POINT FILTER EXAMPLE
 *
 * This example is equivalent to the floating point FIR filter example,
 * except that we use fixed point arithmetic.
 *
**/
 
 
/**
 * The size of our filter.
 */
#define FIR_FILTER_SIZE (8)
 
/**
 * These are coefficients of the FIR filter. In this case the size is 8 and
 * all values are 1/8. This means it works exactly like a sliding window
 * (moving average) filter.
 *
 * We have a signed 16 bit integer and the 16th bit is the sign bit, so
 * 15 bits are left for our numbers. As the filter coefficients are values
 * between -1 and 1 we represent these values as Q15 fixed point numbers, means
 * we set the period after the sign bit. This means if we scale everything by
 * 2^15 = 32768.
 */
#define MOV_AVG_COEFF ((1<<15) / FIR_FILTER_SIZE)
 
int16_t fir_coeffs[FIR_FILTER_SIZE] = {
  MOV_AVG_COEFF, MOV_AVG_COEFF, MOV_AVG_COEFF, MOV_AVG_COEFF,
  MOV_AVG_COEFF, MOV_AVG_COEFF, MOV_AVG_COEFF, MOV_AVG_COEFF
};
 
/**
 * This is the history buffer of the values. It is used as a ring buffer.
 */
int16_t fir_buffer[FIR_FILTER_SIZE];
 
/**
 * This is the actual position of the ring buffer.
 */
int16_t *fir_position = NULL;
 
/**
 * Here we initialise our FIR filter. In this case we only set the
 * initial position and set all buffer values to zero.
 */
void filter_init() {
  fir_position = fir_buffer;
  int i;
  for(i=0; i<FIR_FILTER_SIZE; i++) {
    fir_buffer[i] = 0;
  }
}
 
/**
 * As example, we "outsource" the MAC operation. DSP processors often have
 * a special CPU instruction to do this very fast. Because of the fixed point
 * multiplication with signed numbers the period moves from bit 15 to bit 30.
 * This is where we saturate the accumulator to protect overflowing.
 */
#define MAC(ACC, V1, V2) {                          \
  (ACC) += (int32_t)(V1) * (int32_t)(V2);           \
  if((ACC)      >  0x3fffffff) (ACC) = 0x3fffffff;  \
  else if((ACC) < -0x40000000) (ACC) = -0x40000000; \
}
 
/**
 * This function is called every cycle. It adds the new value (e.g. read from
 * an analog input) and accumulates the products of all history values with
 * their corresponding coefficient.
 *
 * The range of our input and output is -32768 to +32767, but we return
 * floating point to fit the requirements of the main() function.
 *
 */
real_t filter(real_t new_value) {
 
  // 1st coeff position is at pointer position fir_coeffs
  int16_t *p_coeff  = fir_coeffs;
 
  // 1st buffer position is at the actual write position
  int16_t *p_buffer = fir_position;
 
  // This is our function result
  int16_t result = 0;
 
  // This is the accumulator for the MAC operations.
  // It must be much bigger than our values and coefficients, on the one
  // hand because of the multiplications, on the other hand because of the
  // accumulation. In theory it is number of bits of the values plus number
  // of bits of the coefficients plus size of the coefficient vector in bits.
  // We preload the accumulator with 0.5 for rounding, and 0.5 expressed as
  // Q15 fixed point number is 1<<14 because 1<<15 would be 1.0.
  int32_t acc = (1<<14);
 
  // We overwrite the oldest value with the new value. This is now our 1st
  // position to start.
  *fir_position = new_value;
 
  // As the buffer has the same size as the coefficients, we can iterate to
  // the end of the buffer. The p_coeff pointer cannot overflow here.
  while(p_buffer < fir_buffer + FIR_FILTER_SIZE) {
    // Multiply and accumulate operation
    MAC(acc, *p_coeff, *p_buffer);
    // Next coeff, next buffer position
    p_coeff++;
    p_buffer++;
  }
 
  // Reset the pointer to the start of the ring buffer and iterate until the
  // p_coeff pointer reaches its end. After this we have MAC'ed all history
  // values with their corresponding coefficients.
  p_buffer = fir_buffer;
  while(p_coeff < fir_coeffs + FIR_FILTER_SIZE) {
    // MAC operation
    MAC(acc, *p_coeff, *p_buffer);
    // Increment buffer and coefficient position.
    p_coeff++;
    p_buffer++;
  }
 
  // Because of the multiplications your period shifted to bit 30, so we
  // shift it back to bit 15 to get a 16 bit signed number result.
  result = (int16_t) (acc >> 15);
 
  // To finish this, we advance our ring buffer write position and roll over
  // to the beginning if we reached the end of the memory block.
  if(--fir_position < fir_buffer) {
    fir_position = fir_buffer + FIR_FILTER_SIZE - 1;
  }
 
  // Return our filter result. It is a value between -1 and 1 and scaled by
  // 32768 (Q15 number).
  return result;
}

Main program for the examples

In combination with the Makefile, the examples above were compiled in separate executables. This is the main program for all of them.

Hauptprogramm für diese Beispiele

In Kombination mit dem Makefile werden alle oben beschriebenen Beispiel-Quelltexte in separate Ausführbare Programme kompiliert. Dabei enthält diese Datei die main() Funktion, welche sich um Eingabe und Ausgabe kümmert. Sie ruft die jeweiligen filter_init() und filter() Funktionen auf.

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>
 
#if defined __linux__
#include <stdint.h>
#include <sys/types.h>
#elif defined __APPLE__ & __MACH__
#include <stdint.h>
#include <sys/types.h>
#else
#error "You need to define int16_t, uint16_t etc yourself or include an appropriate file"
#endif
 
#define real_t double
 
 
#if defined(USE_IIR_LP_1_ORDER)
  #include "iir_filter_lowpass_1_order.h"
#elif defined(USE_FIR_LP_8_FP)
  #include "fir_filter_lowpass_com_ma8.h"
#elif defined(USE_FIR_LP_8_INT)
  #include "fir_filter_lowpass_int_ma8.h"
#elif defined(USE_FIR_AVG_8_UINT)
  #include "fir_filter_moving_avg8_uint.h"
#elif defined(USE_FIR_AVG_8_FP)
  #include "fir_filter_moving_avg8_fp.h"
#elif defined(MAKE_VALUES)
  #include "make_values.h"
#else
  #warning(No filter selected)
  // Implemented in the example header files. Initialises the filter.
  void filter_init() {};
  // Implemented in the example header files. Runs one filter (cycle) calculation.
  double filter(double new_value) { return new_value; }
#endif
 
/*
################################################################################
##
## MAIN FUNCTION
##
################################################################################
*/
 
/**
 * Here the main. If calls filter_init() and filter(). The rest is getting
 * the input and writing the output to STDOUT.
 */
int main(int argc, char** argv)
{
  FILE *p_file = NULL;
  double new_value = 0;
  double filtered_value = 0;
  unsigned int t = 0;
 
  if(argc == 3 && !strcmp(argv[1], "-f")) {
    if(!(p_file = fopen(argv[2], "r"))) {
      fprintf(stderr, "Failed to open file '%s'.\n", argv[2]);
      return 1;
    }
  } else if(argc == 2 && !strcmp(argv[1], "--stdin")) {
    p_file = stdin;
  } else {
    fprintf(stderr, "Usage:\n\n");
    fprintf(stderr, " %s -f <file name>\n", argv[0]);
    fprintf(stderr, "    Read values from file, one integer per line.\n\n", argv[0]);
    fprintf(stderr, " %s --stdin\n", argv[0]);
    fprintf(stderr, "    Read values from console or pipe, one integer per line.\n", argv[0]);
    fprintf(stderr, "    Enter something that is not a number to exit.");
    return 1;
  }
 
  filter_init();
  while(!feof(p_file)) {
    if(!fscanf(p_file, "%lf", &new_value)) return 0;
    printf("%5u, %12.6f, %12.6f\n", t++, new_value, (double) filter(new_value));
  }
 
  if(p_file) fclose(p_file);
  return 0;
}

Makefile

CC=gcc
CFLAGS=-c -O3
LDFLAGS=

all: mkvalues iir_lowpass fir_lowpass_fp moving_avg_fp moving_avg_uint fir_lowpass_int

clean:
    @rm -f *.o
    @rm -f iir_lowpass
    @rm -f fir_lowpass_fp
    @rm -f fir_lowpass_int
    @rm -f moving_avg_fp
    @rm -f moving_avg_uint
    @rm -f mkvalues

moving_avg_uint: main.c
    @mkdir -p dist
    $(CC) $(CFLAGS) main.c -o main.o -DUSE_FIR_AVG_8_UINT
    $(CC) $(LDFLAGS) main.o -o dist/moving_avg_uint
    @rm -f *.o

moving_avg_fp: main.c
    @mkdir -p dist
    $(CC) $(CFLAGS) main.c -o main.o -DUSE_FIR_AVG_8_FP
    $(CC) $(LDFLAGS) main.o -o dist/moving_avg_fp
    @rm -f *.o

fir_lowpass_fp: main.c
    @mkdir -p dist
    $(CC) $(CFLAGS) main.c -o main.o -DUSE_FIR_LP_8_FP
    $(CC) $(LDFLAGS) main.o -o dist/fir_lowpass_fp
    @rm -f *.o

fir_lowpass_int: main.c
    @mkdir -p dist
    $(CC) $(CFLAGS) main.c -o main.o -DUSE_FIR_LP_8_INT
    $(CC) $(LDFLAGS) main.o -o dist/fir_lowpass_int
    @rm -f *.o

iir_lowpass: main.c
    @mkdir -p dist
    $(CC) $(CFLAGS) main.c -o main.o -DUSE_IIR_LP_1_ORDER
    $(CC) $(LDFLAGS) main.o -o dist/iir_lowpass
    @rm -f *.o

mkvalues: mkvalues.c
    @mkdir -p dist
    $(CC) $(CFLAGS) mkvalues.c -o mkvalues.o
    $(CC) $(LDFLAGS) mkvalues.o -o dist/mkvalues
    @rm -f *.o