This is a Source plugin for MADS.
This plugin is a template taht shows how to acquire and publish data collected at high frequency. In fact, MADS agents work fine up to a timestep of a millisecond, but if you need faster acquisition (e.g. for sound or vibrartion) you want to buffer the data and publish a set of samples in batches.
At the moment, the code only shows how to buffer the data and publish the batch, but the MovingWindowStats class (provided in src/moving_window_stats.hpp) can be used for doing some preprocessing on the batches (in this case, FFT and ACF plus some basic statistics).
In case the FFT is needed, it would be probably wiser to only publish the list of peaks frequencies and their magnitudes, rather than the whole spectrum.
Required MADS version: 1.4.0.
The code provides the base class Acquisitor, which is supposed to be derived for real applications.
The class SerialportAcquisitor provides an example. Things to remember when deriving the base class are:
- The class constructor expects a
nlohmann::jsonobject containing thecapacityfield, which is the batch size (number of samples) - The base class has a template argument, which describes how a single sample is bundled. Derived classes must pick the proper container. For efficiency, we suggest to use a
std::array<double, size>type, wheresizeis the number of scalars in each sample. For example, a 6-DoF IMU would need astd::array<double, 6>, orstd::array<float, 6>if low resolution is enough, orstd::array<unsigned int, 6>if reading raw ineger data. - The class provides the
Acquisitor::samplestruct, which represents a single sample, made by atimetimestamp plus thedatafield (whis time is the class template param) - The derived class must implement its constructor and:
- the
setup()method, that prepared the device for reading data/measurements - and the
acquire()method, which reads a single sample of data and properly pack it into theAcquisitor::samplestruct
- the
- The plugin class file should only be changed to update the
#includefor the acquisitor subclass and by updating the type of the_acqsmart pointer (at the end ofsetup()and in the list of class members). Also the data packaging inget_output()must be adapted to the case.
The get_output() implementation in src/buffered.cpp and src/buffered_sp.cpp uses futures to provide a multi-threaded operation, so that the data packaging and elaboration happens in parallel to data acquisition, as depicted here:
sequenceDiagram
autonumber
loop
Main thread->>+Future: fill_buffer_async()
Future-->>+Main thread:
Main thread->>Future: wait()
deactivate Main thread
Future-->Main thread:
deactivate Future
end
This way, acquisition is requested in (1) from the main thread and starts immediately in (2) on the Future, which returns right away to the main thread. The latter starts preprocessing and packaging the data into JSON, and when it is done, it waits for the future to become available (3). When the acquisition is completed (4) the main thread moves to the next iteration (i.e. next call to get_output()).
Under normal conditions, the acquisition is continuous and there are no noticeable gaps. If the time needed for preprocessing and packaging data from the main thread is longer than the buffer acquisition time, though, a warning is raised, for that means that processing is too slow. Depending on the algorithms, increasing the buffer size might solve the issue. If it doesn't, one can only slow down the acquisition or make the preprocessing more efficient, or delegate the preprocessing to another agent and publish the data unprocessed (only packaged as JSON).
Currently, the supported platforms are:
- Linux
- MacOS
- Windows
Linux and MacOS:
cmake -Bbuild -DCMAKE_INSTALL_PREFIX="$(mads -p)"
cmake --build build -j4
sudo cmake --install buildWindows:
cmake -Bbuild -DCMAKE_INSTALL_PREFIX="$(mads -p)"
cmake --build build --config Release
cmake --install build --config ReleaseThe plugin supports the following settings in the INI file:
# Simple plugin, generating random data
[buffered]
capacity = 10 # Buffer capacity
# More complex plugin, collecting data by an Arduino
# with code on https://github.com/MADS-NET/arduino_plugin/tree/main/arduino/mads
[buffered_sp]
port = "/dev/cu.usbmodem34B7DA5F9A5C2"
baud = 115200
timeout = 100All settings are optional; if omitted, the default values are used.