summaryrefslogtreecommitdiffstats
path: root/src/fig
diff options
context:
space:
mode:
authorMatthias P. Braendli <matthias.braendli@mpb.li>2015-07-18 22:20:11 +0200
committerMatthias P. Braendli <matthias.braendli@mpb.li>2015-07-18 22:20:11 +0200
commit43aebb09438ff589a7be141a5af0aec2c27eaa95 (patch)
treea01cf8fc060407dc665b456aa0bb5514b0d3a5aa /src/fig
parentba8a05f29b819eb99d9cc9e69f8da7003e2c79c1 (diff)
downloaddabmux-43aebb09438ff589a7be141a5af0aec2c27eaa95.tar.gz
dabmux-43aebb09438ff589a7be141a5af0aec2c27eaa95.tar.bz2
dabmux-43aebb09438ff589a7be141a5af0aec2c27eaa95.zip
Add FIG0/2, fib0 scheduler
Diffstat (limited to 'src/fig')
-rw-r--r--src/fig/FIG.h24
-rw-r--r--src/fig/FIG0.cpp145
-rw-r--r--src/fig/FIG0.h26
-rw-r--r--src/fig/FIGCarousel.cpp323
-rw-r--r--src/fig/FIGCarousel.h21
5 files changed, 317 insertions, 222 deletions
diff --git a/src/fig/FIG.h b/src/fig/FIG.h
index 70b9316..90dd872 100644
--- a/src/fig/FIG.h
+++ b/src/fig/FIG.h
@@ -52,12 +52,36 @@ enum class FIG_rate {
E, // all in two minutes
};
+/* Helper function to calculate the deadline for the next transmission, in milliseconds */
+inline int rate_increment_ms(FIG_rate rate)
+{
+ switch (rate) {
+ case FIG_rate::FIG0_0: return 0; // Is a special case
+ case FIG_rate::A: return 100;
+ case FIG_rate::B: return 1000;
+ case FIG_rate::C: return 10000;
+ case FIG_rate::D: return 30000;
+ case FIG_rate::E: return 120000;
+ }
+}
+
class IFIG
{
public:
virtual size_t fill(uint8_t *buf, size_t max_size) = 0;
virtual FIG_rate repetition_rate(void) = 0;
+
+ virtual const int figtype(void) const = 0;
+ virtual const int figextension(void) const = 0;
+
+ virtual const std::string name(void) const
+ {
+ std::stringstream ss;
+ ss << figtype() << "/" << figextension();
+ return ss.str();
+ }
+
};
#endif // __FIG_H_
diff --git a/src/fig/FIG0.cpp b/src/fig/FIG0.cpp
index 85a06b5..09dff4d 100644
--- a/src/fig/FIG0.cpp
+++ b/src/fig/FIG0.cpp
@@ -59,9 +59,9 @@ size_t FIG0_0::fill(uint8_t *buf, size_t max_size)
}
//=========== FIG 0/1 ===========
-//
-FIG0_1::FIG0_1(FIGRuntimeInformation *rti) : m_rti(rti)
+FIG0_1::FIG0_1(FIGRuntimeInformation *rti) :
+ m_rti(rti)
{
subchannelFIG0_1 = m_rti->ensemble->subchannels.end();
}
@@ -72,8 +72,8 @@ size_t FIG0_1::fill(uint8_t *buf, size_t max_size)
if (max_size < 6) {
return 0;
}
- FIGtype0_1 *fig0_1;
- figtype0_1 = (FIGtype0_1 *)buf;
+ FIGtype0_1 *figtype0_1;
+ figtype0_1 = (FIGtype0_1*)buf;
figtype0_1->FIGtypeNumber = 0;
figtype0_1->Length = 1;
@@ -100,7 +100,7 @@ size_t FIG0_1::fill(uint8_t *buf, size_t max_size)
}
if (protection->form == UEP) {
- fig0_1subchShort =
+ FIG_01_SubChannel_ShortF *fig0_1subchShort =
(FIG_01_SubChannel_ShortF*)buf;
fig0_1subchShort->SubChId = (*subchannelFIG0_1)->id;
@@ -119,7 +119,7 @@ size_t FIG0_1::fill(uint8_t *buf, size_t max_size)
figtype0_1->Length += 3;
}
else if (protection->form == EEP) {
- fig0_1subchLong1 =
+ FIG_01_SubChannel_LongF *fig0_1subchLong1 =
(FIG_01_SubChannel_LongF*)buf;
fig0_1subchLong1->SubChId = (*subchannelFIG0_1)->id;
@@ -146,3 +146,136 @@ size_t FIG0_1::fill(uint8_t *buf, size_t max_size)
return max_size - remaining;
}
+
+//=========== FIG 0/2 ===========
+
+FIG0_2::FIG0_2(FIGRuntimeInformation *rti) :
+ m_rti(rti)
+{
+ serviceProgFIG0_2 = m_rti->ensemble->services.end();
+}
+
+size_t FIG0_2::fill(uint8_t *buf, size_t max_size)
+{
+ FIGtype0_2 *fig0_2 = NULL;
+ int cur = 0;
+ ssize_t remaining = max_size;
+
+ // Rotate through the subchannels until there is no more
+ // space
+ if (serviceProgFIG0_2 == m_rti->ensemble->services.end()) {
+ serviceProgFIG0_2 = m_rti->ensemble->services.begin();
+ }
+
+ for (; serviceProgFIG0_2 != m_rti->ensemble->services.end();
+ ++serviceProgFIG0_2) {
+ if ((*serviceProgFIG0_2)->nbComponent(m_rti->ensemble->components) == 0) {
+ continue;
+ }
+
+ if ((*serviceProgFIG0_2)->getType(m_rti->ensemble) != Audio) {
+ continue;
+ }
+
+ ++cur;
+
+ if (fig0_2 == NULL) {
+ fig0_2 = (FIGtype0_2 *)buf;
+
+ fig0_2->FIGtypeNumber = 0;
+ fig0_2->Length = 1;
+ fig0_2->CN = 0;
+ fig0_2->OE = 0;
+ fig0_2->PD = 0;
+ fig0_2->Extension = 2;
+ buf += 2;
+ remaining -= 2;
+ }
+
+ if (remaining < 3 + 2 *
+ (*serviceProgFIG0_2)->nbComponent(m_rti->ensemble->components)) {
+ break;
+ }
+
+ FIGtype0_2_Service *fig0_2serviceAudio = (FIGtype0_2_Service*)buf;
+
+ fig0_2serviceAudio->SId = htons((*serviceProgFIG0_2)->id);
+ fig0_2serviceAudio->Local_flag = 0;
+ fig0_2serviceAudio->CAId = 0;
+ fig0_2serviceAudio->NbServiceComp =
+ (*serviceProgFIG0_2)->nbComponent(m_rti->ensemble->components);
+ buf += 3;
+ fig0_2->Length += 3;
+ remaining -= 3;
+
+ int curCpnt = 0;
+ for (auto component = getComponent(
+ m_rti->ensemble->components, (*serviceProgFIG0_2)->id );
+ component != m_rti->ensemble->components.end();
+ component = getComponent(
+ m_rti->ensemble->components,
+ (*serviceProgFIG0_2)->id,
+ component )
+ ) {
+ auto subchannel = getSubchannel(
+ m_rti->ensemble->subchannels, (*component)->subchId);
+
+ if (subchannel == m_rti->ensemble->subchannels.end()) {
+ etiLog.log(error,
+ "Subchannel %i does not exist for component "
+ "of service %i\n",
+ (*component)->subchId, (*component)->serviceId);
+ throw MuxInitException();
+ }
+
+ switch ((*subchannel)->type) {
+ case Audio:
+ {
+ auto audio_description = (FIGtype0_2_audio_component*)buf;
+ audio_description->TMid = 0;
+ audio_description->ASCTy = (*component)->type;
+ audio_description->SubChId = (*subchannel)->id;
+ audio_description->PS = ((curCpnt == 0) ? 1 : 0);
+ audio_description->CA_flag = 0;
+ }
+ break;
+ case DataDmb:
+ {
+ auto data_description = (FIGtype0_2_data_component*)buf;
+ data_description->TMid = 1;
+ data_description->DSCTy = (*component)->type;
+ data_description->SubChId = (*subchannel)->id;
+ data_description->PS = ((curCpnt == 0) ? 1 : 0);
+ data_description->CA_flag = 0;
+ }
+ break;
+ case Packet:
+ {
+ auto packet_description = (FIGtype0_2_packet_component*)buf;
+ packet_description->TMid = 3;
+ packet_description->setSCId((*component)->packet.id);
+ packet_description->PS = ((curCpnt == 0) ? 1 : 0);
+ packet_description->CA_flag = 0;
+ }
+ break;
+ default:
+ etiLog.log(error,
+ "Component type not supported\n");
+ throw MuxInitException();
+ }
+ buf += 2;
+ fig0_2->Length += 2;
+ remaining -= 2;
+ if (remaining < 0) {
+ etiLog.log(error,
+ "Sorry, no space left in FIG 0/2 to insert "
+ "component %i of program service %i.\n",
+ curCpnt, cur);
+ throw MuxInitException();
+ }
+ ++curCpnt;
+ }
+ }
+ return max_size - remaining;
+}
+
diff --git a/src/fig/FIG0.h b/src/fig/FIG0.h
index 502ce7e..248b501 100644
--- a/src/fig/FIG0.h
+++ b/src/fig/FIG0.h
@@ -35,10 +35,14 @@
class FIG0_0 : public IFIG
{
public:
- FIG0_0(FIGRuntimeInformation* rti) : m_rti(rti) {}
+ FIG0_0(FIGRuntimeInformation* rti) :
+ m_rti(rti) {}
virtual size_t fill(uint8_t *buf, size_t max_size);
virtual FIG_rate repetition_rate(void) { return FIG_rate::A; }
+ virtual const int figtype(void) const { return 0; }
+ virtual const int figextension(void) const { return 0; }
+
private:
FIGRuntimeInformation *m_rti;
};
@@ -52,10 +56,30 @@ class FIG0_1 : public IFIG
virtual size_t fill(uint8_t *buf, size_t max_size);
virtual FIG_rate repetition_rate(void) { return FIG_rate::A; }
+ virtual const int figtype(void) const { return 0; }
+ virtual const int figextension(void) const { return 1; }
+
private:
FIGRuntimeInformation *m_rti;
std::vector<dabSubchannel*>::iterator subchannelFIG0_1;
};
+// FIG type 0/2, MCI, Service Organization, one instance of
+// FIGtype0_2_Service for each subchannel
+class FIG0_2 : public IFIG
+{
+ public:
+ FIG0_2(FIGRuntimeInformation* rti);
+ virtual size_t fill(uint8_t *buf, size_t max_size);
+ virtual FIG_rate repetition_rate(void) { return FIG_rate::A; }
+
+ virtual const int figtype(void) const { return 0; }
+ virtual const int figextension(void) const { return 1; }
+
+ private:
+ FIGRuntimeInformation *m_rti;
+ std::vector<std::shared_ptr<DabService> >::iterator serviceProgFIG0_2;
+};
+
#endif // __FIG0_H_
diff --git a/src/fig/FIGCarousel.cpp b/src/fig/FIGCarousel.cpp
index 4e53ef5..eb78269 100644
--- a/src/fig/FIGCarousel.cpp
+++ b/src/fig/FIGCarousel.cpp
@@ -27,14 +27,32 @@
*/
#include "fig/FIGCarousel.h"
-#include "fig/FIG0.h"
+#include <iostream>
-FIGCarousel::FIGCarousel(boost::shared_ptr<dabEnsemble> ensemble)
+void FIGCarouselElement::reduce_deadline()
+{
+ deadline -= rate_increment_ms(fig->repetition_rate());
+
+ if (deadline < 0) {
+ std::cerr << "FIG " << fig->name() <<
+ "has negative scheduling deadline" << std::endl;
+ }
+}
+
+FIGCarousel::FIGCarousel(boost::shared_ptr<dabEnsemble> ensemble) :
+ m_fig0_0(&m_rti),
+ m_fig0_1(&m_rti),
+ m_fig0_2(&m_rti)
{
m_rti.ensemble = ensemble;
m_rti.currentFrame = 0;
- m_figs.emplace_back<FIG0_0>(&m_rti);
- m_figs.emplace_back<FIG0_1>(&m_rti);
+ m_figs_available[std::make_pair(0, 0)] = &m_fig0_0;
+ m_figs_available[std::make_pair(0, 1)] = &m_fig0_1;
+ m_figs_available[std::make_pair(0, 2)] = &m_fig0_2;
+
+ allocate_fig_to_fib(0, 0, 0);
+ allocate_fig_to_fib(0, 1, 0);
+ allocate_fig_to_fib(0, 2, 0);
}
void FIGCarousel::set_currentFrame(unsigned long currentFrame)
@@ -42,232 +60,108 @@ void FIGCarousel::set_currentFrame(unsigned long currentFrame)
m_rti.currentFrame = currentFrame;
}
-#if 0
-void fib0 {
- switch (insertFIG) {
-
- case 0:
- case 4:
- case 8:
- case 12:
- // FIG type 0/0, Multiplex Configuration Info (MCI),
- // Ensemble information
- fig0_0 = (FIGtype0_0 *) & etiFrame[index];
-
- fig0_0->FIGtypeNumber = 0;
- fig0_0->Length = 5;
- fig0_0->CN = 0;
- fig0_0->OE = 0;
- fig0_0->PD = 0;
- fig0_0->Extension = 0;
-
- fig0_0->EId = htons(ensemble->id);
- fig0_0->Change = 0;
- fig0_0->Al = 0;
- fig0_0->CIFcnt_hight = (currentFrame / 250) % 20;
- fig0_0->CIFcnt_low = (currentFrame % 250);
- index = index + 6;
- figSize += 6;
-
- break;
+void FIGCarousel::allocate_fig_to_fib(int figtype, int extension, int fib)
+{
+ if (fib < 0 or fib >= 3) {
+ throw std::out_of_range("Invalid FIB");
+ }
- case 1:
- case 6:
- case 10:
- case 13:
- // FIG type 0/1, MIC, Sub-Channel Organization,
- // one instance of the part for each subchannel
- figtype0_1 = (FIGtype0_1 *) & etiFrame[index];
-
- figtype0_1->FIGtypeNumber = 0;
- figtype0_1->Length = 1;
- figtype0_1->CN = 0;
- figtype0_1->OE = 0;
- figtype0_1->PD = 0;
- figtype0_1->Extension = 1;
- index = index + 2;
- figSize += 2;
-
- // Rotate through the subchannels until there is no more
- // space in the FIG0/1
- if (subchannelFIG0_1 == ensemble->subchannels.end()) {
- subchannelFIG0_1 = ensemble->subchannels.begin();
- }
+ auto fig = m_figs_available.find(std::make_pair(figtype, extension));
- for (; subchannelFIG0_1 != ensemble->subchannels.end();
- ++subchannelFIG0_1) {
- dabProtection* protection = &(*subchannelFIG0_1)->protection;
+ if (fig != m_figs_available.end()) {
+ FIGCarouselElement el;
+ el.fig = fig->second;
+ el.deadline = 0;
+ m_fibs[fib].push_back(el);
+ }
+ else {
+ std::stringstream ss;
+ ss << "No FIG " << figtype << "/" << extension << " available";
+ throw std::runtime_error(ss.str());
+ }
+}
- if ( (protection->form == UEP && figSize > 27) ||
- (protection->form == EEP && figSize > 26) ) {
- break;
- }
+void FIGCarousel::fib0(int framephase) {
+ std::list<FIGCarouselElement> figs = m_fibs[0];
- if (protection->form == UEP) {
- fig0_1subchShort =
- (FIG_01_SubChannel_ShortF*) &etiFrame[index];
- fig0_1subchShort->SubChId = (*subchannelFIG0_1)->id;
+ std::vector<FIGCarouselElement> sorted_figs;
- fig0_1subchShort->StartAdress_high =
- (*subchannelFIG0_1)->startAddress / 256;
- fig0_1subchShort->StartAdress_low =
- (*subchannelFIG0_1)->startAddress % 256;
+ /* Decrement all deadlines according to the desired repetition rate */
+ for (auto& fig_el : figs) {
+ fig_el.reduce_deadline();
- fig0_1subchShort->Short_Long_form = 0;
- fig0_1subchShort->TableSwitch = 0;
- fig0_1subchShort->TableIndex =
- protection->uep.tableIndex;
+ sorted_figs.push_back(fig_el);
+ }
- index = index + 3;
- figSize += 3;
- figtype0_1->Length += 3;
- }
- else if (protection->form == EEP) {
- fig0_1subchLong1 =
- (FIG_01_SubChannel_LongF*) &etiFrame[index];
- fig0_1subchLong1->SubChId = (*subchannelFIG0_1)->id;
-
- fig0_1subchLong1->StartAdress_high =
- (*subchannelFIG0_1)->startAddress / 256;
- fig0_1subchLong1->StartAdress_low =
- (*subchannelFIG0_1)->startAddress % 256;
-
- fig0_1subchLong1->Short_Long_form = 1;
- fig0_1subchLong1->Option = protection->eep.GetOption();
- fig0_1subchLong1->ProtectionLevel =
- protection->level;
-
- fig0_1subchLong1->Sub_ChannelSize_high =
- getSizeCu(*subchannelFIG0_1) / 256;
- fig0_1subchLong1->Sub_ChannelSize_low =
- getSizeCu(*subchannelFIG0_1) % 256;
-
- index = index + 4;
- figSize += 4;
- figtype0_1->Length += 4;
- }
+ /* Sort the FIGs in the FIB according to their deadline */
+ std::sort(sorted_figs.begin(), sorted_figs.end(),
+ []( const FIGCarouselElement& left,
+ const FIGCarouselElement& right) {
+ return left.deadline < right.deadline;
+ });
+
+ /* Data structure to carry FIB */
+ const size_t fib_size = 30;
+ uint8_t fib_data[fib_size];
+ uint8_t *fib_data_current = fib_data;
+ size_t available_size = fib_size;
+
+ /* Take special care for FIG0/0 */
+ auto fig0_0 = find_if(sorted_figs.begin(), sorted_figs.end(),
+ [](const FIGCarouselElement& f) {
+ return f.fig->repetition_rate() == FIG_rate::FIG0_0;
+ });
+
+ if (fig0_0 != sorted_figs.end()) {
+ sorted_figs.erase(fig0_0);
+
+ if (framephase == 0) { // TODO check for all TM
+ size_t written = fig0_0->fig->fill(fib_data_current, available_size);
+
+ if (written > 0) {
+ available_size -= written;
+ fib_data_current += written;
}
- break;
-
- case 2:
- case 9:
- case 11:
- case 14:
- // FIG type 0/2, MCI, Service Organization, one instance of
- // FIGtype0_2_Service for each subchannel
- fig0_2 = NULL;
- cur = 0;
-
- // Rotate through the subchannels until there is no more
- // space in the FIG0/1
- if (serviceProgFIG0_2 == ensemble->services.end()) {
- serviceProgFIG0_2 = ensemble->services.begin();
+ else {
+ throw std::runtime_error("Failed to write FIG0/0");
}
+ }
+ }
- for (; serviceProgFIG0_2 != ensemble->services.end();
- ++serviceProgFIG0_2) {
- if (!(*serviceProgFIG0_2)->nbComponent(ensemble->components)) {
- continue;
- }
-
- if ((*serviceProgFIG0_2)->getType(ensemble) != 0) {
- continue;
- }
-
- ++cur;
-
- if (fig0_2 == NULL) {
- fig0_2 = (FIGtype0_2 *) & etiFrame[index];
-
- fig0_2->FIGtypeNumber = 0;
- fig0_2->Length = 1;
- fig0_2->CN = 0;
- fig0_2->OE = 0;
- fig0_2->PD = 0;
- fig0_2->Extension = 2;
- index = index + 2;
- figSize += 2;
- }
+ /* Fill the FIB with the FIGs, taking the earliest deadline first */
+ while (available_size > 0 and not sorted_figs.empty()) {
+ auto fig_el = sorted_figs.begin();
+ size_t written = fig_el->fig->fill(fib_data_current, available_size);
- if (figSize + 3
- + (*serviceProgFIG0_2)->nbComponent(ensemble->components)
- * 2 > 30) {
- break;
- }
+ if (written > 0) {
+ available_size -= written;
+ fib_data_current += written;
+ }
- fig0_2serviceAudio = (FIGtype0_2_Service*) &etiFrame[index];
+ sorted_figs.erase(fig_el);
+ }
+}
- fig0_2serviceAudio->SId = htons((*serviceProgFIG0_2)->id);
- fig0_2serviceAudio->Local_flag = 0;
- fig0_2serviceAudio->CAId = 0;
- fig0_2serviceAudio->NbServiceComp =
- (*serviceProgFIG0_2)->nbComponent(ensemble->components);
- index += 3;
- fig0_2->Length += 3;
- figSize += 3;
+#if 0
+void fib0 {
+ switch (insertFIG) {
- int curCpnt = 0;
- for (component = getComponent(ensemble->components,
- (*serviceProgFIG0_2)->id);
- component != ensemble->components.end();
- component = getComponent(ensemble->components,
- (*serviceProgFIG0_2)->id, component)) {
- subchannel = getSubchannel(ensemble->subchannels,
- (*component)->subchId);
+ case 0: case 4: case 8: case 12:
+ // FIG type 0/0, Multiplex Configuration Info (MCI),
+ // Ensemble information
+ // ERASED
+ break;
- if (subchannel == ensemble->subchannels.end()) {
- etiLog.log(error,
- "Subchannel %i does not exist for component "
- "of service %i\n",
- (*component)->subchId, (*component)->serviceId);
- throw MuxInitException();
- }
+ case 1: case 6: case 10: case 13:
+ // FIG type 0/1, MIC, Sub-Channel Organization,
+ // one instance of the part for each subchannel
+ // ERASED
+ break;
- switch ((*subchannel)->type) {
- case Audio:
- audio_description =
- (FIGtype0_2_audio_component*)&etiFrame[index];
- audio_description->TMid = 0;
- audio_description->ASCTy = (*component)->type;
- audio_description->SubChId = (*subchannel)->id;
- audio_description->PS = ((curCpnt == 0) ? 1 : 0);
- audio_description->CA_flag = 0;
- break;
- case DataDmb:
- data_description =
- (FIGtype0_2_data_component*)&etiFrame[index];
- data_description->TMid = 1;
- data_description->DSCTy = (*component)->type;
- data_description->SubChId = (*subchannel)->id;
- data_description->PS = ((curCpnt == 0) ? 1 : 0);
- data_description->CA_flag = 0;
- break;
- case Packet:
- packet_description =
- (FIGtype0_2_packet_component*)&etiFrame[index];
- packet_description->TMid = 3;
- packet_description->setSCId((*component)->packet.id);
- packet_description->PS = ((curCpnt == 0) ? 1 : 0);
- packet_description->CA_flag = 0;
- break;
- default:
- etiLog.log(error,
- "Component type not supported\n");
- throw MuxInitException();
- }
- index += 2;
- fig0_2->Length += 2;
- figSize += 2;
- if (figSize > 30) {
- etiLog.log(error,
- "Sorry, no space left in FIG 0/2 to insert "
- "component %i of program service %i.\n",
- curCpnt, cur);
- throw MuxInitException();
- }
- ++curCpnt;
- }
- }
+ case 2: case 9: case 11: case 14:
+ // FIG type 0/2, MCI, Service Organization, one instance of
+ // FIGtype0_2_Service for each subchannel
+ // ERASED
break;
case 3:
@@ -518,3 +412,4 @@ void fib0 {
}
}
#endif
+
diff --git a/src/fig/FIGCarousel.h b/src/fig/FIGCarousel.h
index cbb6868..42b8d9c 100644
--- a/src/fig/FIGCarousel.h
+++ b/src/fig/FIGCarousel.h
@@ -30,20 +30,39 @@
#define __FIG_CAROUSEL_H_
#include "fig/FIG.h"
+#include "fig/FIG0.h"
#include <list>
+#include <map>
#include <boost/shared_ptr.hpp>
#include "MuxElements.h"
+struct FIGCarouselElement {
+ IFIG* fig;
+ int deadline;
+
+ void reduce_deadline(void);
+};
+
class FIGCarousel {
public:
FIGCarousel(boost::shared_ptr<dabEnsemble> ensemble);
void set_currentFrame(unsigned long currentFrame);
+ void allocate_fig_to_fib(int figtype, int extension, int fib);
+
+ void fib0(int framephase);
+
private:
FIGRuntimeInformation m_rti;
- std::list<IFIG> m_figs;
+ std::map<std::pair<int, int>, IFIG*> m_figs_available;
+
+ // Each FIB contains a list of carousel elements
+ std::map<int, std::list<FIGCarouselElement> > m_fibs;
+ FIG0_0 m_fig0_0;
+ FIG0_1 m_fig0_1;
+ FIG0_2 m_fig0_2;
};
#endif // __FIG_CAROUSEL_H_