Time management module¶
[[TOC]]
Introduction¶
Several parts of the 4G/5G protocol stack keep track of time, for example for timeout events (in RRC, PDCP, or RLC for example).
The obvious simple way to deal with time is to use the computer’s realtime clock. This works well when we run the stack in realtime, with real radio equipment. But in simulation the system may run faster or slower than realtime, so we need another way to deal with time. A good solution is then to base time on the current IQ data processed by the system. So time becomes “IQ samples” based, not realtime.
The system may be decomposed into various components (CU, DU, RU, various UEs) running either in realtime or simulation. Time may be distributed to all these components from only one time source for accurate execution of the scenario, or each component may have its own time source, either based on realtime or IQ samples.
Overall structure¶
The time management module is made of three main parts. The first one is the time source. It can either be realtime or “IQ samples” based. In one running scenario of the system, there can be one or more time sources (imagine one for the gNB, and one per each UE). There can also be only one time source, shared between various users of the time manager.
The second part is the server. One server is attached to one time source and receives a tick every millisecond (real or simulated) from the time source. It distributes the tick to the various connected clients.
And finally the last part is the client, which connects to a single server and receives ticks from it. The client does not have a time source attached to it. It gets the time from the server.
It is possible for a program (monolithic gNB for example) to have only a time source, without server or client.
Configuration¶
To configure the time management module, add the following section in the configuration file.
time_management = {
#valid time sources: realtime, iq_samples
time_source = realtime
#valid modes: standalone, server, client
mode = standalone
#set ip/port of server (for server mode, this is the address/port to bind to;
#for client mode, this is the address/port to connect to)
server_ip = "127.0.0.1"
server_port = 7374
}
If not set in the configuration file, some default is applied, depending
on the program run. (See the function time_manager_start() for details.)
It is also possible to pass configuration parameters on the command line.
For example add --time_manager.time_source iq_samples to the command line
to set the time source to iq_samples. The same syntax is applicable to the
other parameters.
Examples¶
Here come some examples of configuration for typical use cases of OAI.
Monolithic gNB realtime¶
This is a very simple example. The gNB runs in standalone mode with
a realtime time source.
time_management = {
time_source = realtime
mode = standalone
}
Monolithic gNB “IQ samples time”¶
For this configuration, it’s the same as the previous example, but the
time source is now iq_samples.
time_management = {
time_source = iq_samples
mode = standalone
}
RF simulator CU/DU “IQ samples time”¶
Let’s suppose the CU runs on a machine with IP address 1.2.3.4 and the DU runs on a machine with IP address 10.11.12.13.
The DU has the time source and so is the server. The CU acts as a client.
Here comes the configuration for the CU.
time_management = {
mode = client
server_ip = "10.11.12.13"
server_port = 7374
}
time_management = {
time_source = iq_samples
mode = server
server_ip = "10.11.12.13"
server_port = 7374
}
RF simulator CU/DU “realtime”¶
Let’s suppose the CU runs on a machine with IP address 1.2.3.4 and the DU runs on a machine with IP address 10.11.12.13.
CU and DU can both run in standalone mode, with no exchange between them. Any one of them can also acts as a server and the other one as a client.
Let’s suppose for this example that the CU and the DU run in client/server mode. Let’s set the CU as the time source and so as the server. The DU acts as a client.
Here comes the configuration for the CU.
time_management = {
time_source = realtime
mode = server
server_ip = "1.2.3.4"
server_port = 7374
}
time_management = {
mode = client
server_ip = "1.2.3.4"
server_port = 7374
}
Note that the time management module is flexible. It’s possible to configure more complex setups. For example the time source can be a gNB and several UEs can be connected as client.
One could also write a simple program acting as a time source and distributing time to everyone (one or several gNB, several UEs).
Programming API¶
Here comes the internal API of the time manager module. The (simpler) OAI API is described afterwards.
Time source¶
- opaque type:
time_source_t - create a time source:
where
time_source_t *new_time_source(time_source_type_t type)typeis one of: TIME_SOURCE_REALTIMETIME_SOURCE_IQ_SAMPLES- delete a time source:
void free_time_source(time_source_t *time_source) - set millisecond callback:
where
void time_source_set_callback(time_source_t *time_source, void (*callback)(void *), void *callback_data)callbackis a function that will be called for each millisecond using the providedcallback_data - for IQ samples time management, the user of the time source must call
this function when it processes IQ samples:
then the time source will generate a tick for each block for IQ samples lasting one millisecond. (If you pass
void time_source_iq_add(time_source_t *time_source, uint64_t iq_samples_count, uint64_t iq_samples_per_second)iq_samples_countlasting several milliseconds then the time source will generate several ticks in a row, one for each millisecond.)
Server¶
- opaque type:
time_server_t - create a server:
where ip/port is the IP adress and port the server will listen to and
time_server_t *new_time_server(const char *ip, int port, void (*callback)(void *), void *callback_data)callbackis a function that will be called for each millisecond using the providedcallback_data - delete a server:
void free_time_server(time_server_t *time_server) - attach a time source:
void time_server_attach_time_source(time_server_t *time_server, time_source_t *time_source)
Client¶
- opaque type:
time_client_t - create a client:
the client will connect on given server ip/port and use the
time_client_t *new_time_client(const char *server_ip, int server_port, void (*callback)(void *), void *callback_data)callbackwithcallback_datafor each tick received - delete a client:
void free_time_client(time_client_t *time_client)
Threading¶
Each component (time source, server, client) runs its own thread which will be the one calling the configured callback. Be careful of using proper synchronization techniques in your callback to have correct behavior with the other threads of the program.
OAI API¶
OAI uses the time manager through a simplified API. All the code is contained
in time_manager.c, together with some global variables (so that there is no
need to pass objects around, to limit risks of misuse).
Each program (for example: gnb, ue, cu, du) calls the time_manager_start()
function, passing the callback functions to be called when a tick is generated
(several callbacks can be passed) and a default time source. Based on this,
time_manager_start() initializes what is needed.
The function time_manager_iq_samples() is to be called by programs that
read IQ samples. It is called unconditionally. It may do nothing if the
configuration is to use realtime ticks and not IQ samples ticks.
When the program exits, it calls timer_manager_finish() which in turns stops
the various threads created by time_manager_start() and releases all the
allocated data.
Here comes the API.
- init the time manager:
(for valid values of
void time_manager_start(time_manager_tick_function_t *tick_functions, int tick_functions_count, time_source_type_t time_source);client_typeandrunning_modesee intime_manager.h) - update IQ samples’ based time manager:
void time_manager_iq_samples(uint64_t iq_samples_count, uint64_t iq_samples_per_second) - terminate the time manager:
void time_manager_finish(void)