Program data flow
Main role of a
Scheduler is to make whole system run and work properly. Data is processed incessantly until external or internal "signal" informs
Scheduler that it should stop. First thing to be done is creation of necessary objects and buffers in the correct order, then data processing is started.
Scheduler provides an interface for both internal (library classes) and external (user and library) communication.
For example in our implementation there is
DataFeeder class, which is responsible for data acquisition from tuner (or file) and writing it to a buffer, and
Synchronizer class, which is intended for transmission mode detection, NULL symbol finding and carrier frequency drift calculation. Buffer pointers for writing and reading are assigned to these classes (note, it is hard to implement buffer logic, because size of reading data depends on used transmission mode and usually differs from size of writing data).
Synchronizer requires continuous data, thus it may be necessary to copy certain amount of samples from buffer end to buffer begin when it wraps. Also information about carrier frequency drift is fed back to
DataFeeder for correction.
DataDecoder objects are created. During the first use of
Demodulator only FIC is demodulated, because station positions in the transmission frame are unknown. This demodulated data should be saved in yet another buffer for
DataDecoder, which now is ready to decode it. Decoding reveals what radio stations are in the multiplex and what are their parameters (like channel start address and size, kbps etc.), so it's possible to demodulate and decode only one selected station. Task for
Scheduler is to deliver these parameters back to
DataDecoder classes, so they will be able to properly process required data. Next,
Scheduler should create a buffer for exchanging data between these classes (it may be created with constant or variable size, depending on channel size). Buffer logic also needa to be handled, because
Demodulator demodulates one frame at once (FIC and MSC), but multiple frames are needed by
DataDecoder to start data processing (for example in Mode I four frames are required).
When station parameters from FIC are known, it can be deduced whether DAB or DAB+ technology is being broadcasted, thus
AudioDecoder object can be created. It it responsible for playing decoded music on speakers and optionally saving it to a file. DAB uses MPEG-2 codec and DAB+ uses AAC+ codec. Also creation of a buffer is needed (
DataDecoder writes there decoded data and
AudioDecoder reads from there).
Another task for
Scheduler is calculating sampling frequency drift. It can be done by determining length between NULL symbol positions in two consecutive frames and comparing it to reference length well known from the standard. FIC also yields various information which may be useful for user (like current date and time, program type).
To utilize those classes and their methods in continuous data processing, we have created a state machine. Implementation could have been based on various state machine patterns; we used here the simplest one, a switch-case statement. States are implemented as separate methods in
Scheduler class, they are showed in the state machine diagram below.
User can send an external signals via public methods, for example: list connected devices (dongles), quit or restart the program. On the other hand
Scheduler communicates with user by calling virtual methods, which can be implemented as user sees fit; those methods can for example: list stations or get interesting information from FIC.