diff --git a/docs/architecture/index.rst b/docs/architecture/index.rst new file mode 100644 index 0000000..85b9908 --- /dev/null +++ b/docs/architecture/index.rst @@ -0,0 +1,22 @@ +Architecture +============ + +.. toctree:: + :maxdepth: 1 + +The Sampling Engine is designed to provide a configurable interface that allows the system +to work in either the distance or time domains. + +The interface of the library is a simple Sampling Engine that is configured to operate an +internal sampling engine. The internal engine takes in time-based records and can generate +both distance-based and time-based records, and status records to specify which channels +are valid at which point during the data stream. + +The time-based records are inserted into a queue on receipt, and then pushed into to the +sampling engine. The sampling engine pushes the records through a series of filters to make +the desired calculations. The status records are generated by checking the status of the filters +used for each channel. The filters are then read for their values with the new record pushed +into an output queue which will be read by the caller via the exteranally visible Sampling Engine. + +NOTE: The sampling engine will eventually be able to delay records used for application specific +behavior using a similar mechanism. diff --git a/docs/index.rst b/docs/index.rst index ef42646..e8dd879 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -11,6 +11,8 @@ Contents: .. toctree:: :maxdepth: 2 + architecture/index + .. doxygenindex:: SamplingEngine diff --git a/include/samplingEngineInternal/filters/averaging.h b/include/samplingEngineInternal/filters/averaging.h index d308239..406baaa 100644 --- a/include/samplingEngineInternal/filters/averaging.h +++ b/include/samplingEngineInternal/filters/averaging.h @@ -28,7 +28,6 @@ namespace filters //! Constructor AveragingFilter() { - length = 0; } //! Destructor ~AveragingFilter() @@ -64,7 +63,7 @@ namespace filters sum += _value; values.push_back(_value); - if (values.length() > length) + if (values.size() > length) { sum -= values.front(); values.pop_front(); @@ -94,8 +93,8 @@ namespace filters protected: typedef std::deque datalist; /*!< internal type for storing the values */ - size_t length; /*!< filter length */ - T sum; /*!< Running summation of the values */ + size_t length{0}; /*!< filter length */ + T sum{0}; /*!< Running summation of the values */ datalist values; /*!< listing of values being averaged. */ }; diff --git a/include/samplingEngineInternal/generator/time_record_generator.h b/include/samplingEngineInternal/generator/time_record_generator.h new file mode 100644 index 0000000..b5b6df7 --- /dev/null +++ b/include/samplingEngineInternal/generator/time_record_generator.h @@ -0,0 +1,37 @@ +#ifndef INTERNAL_SAMPLING_ENGINE_RECORD_GENERATOR_H__ +#define INTERNAL_SAMPLING_ENGINE_RECORD_GENERATOR_H__ + +#include +#include + +namespace samplingEngine + { + namespace generator + { + namespace time + { + + class TimeRecordGenerator : public samplingEngine::generator::AbstractTimeRecordGenerator + { + public: + TimeRecordGenerator(); + virtual ~TimeRecordGenerator(); + + virtual void configure(const samplingEngine::generator::TimeRecordGeneratorConfiguration& _config); + virtual void initialize(); + virtual void shutdown(); + + virtual void generate_record(samplingEngine::records::time_record*& _record); + virtual void cleanup_record(samplingEngine::records::time_record*& _record); + + protected: + const samplingEngine::generator::TimeRecordGeneratorConfiguration* configuration; + // sensor emulators + + std::deque tachometers; + }; + } + } + } + +#endif //INTERNAL_SAMPLING_ENGINE_RECORD_GENERATOR_H__ diff --git a/include/samplingEngineInternal/geometricEngine/channelOffsets.h b/include/samplingEngineInternal/geometricEngine/channelOffsets.h index 94b4dbc..2d3c389 100644 --- a/include/samplingEngineInternal/geometricEngine/channelOffsets.h +++ b/include/samplingEngineInternal/geometricEngine/channelOffsets.h @@ -17,6 +17,7 @@ namespace geometricEngine size_t offset; size_t length; uint16_t index; + bool zero_on_invalid; }; typedef std::map offsetMapping; @@ -43,9 +44,21 @@ namespace geometricEngine int32_t storeTimeValue(samplingEngine::channels::distance::distanceChannels channel, struct samplingEngine::records::time_record*& _record); int32_t storeTimeStatusValue(samplingEngine::channels::distance::distanceChannels channel, struct samplingEngine::records::status_record*& _record); + size_t time_channel_size() const; + uint16_t time_channel_count() const; + + size_t distance_channel_size() const; + uint16_t distance_channel_count() const; + protected: struct channelOffsetMap timeRecords; struct channelOffsetMap distanceRecords; + + uint16_t size_time_channels; + uint16_t count_time_channels; + + size_t size_distance_channels; + uint16_t count_distance_channels; }; } diff --git a/include/samplingEngineInternal/geometricEngine/engine.h b/include/samplingEngineInternal/geometricEngine/engine.h index 5e11166..b394635 100644 --- a/include/samplingEngineInternal/geometricEngine/engine.h +++ b/include/samplingEngineInternal/geometricEngine/engine.h @@ -157,6 +157,9 @@ namespace geometricEngine // number of distance samples necessary for all channels to be valid assuming data is // within the tolerances of all the filters uint64_t maximum_sample_buffering; + // the current distance record index relative to the start of the engine + // NOTE: this resets on `open` + uint64_t current_distance_record; }; } diff --git a/include/samplingEngineInternal/interfaces/abstractFilter.h b/include/samplingEngineInternal/interfaces/abstractFilter.h index 0519422..16e084e 100644 --- a/include/samplingEngineInternal/interfaces/abstractFilter.h +++ b/include/samplingEngineInternal/interfaces/abstractFilter.h @@ -80,15 +80,15 @@ namespace samplingEngine virtual void processRecord(records::distance_record* _record)=0; // output - function should update its portion of the records - virtual void updateRecord(const records::time_record*& _record)=0; - virtual void updateRecord(const records::distance_record*& _record)=0; - virtual void updateRecord(const records::status_record*& _record, bool timeDomain)=0; + virtual void updateRecord(records::time_record*& _record)=0; + virtual void updateRecord(records::distance_record*& _record)=0; + virtual void updateRecord(records::status_record*& _record, bool timeDomain)=0; // set the current Time-based Record Input Index (start of time queue) virtual void setTimeRecordInputIndex(uint64_t _input_index, size_t _data_size)=0; // set the current Time-based Record Output Index (end of time queue) - virtual void setTimeRecordOutputIndex(uint64_t _input_inde, size_t _data_sizex)=0; + virtual void setTimeRecordOutputIndex(uint64_t _input_index, size_t _data_size)=0; // set the current Distance Record Output Index virtual void setDistanceRecordOutputIndex(uint64_t _input_index, size_t _data_size)=0; diff --git a/include/samplingEngineInternal/sensors/tachometer/abstractTachometer.h b/include/samplingEngineInternal/sensors/tachometer/abstractTachometer.h new file mode 100644 index 0000000..abca858 --- /dev/null +++ b/include/samplingEngineInternal/sensors/tachometer/abstractTachometer.h @@ -0,0 +1,52 @@ +#ifndef ABSTRACT_SENSOR_TACHOMETER_H__ +#define ABSTRACT_SENSOR_TACHOMETER_H__ + +#include + +#include + +#include + +namespace sensor + { + class abstractTachometerSensor: samplingEngine::interfaces::abstractSensor + { + public: + //! Constructor + abstractTachometerSensor(); + //! Destructor + virtual ~abstractTachometerSensor(); + + //! sensor name + /*! + Access the human readable name of the sensor + /returns std::string containing an UTF-8 ASCIIZ string with the name of the sensor + */ + virtual const std::string& sensorName()=0; + + // open the sensor for use + virtual void open(const struct samplingEngine::config::engineConfiguration& _configuration)=0; + // is the sensor open for use? + virtual bool isOpen() const=0; + + // reset the sensor data + virtual void reset()=0; + + // close the sensor + virtual void close()=0; + + // input - function should get the required information from the record + virtual void processRecord(const samplingEngine::records::time_record* _record)=0; + + // output - function should update its portion of the records + virtual void updateRecord(samplingEngine::records::time_record*& _record)=0; + virtual void updateRecord(samplingEngine::records::status_record*& _record, bool timeDomain)=0; + + // set the current Time-based Record Output Index (end of time queue) + virtual void setTimeRecordOutputIndex(uint64_t _input_index, size_t _data_size)=0; + }; + + typedef std::deque tachometerSensorList; + } + +#endif // ABSTRACT_SENSOR_TACHOMETER_H__ diff --git a/include/samplingEngineInternal/sensors/tachometer/fakeTachometer.h b/include/samplingEngineInternal/sensors/tachometer/fakeTachometer.h new file mode 100644 index 0000000..e22fa36 --- /dev/null +++ b/include/samplingEngineInternal/sensors/tachometer/fakeTachometer.h @@ -0,0 +1,74 @@ +#ifndef FAKE_SENSOR_TACHOMETER_H__ +#define FAKE_SENSOR_TACHOMETER_H__ + +#include + +#include +#include + +namespace sensor + { + namespace fake + { + class fakeTachometerSensor: sensor::abstractTachometerSensor + { + public: + //! Constructor + fakeTachometerSensor(); + //! Destructor + virtual ~fakeTachometerSensor(); + + //! sensor name + /*! + Access the human readable name of the sensor + /returns std::string containing an UTF-8 ASCIIZ string with the name of the sensor + */ + const std::string& sensorName(); + + // open the sensor for use + void open(const struct samplingEngine::config::engineConfiguration& _configuration); + // is the sensor open for use? + bool isOpen() const; + + // reset the sensor data + void reset(); + + // close the sensor + void close(); + + // input - function should get the required information from the record + void processRecord(const samplingEngine::records::time_record* _record); + + // output - function should update its portion of the records + void updateRecord(samplingEngine::records::time_record*& _record); + void updateRecord(samplingEngine::records::status_record*& _record, bool _timeDomain); + + // set the current Time-based Record Output Index (end of time queue) + void setTimeRecordOutputIndex(uint64_t _input_index, size_t _data_size); + protected: + // if officialTachometer is true, then this is the sole tachometer + // if it's false, then it needs to be stored into one of the MULTI_TACH channels + // so that the multiTachometer can combine them together + bool officialTachometer{false}; + bool ascending{true}; + uint32_t count_by{1}; + uint32_t max{0x00FFFFFF}; + uint32_t min{0x0}; + + // is the sensor active + bool active{false}; + + // location in time records + // samplingEngine::channels::time::timeChannels channelLocation; + + // sine wave generator + utils::sineWaveGeneratorUnsignedInt32 sineWave; + + private: + // official name + std::string name{"fake-tachometer"}; + }; + } + } + +#endif // FAKE_SENSOR_TACHOMETER_H__ diff --git a/include/samplingEngineInternal/sensors/tachometer/multiTachometer.h b/include/samplingEngineInternal/sensors/tachometer/multiTachometer.h new file mode 100644 index 0000000..ee6d75c --- /dev/null +++ b/include/samplingEngineInternal/sensors/tachometer/multiTachometer.h @@ -0,0 +1,88 @@ +#ifndef MULTI_TACHOMETER_H__ +#define MULTI_TACHOMETER_H__ + +#include + +#include +#include + +#include +#include + +namespace sensor + { + namespace multi + { + class tachometerEntry { + public: + filters::AveragingFilterUInt32 tach32; + filters::AveragingFilterUInt16 tach16; + + tachometerEntry(); + ~tachometerEntry(); + void init(); + void reset(); + void apply(uint32_t _value); + }; + + //! multiTachometer - integrate multiple tachometer sensors + /*! + Receive multiple tachometers and combine them together into a single time-based channels. + This requires all the hardware sensors to be integrated into the time-based record prior + to this tachometer being used. + + The multiTachometer works by averaging the hardware tachometer sensors and generating + a new tachometer sensor value. + */ + class multiTachometerSensor: abstractTachometerSensor + { + public: + //! Constructor + multiTachometerSensor(); + //! Destructor + virtual ~multiTachometerSensor(); + + //! sensor name + /*! + Access the human readable name of the sensor + /returns std::string containing an UTF-8 ASCIIZ string with the name of the sensor + */ + const std::string& sensorName(); + + // open the sensor for use + void open(const struct samplingEngine::config::engineConfiguration& _configuration); + // is the sensor open for use? + bool isOpen() const; + + // reset the sensor data + void reset(); + + // close the sensor + void close(); + + // input - function should get the required information from the record + void processRecord(const samplingEngine::records::time_record* _record); + + // output - function should update its portion of the records + void updateRecord(samplingEngine::records::time_record*& _record); + void updateRecord(samplingEngine::records::status_record*& _record, bool timeDomain); + + // set the current Time-based Record Output Index (end of time queue) + void setTimeRecordOutputIndex(uint64_t _input_index, size_t _data_size); + protected: + typedef std::map tachTracker; + + const std::string name{"multitach"}; + bool active{false}; + + // reads the MULTI_TACH_ channels, combines them together using an averaging + // algorithm and then puts the result into the singular tachometer record. + // these channels are only in use when the multi-tach is in use + uint32_t runningAverage; + + tachTracker inputTachometers; + }; + } + } + +#endif diff --git a/include/samplingEngineInternal/utils/sine_generator.h b/include/samplingEngineInternal/utils/sine_generator.h new file mode 100644 index 0000000..a8e1252 --- /dev/null +++ b/include/samplingEngineInternal/utils/sine_generator.h @@ -0,0 +1,126 @@ +#ifndef UTILS_SINE_GENERATOR_H__ +#define UTILS_SINE_GENERATOR_H__ + +#include +#include + +namespace utils + { + + //! utility for generating a sine-wave + /*! + The sine wve generator just produces a sine-like wave by incrementing a + numberic value. It is recommended to use an integer type for T, such + as uint8_t, uint16_t, uint32_t, or uint64_t or their signed equiavalents. + These are simple and efficient values. + + If unsigned types are used, then it is recommended to give a starting + offset equal to at least half the delta between the min and max ranges to + prevent rollover of the type. + */ + template + class sineWaveGenerator + { + public: + sineWaveGenerator() {} + ~sineWaveGenerator() {} + + void open(T _starting_offset, T _min, T _max, T _count_by, bool _ascending) + { + // encource that it must count by a value greater than 0 + if (_count_by < T(0)) { + throw std::out_of_range("counter must be greater than 0"); + } + + is_open = true; + max = _max; + min = _min; + start = _starting_offset; + count_by = _count_by; + ascending = _ascending; + if (start < _min || start > _max) + { + throw std::out_of_range("start is outside the specified min and max"); + } + // is there a good way to test the type to verify it + // has enough travel for the sinewave? Especially unsigned types? + if (int64_t(T(-1)) != int64_t(-1)) + { + // calculate the travel + double travel_distance = max - min; + // determine the max the value can hold + double type_max = double(T(-1)); + // check travel distance is smaller than the maximum of the type + // note: this is done using double floating point to try to match + // the maximum range possible. Theoretically a 64-bit unsigned + // integer could also work, but its better to use something larger + // than is actually used for this test + if (travel_distance > type_max) + { + throw std::out_of_range("storage type does not allow enough travel for the specified configuration"); + } + } + current = start; + } + bool isOpen() const + { + return is_open; + } + void reset() + { + current = start; + } + void close() + { + is_open = false; + } + + T read() + { + // for the purposes here, a sine-wave is just incrementing/decrementing + // the counter + T result = current; + if (ascending) { + current += count_by; + } else { + current -= count_by; + } + + if (current >= max) + { + ascending = false; + } + else if (current <= min) + { + ascending = true; + } + return result; + } + + T reverse() + { + ascending = !ascending; + } + + protected: + bool is_open{false}; + bool ascending{true}; + T max; + T min; + T start; + T current{0}; + T count_by{1}; + }; + + // common types that may be useful + typedef sineWaveGenerator sineWaveGeneratorSignedInt8; + typedef sineWaveGenerator sineWaveGeneratorUnsignedInt8; + typedef sineWaveGenerator sineWaveGeneratorSignedInt16; + typedef sineWaveGenerator sineWaveGeneratorUnsignedInt16; + typedef sineWaveGenerator sineWaveGeneratorSignedInt32; + typedef sineWaveGenerator sineWaveGeneratorUnsignedInt32; + typedef sineWaveGenerator sineWaveGeneratorSignedInt64; + typedef sineWaveGenerator sineWaveGeneratorUnsignedInt64; + } + +#endif //UTILS_SINE_GENERATOR_H__ diff --git a/public/generator/time_generator.h b/public/generator/time_generator.h new file mode 100644 index 0000000..09a7231 --- /dev/null +++ b/public/generator/time_generator.h @@ -0,0 +1,38 @@ +#ifndef SAMPLING_ENGINE_RECORD_GENERATOR_H__ +#define SAMPLING_ENGINE_RECORD_GENERATOR_H__ + +#include +#include + +namespace samplingEngine + { + namespace generator + { + + struct TimeRecordGeneratorConfiguration + { + uint16_t sample_rate{400}; // how many samples per second, default 400 HZ + uint8_t tachometer_count{1}; // how many tachometers should be emulated, default 1 + + samplingEngine::channels::channelList time_channels; // time record channel configuration + }; + + class AbstractTimeRecordGenerator + { + public: + AbstractTimeRecordGenerator(); + virtual ~AbstractTimeRecordGenerator(); + + virtual void configure(const TimeRecordGeneratorConfiguration& _config)=0; + virtual void initialize()=0; + virtual void shutdown()=0; + + virtual void generate_record(samplingEngine::records::time_record*& _record)=0; + virtual void cleanup_record(samplingEngine::records::time_record*& _record)=0; + }; + + AbstractTimeRecordGenerator* NewTimeRecordGenerator(); + } + } + +#endif //SAMPLING_ENGINE_RECORD_GENERATOR__ diff --git a/public/samplingEngine/channels/channel.h b/public/samplingEngine/channels/channel.h index da4a285..7210663 100644 --- a/public/samplingEngine/channels/channel.h +++ b/public/samplingEngine/channels/channel.h @@ -119,6 +119,15 @@ namespace samplingEngine */ uint16_t factor; + //! zero channel on invalid data + /*! + Each channel has a status. It may be desirable to show data even when + the channel data is invalid; or it may be desireable to not show any + information. By default (zero_on_invalid = false), all data is sent out; + if zero_on_invalid is set to true, then the channel will be zero'd out. + */ + bool zero_on_invalid; + //! channel description /*! A string containing user information about the channel. diff --git a/public/samplingEngine/channels/distance_channels.h b/public/samplingEngine/channels/distance_channels.h index c233048..f3b66c5 100644 --- a/public/samplingEngine/channels/distance_channels.h +++ b/public/samplingEngine/channels/distance_channels.h @@ -59,7 +59,15 @@ namespace samplingEngine LONGITUDINAL, /* X-Axis Rotation (Pitch) */ LATITUDINAL, /* Y-Axis Rotation (Roll) */ SPIN, /* Z-Axis Rotation (Yaw) */ + + // multi-tach support channels + MULTI_TACH_0, /* Multitach: Tachometer Signal 1 */ + MULTI_TACH_1, /* Multitach: Tachometer Signal 2 */ + MULTI_TACH_2, /* Multitach: Tachometer Signal 3 */ + MULTI_TACH_3, /* Multitach: Tachometer Signal 4 */ }; + + typedef std::deque distanceChannelList; } inline uint16_t distanceChannelToChannelType(samplingEngine::channels::distance::distanceChannels dc) diff --git a/public/samplingEngine/channels/time_channels.h b/public/samplingEngine/channels/time_channels.h index b1c6a0d..9621f43 100644 --- a/public/samplingEngine/channels/time_channels.h +++ b/public/samplingEngine/channels/time_channels.h @@ -2,6 +2,7 @@ #define SAMPLING_ENGINE_TIME_CHANNELS_H__ #include +#include namespace samplingEngine { @@ -59,7 +60,15 @@ namespace samplingEngine LONGITUDINAL, /* X-Axis Rotation (Pitch) */ LATITUDINAL, /* Y-Axis Rotation (Roll) */ SPIN, /* Z-Axis Rotation (Yaw) */ + + // multi-tach support channels + MULTI_TACH_0, /* Multitach: Tachometer Signal 1 */ + MULTI_TACH_1, /* Multitach: Tachometer Signal 2 */ + MULTI_TACH_2, /* Multitach: Tachometer Signal 3 */ + MULTI_TACH_3, /* Multitach: Tachometer Signal 4 */ }; + + typedef std::deque timeChannelList; } inline uint16_t timeChannelToChannelType(samplingEngine::channels::time::timeChannels tc) diff --git a/public/samplingEngine/configuration.h b/public/samplingEngine/configuration.h index 251dc6d..8b2aebd 100644 --- a/public/samplingEngine/configuration.h +++ b/public/samplingEngine/configuration.h @@ -25,7 +25,17 @@ namespace samplingEngine Calibration of the tachometer sensor data used to determine how much linear movement has occurred between any two samples. */ - uint64_t pulses_per_meter; + uint64_t pulses_per_meter{0}; + //! Enable a fake tachometer for testing + /*! + Test the system by generating a dummy tachometer value + */ + bool simulate_tachometer{false}; + bool simulator_official{false}; + bool simulator_ascending{true}; + uint32_t simulator_count_by{1}; + uint32_t simulator_max{0x00FFFFFF}; // 24-bit tachometer + uint32_t simulator_min{0x0}; }; //! Sample Configuration @@ -89,13 +99,13 @@ namespace samplingEngine /*! The constant number of records being provided into the engine, f.e 100 HZ. */ - uint16_t time_sample_rate_hz; + uint16_t time_sample_rate_hz{100}; //! Distance Sample Rate in Millimeters /*! The maximum distance between two distance records that the engine should generate, f.e 250 mm. */ - uint16_t distance_sample_rate_mm; + uint16_t distance_sample_rate_mm{250}; }; //! Sensor Configuration @@ -134,7 +144,7 @@ namespace samplingEngine A negative value moves forward in time/distance Default is zero, no delay */ - int32_t delay; + int32_t delay{0}; }; typedef std::map filterConfigurations; diff --git a/public/samplingEngine/error_codes.h b/public/samplingEngine/error_codes.h index d5dc930..c537b11 100644 --- a/public/samplingEngine/error_codes.h +++ b/public/samplingEngine/error_codes.h @@ -17,18 +17,20 @@ //! Short-cut for validating success #define SAMPLING_ENGINE_CHECK_SUCCESS(v) (SAMPLING_ENGINE_MATCHES_ERROR_CODE(v, SAMPLING_ENGINE_ERROR_SUCCESS)) -#define SAMPLING_ENGINE_ERROR_SUCCESS (0x00000000) /*!< Success - No Error */ -#define SAMPLING_ENGINE_ERROR_BAD_PARAMETER (0x00000001) /*!< Bad parameter was passed to the function */ -#define SAMPLING_ENGINE_ERROR_NOT_INITIALIZED (0x00000002) /*!< Engine Not initialized */ -#define SAMPLING_ENGINE_ERROR_ALREADY_INITIALIZED (0x00000003) /*!< Engine is already initialized */ -#define SAMPLING_ENGINE_ERROR_FILTER_MISMATCH (0x00000004) /*!< Filter mis-match */ -#define SAMPLING_ENGINE_ERROR_MEMORY_ALLOCATION (0x00000005) /*!< Unable to allocate memory */ -#define SAMPLING_ENGINE_ERROR_FILTER_NOT_PRIMED (0x00000006) /*!< Filter not ready */ -#define SAMPLING_ENGINE_ERROR_INVALID_POINTER (0x00000007) /*!< Invalid pointer (Special case of Bad Parameter) */ -#define SAMPLING_ENGINE_ERROR_DEPENDENCY_NOT_FOUND (0x00000008) /*!< Calculation dependency not found */ -#define SAMPLING_ENGINE_ERROR_NO_RECORDS_AVAILABLE (0x00000009) /*!< No records are currently available */ -#define SAMPLING_ENGINE_ERROR_INVALID_STATE (0x0000000A) /*!< System is in an invalid state */ -#define SAMPLING_ENGINE_ERROR_NOT_SUPPORTED_YET (0x0000000B) /*!< Functionality is not yet supported */ +#define SAMPLING_ENGINE_ERROR_SUCCESS (0x00000000) /*!< Success - No Error */ +#define SAMPLING_ENGINE_ERROR_BAD_PARAMETER (0x00000001) /*!< Bad parameter was passed to the function */ +#define SAMPLING_ENGINE_ERROR_NOT_INITIALIZED (0x00000002) /*!< Engine Not initialized */ +#define SAMPLING_ENGINE_ERROR_ALREADY_INITIALIZED (0x00000003) /*!< Engine is already initialized */ +#define SAMPLING_ENGINE_ERROR_FILTER_MISMATCH (0x00000004) /*!< Filter mis-match */ +#define SAMPLING_ENGINE_ERROR_MEMORY_ALLOCATION (0x00000005) /*!< Unable to allocate memory */ +#define SAMPLING_ENGINE_ERROR_FILTER_NOT_PRIMED (0x00000006) /*!< Filter not ready */ +#define SAMPLING_ENGINE_ERROR_INVALID_POINTER (0x00000007) /*!< Invalid pointer (Special case of Bad Parameter) */ +#define SAMPLING_ENGINE_ERROR_DEPENDENCY_NOT_FOUND (0x00000008) /*!< Calculation dependency not found */ +#define SAMPLING_ENGINE_ERROR_NO_RECORDS_AVAILABLE (0x00000009) /*!< No records are currently available */ +#define SAMPLING_ENGINE_ERROR_INVALID_STATE (0x0000000A) /*!< System is in an invalid state */ +#define SAMPLING_ENGINE_ERROR_NOT_SUPPORTED_YET (0x0000000B) /*!< Functionality is not yet supported */ +#define SAMPLING_ENGINE_ERROR_TOO_MANY_TIME_CHANNELS (0x0000000C) /*!< Time channel count will overrun the record size storage capacity */ +#define SAMPLING_ENGINE_ERROR_TOO_MANY_DISTNACE_CHANNELS (0x0000000D) /*!< Distance channel count will overrun the record size storage capacity */ #define SAMPLING_ENGINE_ERROR_MAX ( SAMPLING_ENGINE_ERROR_INVALID_STATE + 1) /*!< Maximum Error Value */ diff --git a/public/samplingEngine/records/distance_record.h b/public/samplingEngine/records/distance_record.h index 25fda27..6c362b0 100644 --- a/public/samplingEngine/records/distance_record.h +++ b/public/samplingEngine/records/distance_record.h @@ -87,6 +87,8 @@ namespace samplingEngine */ uint8_t channels[1]; }; + + const size_t distance_record_max_size = 0xFFFFFFFF; } } diff --git a/public/samplingEngine/records/status_record.h b/public/samplingEngine/records/status_record.h index 10fb1eb..d8d8421 100644 --- a/public/samplingEngine/records/status_record.h +++ b/public/samplingEngine/records/status_record.h @@ -89,6 +89,8 @@ namespace samplingEngine //! List of status entries, one for each configured channel struct samplingEngine::records::status_record_entry channelStatus[1]; }; + + const size_t status_record_max_size = 0xFFFFFFFF; } } diff --git a/public/samplingEngine/records/time_record.h b/public/samplingEngine/records/time_record.h index 645fea4..b25ea9f 100644 --- a/public/samplingEngine/records/time_record.h +++ b/public/samplingEngine/records/time_record.h @@ -48,6 +48,8 @@ namespace samplingEngine */ uint8_t channels[1]; }; + + const uint32_t time_record_max_size = 0xFFFFFFFF; } } diff --git a/public/samplingEngine/sensors/abstractSensor.h b/public/samplingEngine/sensors/abstractSensor.h new file mode 100644 index 0000000..eb2eeda --- /dev/null +++ b/public/samplingEngine/sensors/abstractSensor.h @@ -0,0 +1,70 @@ +#ifndef SAMPLING_ENGINE_INTERFACE_ABSTRACT_SENSOR_H__ +#define SAMPLING_ENGINE_INTERFACE_ABSTRACT_SENSOR_H__ + +#include + +#include +#include + +#include + +#include +#include + +namespace samplingEngine + { + namespace interfaces + { + //! List of Sensor names + typedef std::deque sensorNameList; + + class abstractSensor; + //! List of sensors + typedef std::deque abstractSensorList; + + //! Interface of a sensor + /*! + Generic interface for any implemented sensor + + Sensors can only work on time-based data. + */ + class abstractSensor + { + public: + //! Constructor + abstractSensor(); + //! Destructor + virtual ~abstractSensor(); + + //! sensor name + /*! + Access the human readable name of the sensor + /returns std::string containing an UTF-8 ASCIIZ string with the name of the sensor + */ + virtual const std::string& sensorName()=0; + + // open the sensor for use + virtual void open(const struct samplingEngine::config::engineConfiguration& _configuration)=0; + // is the sensor open for use? + virtual bool isOpen() const=0; + + // reset the sensor data + virtual void reset()=0; + + // close the sensor + virtual void close()=0; + + // input - function should get the required information from the record + virtual void processRecord(const samplingEngine::records::time_record* _record)=0; + + // output - function should update its portion of the records + virtual void updateRecord(samplingEngine::records::time_record*& _record)=0; + virtual void updateRecord(samplingEngine::records::status_record*& _record, bool timeDomain)=0; + + // set the current Time-based Record Output Index (end of time queue) + virtual void setTimeRecordOutputIndex(uint64_t _input_index, size_t _data_size)=0; + }; + } + } + +#endif // SAMPLING_ENGINE_INTERFACE_ABSTRACT_SENSOR_H__ diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index e4daf23..e52cf37 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -51,11 +51,11 @@ ENABLE_TESTING() ADD_SUBDIRECTORY(tests) # Command for building the HTML webpage version of the coverage data -#IF (${ENABLE_CODE_COVERAGE}) -# ADD_CUSTOM_TARGET( -# "coverage-html" -# COMMAND "gcovr -r ../ . --html-details ${CMAKE_BINARY_DIR}/coverage.html" -# BYPRODUCTS "coverage.html" -# USES_TERMINAL -# ) -#ENDIF() +IF (${ENABLE_CODE_COVERAGE}) + ADD_CUSTOM_TARGET( + "coverage-html" + COMMAND "gcovr -r ../ . --html-details coverage.html" + BYPRODUCTS "coverage.html coverage.*.html coverage.css" + USES_TERMINAL + ) +ENDIF() diff --git a/src/engines/channelOffsets.cpp b/src/engines/channelOffsets.cpp index 2637bb0..7b3aa54 100644 --- a/src/engines/channelOffsets.cpp +++ b/src/engines/channelOffsets.cpp @@ -1,5 +1,8 @@ #include #include +#include +#include +#include namespace geometricEngine { @@ -16,16 +19,32 @@ int32_t recordOffsetMap::initialize(const struct samplingEngine::config::engineC { int32_t returnValue = SAMPLING_ENGINE_MAKE_ERROR_CODE(SAMPLING_ENGINE_ERROR_SUCCESS); uint16_t index = 0; + size_time_channels = 0; + size_distance_channels = 0; for (auto iter = _configuration.time_channels.cbegin(); iter != _configuration.time_channels.cend(); ++iter) { struct dataPoint dp; dp.offset = iter->data_offset; dp.length = iter->byte_count; dp.index = index; - timeRecords.channel[iter->channel_type] = dp; + dp.zero_on_invalid = iter->zero_on_invalid; + + size_time_channels += iter->byte_count; + timeRecords.channel[iter->channel_type] = dp; timeRecords.status[iter->channel_type] = index; } + count_time_channels = index + 1; + if ((sizeof(samplingEngine::records::time_record) + size_time_channels) > samplingEngine::records::time_record_max_size) + { + returnValue = SAMPLING_ENGINE_MAKE_ERROR_CODE(SAMPLING_ENGINE_ERROR_TOO_MANY_TIME_CHANNELS); + return returnValue; + } + if ((sizeof(samplingEngine::records::status_record) + (sizeof(samplingEngine::records::status_record_entry)*count_time_channels)) > samplingEngine::records::status_record_max_size) + { + returnValue = SAMPLING_ENGINE_MAKE_ERROR_CODE(SAMPLING_ENGINE_ERROR_TOO_MANY_TIME_CHANNELS); + return returnValue; + } index = 0; for (auto iter = _configuration.distance_channels.cbegin(); iter != _configuration.distance_channels.cend(); ++iter) @@ -34,16 +53,34 @@ int32_t recordOffsetMap::initialize(const struct samplingEngine::config::engineC dp.offset = iter->data_offset; dp.length = iter->byte_count; dp.index = index; - distanceRecords.channel[iter->channel_type] = dp; + dp.zero_on_invalid = iter->zero_on_invalid; + + size_distance_channels += iter->byte_count; + distanceRecords.channel[iter->channel_type] = dp; distanceRecords.status[iter->channel_type] = index; } + count_distance_channels = index + 1; + if ((sizeof(samplingEngine::records::distance_record) + size_distance_channels) > samplingEngine::records::distance_record_max_size) + { + returnValue = SAMPLING_ENGINE_MAKE_ERROR_CODE(SAMPLING_ENGINE_ERROR_TOO_MANY_DISTNACE_CHANNELS); + return returnValue; + } + if ((sizeof(samplingEngine::records::status_record) + (sizeof(samplingEngine::records::status_record_entry)*count_distance_channels)) > samplingEngine::records::status_record_max_size) + { + returnValue = SAMPLING_ENGINE_MAKE_ERROR_CODE(SAMPLING_ENGINE_ERROR_TOO_MANY_TIME_CHANNELS); + return returnValue; + } return returnValue; } void recordOffsetMap::reset() { + count_time_channels = 0; + size_time_channels = 0; + count_distance_channels = 0; + size_distance_channels = 0; distanceRecords.channel.clear(); distanceRecords.status.clear(); timeRecords.channel.clear(); @@ -74,4 +111,23 @@ int32_t recordOffsetMap::storeTimeStatusValue(samplingEngine::channels::distance return returnValue; } +uint16_t recordOffsetMap::time_channel_count() const + { + return count_time_channels; + } + +size_t recordOffsetMap::time_channel_size() const + { + return size_time_channels; + } + +uint16_t recordOffsetMap::distance_channel_count() const + { + return count_distance_channels; + } + +size_t recordOffsetMap::distance_channel_size() const + { + return size_distance_channels; + } } diff --git a/src/engines/geometricEngine.cpp b/src/engines/geometricEngine.cpp index 8ed8a06..6716a77 100644 --- a/src/engines/geometricEngine.cpp +++ b/src/engines/geometricEngine.cpp @@ -1,6 +1,7 @@ #include #include -#include +#include +#include #include #include @@ -8,10 +9,19 @@ namespace geometricEngine { +// status records have a structure that make up each channel entry +inline size_t calculateStatusRecordSize(uint16_t _channel_count) + { + size_t status_size = sizeof(samplingEngine::records::status_record); + status_size += (sizeof(samplingEngine::records::status_record_entry) * _channel_count); + return status_size; + } + geometricEngine::geometricEngine() : samplingEngine::core::coreSamplingEngine() { geometric_engine_active = false; maximum_sample_buffering = 0; + current_distance_record = 0; } geometricEngine::~geometricEngine() @@ -38,6 +48,8 @@ int32_t geometricEngine::open(const struct samplingEngine::config::engineConfigu maximum_sample_buffering = std::max(maximum_sample_buffering, (*iter)->required_samples()); } } + // reset the distance record counter + current_distance_record = 0; return returnValue; } @@ -105,8 +117,10 @@ int32_t geometricEngine::addFilter(samplingEngine::interfaces::abstractFilter* _ return returnValue; } + int32_t geometricEngine::addInterdependentFilters(samplingEngine::interfaces::abstractFilterList& /* _interdependentFilters */) { + // filters that have seamlingly circular dependencies or close relationships int32_t returnValue = SAMPLING_ENGINE_MAKE_ERROR_CODE(SAMPLING_ENGINE_ERROR_NOT_SUPPORTED_YET); // TODO @@ -122,7 +136,7 @@ int32_t geometricEngine::processRecord(const struct samplingEngine::records::tim if (_record != NULL) { // copy the record into the internal data so we don't need to care about what - // happens ot the incoming record and can use it as we like + // happens to the incoming record and can use it as we like samplingEngine::queues::record_container new_record; new_record.record_data.time_record = (samplingEngine::records::time_record*) calloc(1, _record->length); memcpy(new_record.record_data.time_record, _record, _record->length); @@ -268,6 +282,8 @@ int32_t geometricEngine::process_records() { for (samplingEngine::interfaces::abstractFilterList::iterator iter = filters.begin(); iter != filters.end(); ++iter) { + (*iter)->setTimeRecordInputIndex(input_queue.front().record_data.time_record->index, maximum_sample_buffering); + (*iter)->setTimeRecordOutputIndex(input_queue.back().record_data.time_record->index, maximum_sample_buffering); (*iter)->processRecord(input_queue.front().record_data.time_record); } free(input_queue.front().record_data.time_record); @@ -275,17 +291,137 @@ int32_t geometricEngine::process_records() input_queue.pop_front(); } - // TODO: - // 1. Extract data - // 2. Delay the data appropriately - // 3. Build a status record - // 3.1 Add to output queue - // 4. Build a time record - // 4.1 Build Record - // 4.2 Add to output queue - // 5. Build a distance record - // 5.1 Build Record - // 5.2 Add to output queue + // make the output records - since their length is dynamic based + // on configuration they must be allocated on the heap even temporarily + struct samplingEngine::records::status_record* time_status = NULL; + struct samplingEngine::records::status_record* distance_status = NULL; + // time record + struct samplingEngine::records::time_record* time_record = NULL; + // distance record + struct samplingEngine::records::distance_record* distance_record = NULL; + + const size_t time_status_size = calculateStatusRecordSize(channelMapper.time_channel_count()); + time_status = (samplingEngine::records::status_record*) calloc(1, time_status_size); + if (time_status != NULL) + { + time_status->length = uint32_t(time_status_size); + time_status->id = samplingEngine::records::STATUS_RECORD_TYPE; + } + // time record uses a byte array; its size is only known by accruing the size of all the time-based channels + const size_t time_record_size = sizeof(samplingEngine::records::time_record) + channelMapper.time_channel_size(); + time_record = (samplingEngine::records::time_record*) calloc(1, time_record_size); + if (time_record != NULL) + { + time_record->length = uint32_t(time_record_size); + time_record->id = samplingEngine::records::TIME_RECORD_TYPE; + } + + const size_t distance_status_size = calculateStatusRecordSize(channelMapper.distance_channel_count()); + distance_status = (samplingEngine::records::status_record*) calloc(1, distance_status_size); + if (distance_status != NULL) + { + distance_status->length = uint32_t(distance_status_size); + distance_status->id = samplingEngine::records::STATUS_RECORD_TYPE; + } + + // distance record uses a byte array; its size is only known by accruing the size of all the distance-based channels + const size_t distance_record_size = sizeof(samplingEngine::records::distance_record) + channelMapper.time_channel_size(); + distance_record = (samplingEngine::records::distance_record*) calloc(1, distance_record_size); + if (distance_record != NULL) + { + distance_record->length = uint32_t(distance_record_size); + distance_record->id = samplingEngine::records::DISTANCE_RECORD_TYPE; + } + + // extract the data + for (samplingEngine::interfaces::abstractFilterList::iterator iter = filters.begin(); iter != filters.end(); ++iter) + { + // set the distance record data + (*iter)->setDistanceRecordOutputIndex(current_distance_record, maximum_sample_buffering); + if ((*iter)->isTimeBasedFilter() == true) + { + // update the time status record + if (time_status != NULL) + { + (*iter)->updateRecord(time_status, true); + } + // extract a time record + if (time_record != NULL) + { + (*iter)->updateRecord(time_record); + } + } + if ((*iter)->isDistanceBasedFilter() == true) + { + // update the distance status record + if (distance_status != NULL) + { + (*iter)->updateRecord(distance_status, false); + } + // extract a distnace record + if (distance_record != NULL) + { + (*iter)->updateRecord(distance_record); + } + } + } + + if (time_status != NULL) + { + samplingEngine::queues::record_container new_record; + new_record.record_data.status_record = (samplingEngine::records::status_record*) calloc(1, time_status->length); + if (new_record.record_data.status_record != NULL) + { + memcpy(new_record.record_data.status_record, time_status, time_status->length); + new_record.record_type = samplingEngine::records::STATUS_RECORD_TYPE; + output_queue.push_back(new_record); + } + } + if (time_record != NULL) + { + samplingEngine::queues::record_container new_record; + new_record.record_data.time_record = (samplingEngine::records::time_record*) calloc(1, time_record->length); + if (new_record.record_data.time_record != NULL) + { + memcpy(new_record.record_data.time_record, time_record, time_record->length); + new_record.record_type = samplingEngine::records::TIME_RECORD_TYPE; + output_queue.push_back(new_record); + } + } + if (distance_status != NULL) + { + samplingEngine::queues::record_container new_record; + new_record.record_data.status_record = (samplingEngine::records::status_record*) calloc(1, distance_status->length); + if (new_record.record_data.status_record != NULL) + { + memcpy(new_record.record_data.status_record, distance_status, distance_status->length); + new_record.record_type = samplingEngine::records::STATUS_RECORD_TYPE; + output_queue.push_back(new_record); + } + } + if (distance_record != NULL) + { + samplingEngine::queues::record_container new_record; + new_record.record_data.distance_record = (samplingEngine::records::distance_record*) calloc(1, distance_record->length); + if (new_record.record_data.distance_record != NULL) + { + memcpy(new_record.record_data.distance_record, distance_record, distance_record->length); + new_record.record_type = samplingEngine::records::DISTANCE_RECORD_TYPE; + output_queue.push_back(new_record); + } + } + + #define CLEANUP_MEMORY(p) \ + { \ + if (p != NULL ) \ + { \ + free(p); \ + } \ + } + CLEANUP_MEMORY(time_status); + CLEANUP_MEMORY(time_record); + CLEANUP_MEMORY(distance_status); + CLEANUP_MEMORY(distance_record); return returnValue; } diff --git a/src/generator/new_time_record_generator.cpp b/src/generator/new_time_record_generator.cpp new file mode 100644 index 0000000..762505b --- /dev/null +++ b/src/generator/new_time_record_generator.cpp @@ -0,0 +1,16 @@ +#include + +#include + +namespace samplingEngine + { + namespace generator + { + AbstractTimeRecordGenerator* NewTimeRecordGenerator() + { + // auto trg = new samplingEngine::generator::time::TimeRecordGenerator(); + // return reinterpret_cast(trg); + return new samplingEngine::generator::time::TimeRecordGenerator(); + } + } + } diff --git a/src/generator/time_record_generator.cpp b/src/generator/time_record_generator.cpp new file mode 100644 index 0000000..6dc0c93 --- /dev/null +++ b/src/generator/time_record_generator.cpp @@ -0,0 +1,36 @@ +#include + +namespace samplingEngine + { + namespace generator + { + namespace time + { + TimeRecordGenerator::TimeRecordGenerator() + { + } + TimeRecordGenerator::~TimeRecordGenerator() + { + } + void TimeRecordGenerator::configure(const samplingEngine::generator::TimeRecordGeneratorConfiguration& /*(_config */) + { + } + void TimeRecordGenerator::initialize() + { + } + void TimeRecordGenerator::shutdown() + { + } + void TimeRecordGenerator::generate_record(samplingEngine::records::time_record*& /* _record */) + { + // allocate a record + // fill it out + // return it + } + void TimeRecordGenerator::cleanup_record(samplingEngine::records::time_record*& /* _record */) + { + // deallocate a record + } + } + } + } diff --git a/src/sensors/abstractSensor.cpp b/src/sensors/abstractSensor.cpp new file mode 100644 index 0000000..13f005a --- /dev/null +++ b/src/sensors/abstractSensor.cpp @@ -0,0 +1,17 @@ +#include +#include + +#include +#include + +namespace samplingEngine { + namespace interfaces { + + abstractSensor::abstractSensor() + { + } + abstractSensor::~abstractSensor() + { + } + } +} diff --git a/src/sensors/tachometer/abstractTachometer.cpp b/src/sensors/tachometer/abstractTachometer.cpp new file mode 100644 index 0000000..a9bb769 --- /dev/null +++ b/src/sensors/tachometer/abstractTachometer.cpp @@ -0,0 +1,14 @@ +#include +#include + +namespace sensor { + + abstractTachometerSensor::abstractTachometerSensor() : samplingEngine::interfaces::abstractSensor() + { + } + + abstractTachometerSensor::~abstractTachometerSensor() + { + } + +} diff --git a/src/sensors/tachometer/fakeTachometer.cpp b/src/sensors/tachometer/fakeTachometer.cpp new file mode 100644 index 0000000..92c31a5 --- /dev/null +++ b/src/sensors/tachometer/fakeTachometer.cpp @@ -0,0 +1,78 @@ +#include +#include + +namespace sensor { + + namespace fake { + + fakeTachometerSensor::fakeTachometerSensor() : sensor::abstractTachometerSensor() + { + } + + fakeTachometerSensor::~fakeTachometerSensor() + { + } + + const std::string& fakeTachometerSensor::sensorName() + { + return name; + } + + void fakeTachometerSensor::open(const struct samplingEngine::config::engineConfiguration& _configuration) + { + // + active = _configuration.sensors.tachometer.simulate_tachometer; + if (true == active) + { + officialTachometer = _configuration.sensors.tachometer.simulator_official; + ascending = _configuration.sensors.tachometer.simulator_ascending; + count_by = _configuration.sensors.tachometer.simulator_count_by; + min = _configuration.sensors.tachometer.simulator_min; + max = _configuration.sensors.tachometer.simulator_max; + sineWave.open(0, min, max, count_by, ascending); + + // how would time channels apply to this? + // _configuration.time_channels. + } + } + + bool fakeTachometerSensor::isOpen() const + { + return active; + } + + void fakeTachometerSensor::reset() + { + sineWave.reset(); + } + + void fakeTachometerSensor::close() + { + sineWave.close(); + } + + void fakeTachometerSensor::processRecord(const samplingEngine::records::time_record* /* _record */) + { + // there is nothing to process as the data is generated here + } + + void fakeTachometerSensor::updateRecord(samplingEngine::records::time_record*& _record) + { + if (_record != NULL) + { + _record->tachometer = int64_t(sineWave.read()); + } + } + + void fakeTachometerSensor::updateRecord(samplingEngine::records::status_record*& /* _record */, bool /* _timeDomain */) + { + // there are no channels to manage + } + + void fakeTachometerSensor::setTimeRecordOutputIndex(uint64_t /* _input_index */, size_t /* _data_size_ */) + { + // nothing to track as its part of the core record data + } + + } +} diff --git a/src/sensors/tachometer/multiTachometer.cpp b/src/sensors/tachometer/multiTachometer.cpp new file mode 100644 index 0000000..57eea79 --- /dev/null +++ b/src/sensors/tachometer/multiTachometer.cpp @@ -0,0 +1,77 @@ +#include +#include + +namespace sensor { + + namespace multi { + + tachometerEntry::tachometerEntry() + { + } + tachometerEntry::~tachometerEntry() + { + } + void tachometerEntry::init() + { + } + void tachometerEntry::reset() + { + } + void tachometerEntry::apply(uint32_t /* _value */) + { + //uint32_t mask = 0x0000FFFF; + //uint32_t roll_managed = _value & mask; + } + + multiTachometerSensor::multiTachometerSensor() : sensor::abstractTachometerSensor() + { + } + + multiTachometerSensor::~multiTachometerSensor() + { + } + + const std::string& multiTachometerSensor::sensorName() + { + return name; + } + + void multiTachometerSensor::open(const struct samplingEngine::config::engineConfiguration& /* _configuration */) + { + } + + bool multiTachometerSensor::isOpen() const + { + return active; + } + + void multiTachometerSensor::reset() + { + } + + void multiTachometerSensor::close() + { + } + + void multiTachometerSensor::processRecord(const samplingEngine::records::time_record* /* _record */) + { + } + + void multiTachometerSensor::updateRecord(samplingEngine::records::time_record*& _record) + { + if (_record != NULL) + { + // _record->tachometer = int64_t(sineWave.read()); + } + } + + void multiTachometerSensor::updateRecord(samplingEngine::records::status_record*& /* _record */, bool /* _timeDomain */) + { + } + + void multiTachometerSensor::setTimeRecordOutputIndex(uint64_t /* _input_index */, size_t /* _data_size_ */) + { + } + + } +} diff --git a/src/tests/filters/test_averaging_filter.cpp b/src/tests/filters/test_averaging_filter.cpp new file mode 100644 index 0000000..2deb3db --- /dev/null +++ b/src/tests/filters/test_averaging_filter.cpp @@ -0,0 +1,92 @@ +#define BOOST_TEST_DYN_LINK +#define BOOST_TEST_NO_MAIN + +#include + +#include +#include + +#include + +template +modType getMod(modType value, modType max) + { + // ensure not too large + if (value >= max) + { + value -= max; + } + // ensure not too small + if (value < 0) + { + value += max; + } + return value; + } + +BOOST_AUTO_TEST_SUITE( AveragingFilters ); + +typedef boost::mpl::list test_types; + +// be careful with the length and values as the combination +// needs to be within the limit of the types. For simplicity here +// keep the total value under 128; otherwise additional tests will be +// needed for int8_t and uint8_t. +const size_t length = 10; + +BOOST_AUTO_TEST_CASE_TEMPLATE( AveragingFilters_initialize, T, test_types ) +{ + filters::AveragingFilter averagingFilterInstance; + + averagingFilterInstance.initialize(length); + BOOST_CHECK_EQUAL(averagingFilterInstance.getLength(), length); +} + +BOOST_AUTO_TEST_CASE_TEMPLATE( AveragingFilters_empty, T, test_types ) +{ + filters::AveragingFilter averagingFilterInstance; + + averagingFilterInstance.initialize(length); + + T result = averagingFilterInstance.get(); + BOOST_CHECK_EQUAL(result, 0); +} + +BOOST_AUTO_TEST_CASE_TEMPLATE( AveragingFilters_average_const, T, test_types ) +{ + filters::AveragingFilter averagingFilterInstance; + + averagingFilterInstance.initialize(length); + T value = 10; + for (size_t i = 0; i < length; ++i) + { + averagingFilterInstance.apply(value); + } + + BOOST_CHECK_EQUAL(averagingFilterInstance.getLength(), length); + T result = averagingFilterInstance.get(); + BOOST_CHECK_EQUAL(result, value); +} + +BOOST_AUTO_TEST_CASE_TEMPLATE( AveragingFilters_average_variable, T, test_types ) +{ + filters::AveragingFilter averagingFilterInstance; + + averagingFilterInstance.initialize(length); + T total = 0; + T value_max = 10; + + for (size_t i = 0; i < length; ++i) + { + T value = getMod(rand(), value_max) + 1; + averagingFilterInstance.apply(value); + total += value; + } + T expected_average = total / length; + + BOOST_CHECK_EQUAL(averagingFilterInstance.getLength(), length); + T result = averagingFilterInstance.get(); + BOOST_CHECK_EQUAL(result, expected_average); +} + +BOOST_AUTO_TEST_SUITE_END(); diff --git a/src/utils/sine_generator.cpp b/src/utils/sine_generator.cpp new file mode 100644 index 0000000..8d84c34 --- /dev/null +++ b/src/utils/sine_generator.cpp @@ -0,0 +1 @@ +#include