Create custom libraries

When creating and managing large applications, it is useful to split the code into multiple files and libraries. This improves code maintainability and code reuse across projects. Library functions can be directly called within the processing nodes of a Huxon application.

Currently, Huxon language supports C/C++ header-only custom library with these restrictions:

  • all global functions and variables should be declared as static or defined within an anonymous namespace

  • non-const global variables are not supported

  • static variables within functions are not supported

  • the files of the library should be placed within the application source files directory (needed to ensure that when the application is packaged, all custom library files are also included)

Note

A standard approach when designing Huxon applications is to define the elements of the data processing pipeline (i.e. sensors, channels, processing nodes) within the main file and define the logic and data types of complex processing nodes in external files.

FIR filter library example

In the following example, we build a simple Finite Input Response (FIR) filter library and use it inside an example application to perform low and high pass filtering of acceleration data from an inertial sensor.

Custom library: fir.hpp

#ifndef __FIR_HPP__
#define __FIR_HPP__

#include <huxon/lang.hpp>
#include <array>

/**
 * @brief State for a FIR filter
 *
 * @tparam N the number of taps of the filter (the order of the filter is N - 1)
 */
template<hux::uint64_t N>
struct fir_state_t {
    std::array<float, N> data; /* array to store the most recent N input samples */
    hux::uint64_t num_samples = 0; /* number of input samples stored in array data */
};

/**
 * @brief Apply a fir filter to an input signal
 *
 * @tparam N the number of taps of the filter (the order of the filter is N - 1)
 *
 * @param fir_state the current state of the filter, keeps the most recent N samples
 * @param fir_coeffs the N FIR filter coefficients (fir_coeffs[0] is applied to x)
 * @param x the current sample from the input signal
 *
 * @return the output sample y after applying the FIR filter
 */
template<hux::uint64_t N>
static float fir_filter(
    fir_state_t<N> &fir_state, const std::array<float, N> &fir_coeffs, float x)
{
    /* Shift stored samples by one position to the right (position 0 will contain x) */
    for(hux::int64_t i = N - 1; i > 0; i--) {
        fir_state.data[i] = fir_state.data[i - 1];
    }

    /* Increase the number of stored samples if the window is not yet full */
    if(fir_state.num_samples < N) {
        fir_state.num_samples++;
    }

    /* Store the current input sample x at the beginning of the samples window */
    fir_state.data[0] = x;

    /* Compute the output of the FIR filter */
    float y = 0.0f;
    for(hux::int64_t i = 0; i < fir_state.num_samples; i++) {
        y += fir_state.data[i] * fir_coeffs[i];
    }
    return y;
}

#endif // __FIR_HPP__

Main application: vibration_analysis.hpp

#include <huxon/lang.hpp>
#include <huxon/sensors/STMicroelectronics.hpp>

/* Include standard libraries */
#include <array>

/* Include custom fir.hpp library */
#include "fir.hpp"

/* Low pass 4 Hz FIR filter coefficients, assume sampling frequency at 25 Hz */
static const std::array<float, 16> low_pass_4hz = {
    -0.01083, -0.06309, -0.10576, -0.12135,
    -0.06452,  0.06436,  0.21868,  0.32375,
     0.32375,  0.21868,  0.06436, -0.06452,
    -0.12135, -0.10576, -0.06309, -0.01083,
};

/* High pass 10 Hz FIR filter coefficients, assume sampling frequency at 25 Hz */
static const std::array<float, 16> high_pass_10hz = {
    -0.06242,  0.11531, -0.17653,  0.22310,
    -0.24096,  0.21783, -0.15223,  0.05470,
     0.05470, -0.15223,  0.21783, -0.24096,
     0.22310, -0.17653,  0.11531, -0.06242,
};

/* namespace alias to STMicroelectronics sensors */
namespace STM = hux::sensors::STMicroelectronics;

/* Declare an inertial sensor with simulation data loaded from file
 * and configured at an output data rate of 25 Hz
 * */
HUX_DECLARE_SIMULATION_DATA(imu_sim_dataset,
    hux::simulation::load_csv<float, float, float, hux::uint64_t>("dataset.csv", ";")
);
HUX_DECLARE_SENSOR_CONFIGURATION(imu_config, STM::IIS2DLPC,
    .odr = STM::IIS2DLPC::configs::odr_25
);
HUX_DECLARE_SENSOR(imu, STM::IIS2DLPC, imu_sim_dataset, imu_config);

/* Send the acceleration of the inertial sensor over the Z axis through a channel */
HUX_DECLARE_CHANNEL(acc_z_ch, merge, imu.get_accZ());

/* Apply a low pass FIR filter over the acceleration data */
HUX_DECLARE_STATEFUL_PROCESSING(low_pass, fir_state_t<low_pass_4hz.size()>, acc_z_ch, {
    return fir_filter<low_pass_4hz.size()>(hux_state, low_pass_4hz, hux_input);
});

/* Apply a high pass FIR filter over the acceleration data */
HUX_DECLARE_STATEFUL_PROCESSING(high_pass, fir_state_t<high_pass_10hz.size()>, acc_z_ch, {
    return fir_filter<high_pass_10hz.size()>(hux_state, high_pass_10hz, hux_input);
});

/* Combine low and high pass acceleration data with a zip_latest channel */
HUX_DECLARE_CHANNEL(filter_ch, zip_latest, low_pass, high_pass);

/* Prepare the application output */
HUX_DECLARE_PROCESSING(output, filter_ch, {
    HUX_DECLARE_OUTPUT_VALUE(out_lp_z, Float, "low_pass", hux::get<0>(hux_input));
    HUX_DECLARE_OUTPUT_VALUE(out_hp_z, Float, "high_pass", hux::get<1>(hux_input));
    HUX_DECLARE_OUTPUT_VALUE(out, Object, "acceleration_z", out_lp_z, out_hp_z);
    return out;
});

HUX_REGISTER_OUTPUT(output);

The following is an example of a dataset.csv file used in the main application for simulating a IIS2DLPC sensor that records a 1 Hz signal on the Z acceleration axis:

accX;           accY;           accZ;           timestamp
-0.016572;      0.013709;       -0.986440;      1653408559000
0.024537;       0.001149;       -0.732817;      1653408559040
-0.004182;      -0.018094;      -0.507608;      1653408559080
-0.013477;      0.014813;       -0.291685;      1653408559120
0.022886;       -0.022052;      -0.175411;      1653408559160
-0.004114;      0.002691;       -0.042375;      1653408559200
-0.004280;      0.014947;       -0.005094;      1653408559240
0.004808;       -0.006104;      -0.042236;      1653408559280
0.024653;       0.015950;       -0.094597;      1653408559320
-0.010422;      0.004008;       -0.232962;      1653408559360
-0.010318;      -0.022360;      -0.411563;      1653408559400
-0.021452;      -0.006664;      -0.625100;      1653408559440
0.015425;       -0.005090;      -0.855007;      1653408559480
-0.019919;      0.005662;       -1.104035;      1653408559520
-0.009567;      0.011804;       -1.362305;      1653408559560
-0.003888;      -0.004833;      -1.576643;      1653408559600
0.002182;       -0.013544;      -1.770006;      1653408559640
0.024246;       -0.009171;      -1.924126;      1653408559680
0.022234;       0.002105;       -1.999583;      1653408559720
-0.006664;      0.007856;       -1.994960;      1653408559760
0.025922;       -0.020688;      -1.961948;      1653408559800
-0.005183;      -0.019553;      -1.840052;      1653408559840
0.019111;       -0.019405;      -1.688315;      1653408559880
0.016291;       0.003112;       -1.495852;      1653408559920
0.030361;       -0.001869;      -1.264616;      1653408559960
-0.018371;      0.013981;       -0.989187;      1653408560000
-0.017521;      -0.000498;      -0.754881;      1653408560040
-0.015006;      -0.014420;      -0.531531;      1653408560080
0.008786;       -0.005696;      -0.328645;      1653408560120
-0.016400;      0.005703;       -0.164868;      1653408560160
-0.015525;      -0.009500;      -0.047345;      1653408560200
0.028506;       -0.009639;      0.011150;       1653408560240
-0.000666;      0.015332;       -0.008139;      1653408560280
0.026692;       -0.023528;      -0.108727;      1653408560320
0.002639;       0.007631;       -0.212036;      1653408560360
-0.012415;      0.014837;       -0.388772;      1653408560400
-0.008944;      0.006342;       -0.617039;      1653408560440
0.010806;       0.005647;       -0.893109;      1653408560480
0.029497;       -0.014762;      -1.142354;      1653408560520
0.015104;       0.024775;       -1.372778;      1653408560560
0.033652;       -0.008516;      -1.585329;      1653408560600
-0.012921;      0.017995;       -1.769199;      1653408560640
-0.005548;      -0.003635;      -1.927700;      1653408560680
0.008480;       0.022677;       -1.961294;      1653408560720
0.028719;       0.017407;       -1.986284;      1653408560760
-0.009694;      -0.023200;      -1.934098;      1653408560800
0.014670;       0.019595;       -1.820349;      1653408560840
-0.001711;      0.010589;       -1.675386;      1653408560880
0.026336;       0.015640;       -1.493786;      1653408560920
0.015547;       -0.022674;      -1.233107;      1653408560960
0.023428;       0.003091;       -1.021332;      1653408561000