diff options
Diffstat (limited to 'src/roof-config.c')
-rw-r--r-- | src/roof-config.c | 245 |
1 files changed, 245 insertions, 0 deletions
diff --git a/src/roof-config.c b/src/roof-config.c new file mode 100644 index 0000000..189c08f --- /dev/null +++ b/src/roof-config.c @@ -0,0 +1,245 @@ +#include <stdio.h> +#include <stdint.h> + +#include <json-glib/json-glib.h> + +#include <ufo/ufo.h> + +#include "ufo-roof.h" +#include "ufo-roof-error.h" +#include "ufo-roof-config.h" + +#define roof_config_node_get_with_default(var, parent, type, name, default) do { \ + JsonNode *node = json_object_get_member(parent, name); \ + if (node) var = json_node_get_##type(node); \ + else var = default; \ + } while(0) + +#define roof_config_node_get_string_with_default(var, parent, name, default) do { \ + const gchar *str; \ + JsonNode *node = json_object_get_member(parent, name); \ + if (node) str = json_node_get_string(node); \ + else str = default; \ + if (var != str) { \ + if (var) g_free(var); \ + var = g_strdup(str); \ + } \ + } while(0) + +#define roof_config_node_get(var, parent, type, name) \ + roof_config_node_get_with_default(var, parent, type, name, var) + +#define roof_config_node_get_string(var, parent, name) \ + roof_config_node_get_string_with_default(var, parent, name, var) + + +typedef struct { + RoofConfig cfg; + + JsonParser *parser; +} RoofConfigPrivate; + +void roof_config_free(RoofConfig *cfg) { + if (cfg) { + RoofConfigPrivate *priv = (RoofConfigPrivate*)cfg; + + if (priv->parser) + g_object_unref (priv->parser); + + free(cfg); + } +} + +RoofConfig *roof_config_new(const char *config, RoofConfigFlags flags, GError **error) { + RoofConfigPrivate *priv; + RoofConfig *cfg; + +// JsonNode *node; + JsonObject *root = NULL; + JsonObject *hardware = NULL; + JsonObject *geometry = NULL; + JsonObject *optics = NULL; + JsonObject *network = NULL; + JsonObject *performance = NULL; + JsonObject *simulation = NULL; + JsonObject *reconstruction = NULL; + JsonObject *data = NULL; + + GError *gerr = NULL; + + priv = (RoofConfigPrivate*)malloc(sizeof(RoofConfigPrivate)); + if (!priv) roof_new_error(error, "Can't allocate RoofConfig"); + + memset(priv, 0, sizeof(RoofConfigPrivate)); + + // Set defaults + cfg = &priv->cfg; + + cfg->roof_mode = FALSE; + cfg->n_planes = 2; + cfg->n_modules = 16; + cfg->channels_per_module = 16; + cfg->bit_depth = 16; + cfg->samples_per_rotation = 1000; + cfg->sample_rate = 0; + cfg->imaging_rate = 0; + + cfg->port = 52067; + cfg->n_streams = 1; + cfg->protocol = "udp"; + cfg->network_timeout = 100000000; // FIXME: remove 0 + cfg->header_size = sizeof(RoofPacketHeader); + cfg->payload_size = 0; + cfg->max_packet_size = 0; + cfg->max_packets = 100; + cfg->dataset_size = 0; + cfg->buffer_size = 2; + cfg->latency_buffers = 0; + cfg->drop_buffers = 0; + cfg->sockets_per_thread = 4; + + + // Read configuration + priv->parser = json_parser_new_immutable (); + json_parser_load_from_file (priv->parser, config, &gerr); + + if (gerr != NULL) { + g_propagate_prefixed_error(error, gerr, "Error parsing JSON file (%s) with ROOF configuration: ", config); + roof_config_free(cfg); + return NULL; + } + + root = json_node_get_object (json_parser_get_root (priv->parser)); + + if (root) { + roof_config_node_get(hardware, root, object, "hardware"); + roof_config_node_get(geometry, root, object, "geometry"); + roof_config_node_get(optics, root, object, "optics"); + roof_config_node_get(network, root, object, "network"); + roof_config_node_get(reconstruction, root, object, "reconstruction"); + roof_config_node_get(performance, root, object, "performance"); + roof_config_node_get(data, root, object, "data"); + + if (flags&ROOF_CONFIG_SIMULATION) + roof_config_node_get(simulation, root, object, "simulation"); + } + + if (hardware) { + roof_config_node_get(cfg->n_planes, hardware, int, "planes"); + roof_config_node_get(cfg->n_modules, hardware, int, "modules"); + roof_config_node_get(cfg->channels_per_module, hardware, int, "channels_per_module"); + roof_config_node_get(cfg->bit_depth, hardware, int, "bit_depth"); + + roof_config_node_get(cfg->samples_per_rotation, hardware, int, "samples_per_rotation"); + roof_config_node_get(cfg->sample_rate, hardware, int, "sample_rate"); + roof_config_node_get(cfg->imaging_rate, hardware, int, "imaging_rate"); + + if ((cfg->sample_rate)||(cfg->imaging_rate)) { + if ((!cfg->sample_rate)||(!cfg->imaging_rate)||(cfg->sample_rate%cfg->imaging_rate)) { + roof_config_free(cfg); + roof_new_error(error, "Invalid sample (%u) and imaging (%u) rates are specified", cfg->sample_rate, cfg->imaging_rate); + } + + if ((json_object_get_member(hardware, "samples_per_rotation"))&&(cfg->samples_per_rotation != (cfg->sample_rate / cfg->imaging_rate))) { + roof_config_free(cfg); + roof_new_error(error, "The specified samples-per-rotation (%u) doesn't match sample/imaging rates (%u / %u)", cfg->samples_per_rotation, cfg->sample_rate, cfg->imaging_rate); + } + + cfg->samples_per_rotation = cfg->sample_rate / cfg->imaging_rate; + } + + if ((cfg->bit_depth%8)||(cfg->bit_depth > 32)) { + roof_config_free(cfg); + roof_new_error(error, "Invalid bit-depth (%u) is configured, only 8, 16, 24, 32 is currently supported", cfg->bit_depth); + } + + cfg->fan_projections = cfg->samples_per_rotation; + cfg->fan_bins = cfg->n_modules * cfg->channels_per_module; + + cfg->dataset_size = cfg->fan_projections * cfg->fan_bins * (cfg->bit_depth / 8); + cfg->n_streams = cfg->n_modules; + cfg->roof_mode = TRUE; + } + + if (network) { + roof_config_node_get(cfg->port, network, int, "port"); + roof_config_node_get(cfg->n_streams, network, int, "streams"); + + roof_config_node_get(cfg->payload_size, network, int, "payload_size"); + roof_config_node_get(cfg->header_size, network, int, "header_size"); + roof_config_node_get(cfg->max_packet_size, network, int, "max_packet_size"); + roof_config_node_get(cfg->dataset_size, network, int, "dataset_size"); + + if (!cfg->payload_size) { + roof_config_free(cfg); + roof_new_error(error, "Packet payload and header size must be set"); + } + + if ((cfg->header_size < sizeof(RoofPacketHeader))&&(!strncmp(cfg->protocol, "udp", 3))) { + roof_config_free(cfg); + roof_new_error(error, "The header with packet id (%lu bytes) is expected for un-ordered protocols", sizeof(RoofPacketHeader)); + } + + if (!cfg->dataset_size) + cfg->dataset_size = cfg->payload_size; + } + + if (simulation) { + roof_config_node_get(cfg->header_size, simulation, int, "header_size"); + + if (!cfg->payload_size) + cfg->payload_size = cfg->dataset_size; + } + + if (performance) { + roof_config_node_get(cfg->max_packets, performance, int, "packets_at_once"); + roof_config_node_get(cfg->buffer_size, performance, int, "buffer_size"); + roof_config_node_get(cfg->drop_buffers, performance, int, "drop_buffers"); + roof_config_node_get(cfg->latency_buffers, performance, int, "latency_buffers"); + roof_config_node_get(cfg->sockets_per_thread, performance, int, "sockets_per_thread"); + } + + + // Check configuration consistency + guint fragments_per_dataset = cfg->dataset_size / cfg->payload_size; + guint fragments_per_stream = fragments_per_dataset / cfg->n_streams; + + // Dataset should be split in an integer number of network packets (we don't expect data from different datasets in one packet at the moment) + if ((cfg->dataset_size % cfg->payload_size)||(fragments_per_dataset%cfg->n_streams)) { + roof_config_free(cfg); + roof_new_error(error, "Inconsistent ROOF configuration: dataset_size=%u, packet_size=%u, data_streams=%u", cfg->dataset_size, cfg->payload_size, cfg->n_streams); + } + + // Packet should contain an integer number of complete projections (their parts provided by a single module) + if ((cfg->roof_mode)&&(cfg->payload_size % (cfg->channels_per_module * (cfg->bit_depth / 8)))) { + roof_config_free(cfg); + roof_new_error(error, "Inconsistent ROOF configuration: packet_size=%u, projection_size=%u (%u channels x %u bits)", cfg->payload_size, cfg->channels_per_module * (cfg->bit_depth / 8), cfg->channels_per_module, cfg->bit_depth); + } + + if (!cfg->max_packet_size) + cfg->max_packet_size = cfg->header_size + cfg->payload_size; + + if (hardware) { + if (cfg->n_modules != cfg->n_streams) { + roof_config_free(cfg); + roof_new_error(error, "Currently, number of ROOF modules (%u) is exepcted to be equal to number of independent data streams (%u)", cfg->n_modules, cfg->n_streams); + } + + if (cfg->dataset_size != (cfg->fan_projections * cfg->fan_bins * cfg->bit_depth / 8)) { + roof_config_free(cfg); + roof_new_error(error, "Specified dataset size (%u) does not match ROOF configuration (modules: %u, channels-per-module: %u, bit-depth: %u, samples-per-rotation: %u)", cfg->dataset_size, cfg->n_modules, cfg->channels_per_module, cfg->bit_depth, cfg->samples_per_rotation); + } + } + + if ((cfg->buffer_size * fragments_per_stream) < cfg->max_packets) { + cfg->max_packets = cfg->buffer_size * fragments_per_stream / 2; + } + + if (cfg->buffer_size < 4) { + cfg->drop_buffers = 0; + } else if (cfg->drop_buffers >= cfg->buffer_size) { + cfg->drop_buffers = cfg->buffer_size / 2; + } + + return cfg; +} |