diff --git a/openrtx/include/core/ringbuf.h b/openrtx/include/core/ringbuf.h
new file mode 100644
index 00000000..419e427b
--- /dev/null
+++ b/openrtx/include/core/ringbuf.h
@@ -0,0 +1,167 @@
+/***************************************************************************
+ * Copyright (C) 2022 by Federico Amedeo Izzo IU2NUO, *
+ * Niccolò Izzo IU2KIN *
+ * Frederik Saraci IU2NRO *
+ * Silvano Seva IU2KWO *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 3 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, see *
+ ***************************************************************************/
+
+#ifndef RINGBUF_H
+#define RINGBUF_H
+
+#ifndef __cplusplus
+#error This header is C++ only!
+#endif
+
+#include
+#include
+
+/**
+ * Class implementing a statically allocated circular buffer with blocking and
+ * non-blocking push and pop functions.
+ */
+template < typename T, size_t N >
+class RingBuffer
+{
+public:
+
+ /**
+ * Constructor.
+ */
+ RingBuffer() : readPos(0), writePos(0), numElements(0)
+ {
+ pthread_mutex_init(&mutex, NULL);
+ pthread_cond_init(¬_empty, NULL);
+ pthread_cond_init(¬_full, NULL);
+ }
+
+ /**
+ * Destructor.
+ */
+ ~RingBuffer()
+ {
+ pthread_mutex_destroy(&mutex);
+ pthread_cond_destroy(¬_empty);
+ pthread_cond_destroy(¬_full);
+ }
+
+ /**
+ * Push an element to the buffer.
+ *
+ * @param elem: element to be pushed.
+ * @param blocking: if set to true, when the buffer is full this function
+ * blocks the execution flow until at least one empty slot is available.
+ * @return true if the element has been successfully pushed to the queue,
+ * false if the queue is empty.
+ */
+ bool push(const T& elem, bool blocking)
+ {
+ pthread_mutex_lock(&mutex);
+
+ if((numElements >= N) && (blocking == false))
+ {
+ // No space available and non-blocking call: unlock mutex and return
+ pthread_mutex_unlock(&mutex);
+ return false;
+ }
+
+ // The call is blocking: wait until there is some free space
+ while(numElements >= N)
+ {
+ pthread_cond_wait(¬_full, &mutex);
+ }
+
+ // There is free space, push data into the queue
+ data[writePos] = elem;
+ writePos = (writePos + 1) % N;
+
+ // Signal that the queue is not empty
+ if(numElements == 0) pthread_cond_signal(¬_empty);
+ numElements += 1;
+
+ pthread_mutex_unlock(&mutex);
+ return true;
+ }
+
+ /**
+ * Pop an element from the buffer.
+ *
+ * @param elem: place where to store the popped element.
+ * @param blocking: if set to true, when the buffer is empty this function
+ * blocks the execution flow until at least one element is available.
+ * @return true if the element has been successfully popped from the queue,
+ * false if the queue is empty.
+ */
+ bool pop(T& elem, bool blocking)
+ {
+ pthread_mutex_lock(&mutex);
+
+ if(numElements == 0)
+ {
+ if(blocking)
+ {
+ while(numElements == 0)
+ {
+ pthread_cond_wait(¬_empty, &mutex);
+ }
+ }
+ else
+ {
+ pthread_mutex_unlock(&mutex);
+ return false;
+ }
+ }
+
+ elem = data[readPos];
+ readPos = (readPos + 1) % N;
+ numElements -= 1;
+ pthread_mutex_unlock(&mutex);
+
+ return true;
+ }
+
+ /**
+ * Check if the buffer is empty.
+ *
+ * @return true if the buffer is empty.
+ */
+ bool empty()
+ {
+ return numElements == 0;
+ }
+
+ /**
+ * Check if the buffer is full.
+ *
+ * @return true if the buffer is full.
+ */
+ bool full()
+ {
+ return numElements >= N;
+ }
+
+private:
+
+ size_t readPos; ///< Read pointer.
+ size_t writePos; ///< Write pointer.
+ size_t numElements; ///< Number of elements currently present.
+ T data[N]; ///< Data storage.
+
+ pthread_mutex_t mutex; ///< Mutex for concurrent access.
+ pthread_cond_t not_empty; ///< Queue not empty condition.
+ pthread_cond_t not_full; ///< Queue not full condition.
+};
+
+#endif // RINGBUF_H