Monte Carlo analysis for 5G

Detailed Description

This is an example of how to use 5G Monte-Carlo analysis capability with WinProp API. The full example is distributed with the installation.
#include <stdio.h>
#include <string>
#include <iostream>
#include <cstring>

#include "monte_carlo_5g.h"

#ifndef API_DATA_FOLDER
#define API_DATA_FOLDER "../../api/winprop/data/"
#endif // !API_DATA_FOLDER
#define SERVICES_5G 7
#define MAX_CELLS 3

int main(int argc, char** argv)
{

	// ------ General and Propagation and Network Planing Parameters ------ //
	WinProp_ParaMain    GeneralParameters;
	WinProp_Pattern     WinPropPattern;
	WinProp_Antenna     Antenna[MAX_CELLS];
	WinProp_Carrier     Carrier[MAX_CELLS];
	
	// Propagation results 
	WinProp_Result      PropResults[MAX_CELLS];

	int                 ProjectHandle = 0, Error = 0;
	int                 ParaValueInt = 0;       // to set int parameters 
	double              ParaValueDouble = 0.;   // to set double  parameters 

	// Prediction heights 
	int                 NrPredictionHeights = 1;
	double              PredictionHeights[1] = {1.5};

	// ---------------------- Initialisation of parameters ---------------- //
	WinProp_Structure_Init_ParameterMain(&GeneralParameters);
	WinProp_Structure_Init_Pattern(&WinPropPattern);
	for (int cell = 0; cell < MAX_CELLS; cell++)
	{
		WinProp_Structure_Init_Antenna(&Antenna[cell]);
		WinProp_Structure_Init_Carrier(&Carrier[cell]);
		WinProp_Structure_Init_Result(&PropResults[cell]);
	}

	// ---------------- Definition of parameters -------------------------- //
	// Definition of scenario. 
	const char* my_odb_database = API_DATA_FOLDER "outdoor/Frankfurt"; // name of .odb database without extensions 
	GeneralParameters.ScenarioMode = SCENARIOMODE_URBAN; // Urban prediction
	GeneralParameters.PredictionModelUrban = PREDMODEL_UDP; // Use Dominant Path Model

	// Definition of prediction area.
	GeneralParameters.UrbanLowerLeftX = 475080.0;
	GeneralParameters.UrbanLowerLeftY = 551860.0;
	GeneralParameters.UrbanUpperRightX = 476840.0;
	GeneralParameters.UrbanUpperRightY = 553200.0;

	// Copy coordinates to prediction area of second model (not yet used).
	GeneralParameters.RuralLowerLeftX = GeneralParameters.UrbanLowerLeftX;
	GeneralParameters.RuralLowerLeftY = GeneralParameters.UrbanLowerLeftY;
	GeneralParameters.RuralUpperRightX = GeneralParameters.UrbanUpperRightX;
	GeneralParameters.RuralUpperRightY = GeneralParameters.UrbanUpperRightY;

	// Size of matrix with results.
	GeneralParameters.Resolution = 10.0;                       // Resolution in meter
	GeneralParameters.NrLayers = NrPredictionHeights;          // Number of prediction heights
	GeneralParameters.PredictionHeights = PredictionHeights;   // Prediction height in meter
	

	// Building vector data and topography.
	GeneralParameters.BuildingsMode = BUILDINGSMODE_BINARY; // load a .odb database 
	sprintf(GeneralParameters.BuildingsName, "%s", my_odb_database); 

	// Antenna patten (will be used after the propagation analysis to mask the omni-propagation
	// predictions)
	WinPropPattern.Mode = PATTERN_MODE_FILE;   // Load pattern from file
	sprintf(WinPropPattern.Filename, "%s", API_DATA_FOLDER "antennas/Beams2Control4Data.ffe"); // Pattern file
	

	// -------------------- Network Planning Project ----------------------- //
	// Create a new network planning project based on 5G FDD air interface                           
	if (Error == 0)
	{
		Error = WinProp_Net_Project_Open(&ProjectHandle, NET_AIRINTERFACE_5G_FDD_GENERIC, NULL);
	}
	
	// Set a name for the project.
	if (Error == 0)
	{
		char ProjectName[200];
		sprintf(ProjectName, "%s", "MonteCarlo-5G");
		Error = WinProp_Net_Project_Para_Set(ProjectHandle, NET_PARA_PROJECTNAME, NULL, NULL, ProjectName);
	}

	// --------------------------  Carriers  -------------------------------- //
	// Add 3 carriers in n1 band.
	if (Error == 0)
	{
		double FrequencyUL = 0.;
		double FrequencyDL = 0.;
		double CarrierSeparation = 20.;
		for (int CurrentCarrier = 0; CurrentCarrier < 3; CurrentCarrier++)
		{
			FrequencyUL = 1930.0 + (CurrentCarrier * CarrierSeparation);
			FrequencyDL = 2120.0 + (CurrentCarrier * CarrierSeparation);
			Error = WinProp_Net_Carrier_Add(ProjectHandle, CurrentCarrier + 1);

			Error = WinProp_Net_Carrier_Para_Set(
				ProjectHandle, CurrentCarrier + 1, NET_PARA_CARRIER_FREQ_DL, &FrequencyDL, NULL, NULL);
			Error = WinProp_Net_Carrier_Para_Set(
				ProjectHandle, CurrentCarrier + 1, NET_PARA_CARRIER_FREQ_UL, &FrequencyUL, NULL, NULL);
		}
	}

	// Check if number of carriers is correct. 
	if (Error == 0)
	{
		int NrCarriers = 0;
		Error = WinProp_Net_Project_Para_Get(ProjectHandle, NET_PARA_CARRIERS, NULL, &NrCarriers, NULL);
		if (Error == 0)
		{
			if (NrCarriers != 3)
				Error = 1;
		}
	}

	// --------------------------  antennas  -------------------------------- //
	// Position and configuration of the antennas (cells).
	char AntennaName[MAX_CELLS][500] = { "Ant 3", "Ant 2", "Ant 1" };
	double SiteX = 475500.;
	double SiteY = 552897.;
	double SiteZ = 12.;
	double Power = 40.;  // Power in dBm
	double Azmith[MAX_CELLS] = { 240., 120., 0. };

	// same frequencies in MHz as defined for the carriers
	double Frequency[MAX_CELLS] = { 2160., 2140., 2120. };

	double Downtilt = 0.;
	int AntennaCarrier[MAX_CELLS] = { 3, 2, 1 };

    if (Error == 0)
    {
		for (int cell = 0; cell < MAX_CELLS; cell++)
		{	
			// Set antenna properties now.
			AntennaPropertiesSet(
				&Antenna[cell], SiteX, SiteY, SiteZ, Frequency[cell], AntennaName[cell],
				Power, WINPROP_POWER_OUTPUT, WINPROP_MODEL_DPM, Azmith[cell], Downtilt, cell + 1);

			Carrier[cell].CarrierID = AntennaCarrier[cell];
			// Define two data beams for each cell (network planning analysis)
			Carrier[cell].NrAntennaBeams = 2; 
			CarrierPropertiesSet(&Antenna[cell], &Carrier[cell]);
		}
	}
	
	// -------------------------  Transmission modes  --------------------------- //
	// Define some (only 7) of the 5G transmission modes
	if (Error == 0)
	{
		const char*         TransmissionMode_Name[SERVICES_5G] = { 
			                                 "00 - QPSK - R 120",
											 "04 - QPSK - R 602",
											 "08 - 16 QAM - R 553",
											 "12 - 64 QAM - R 517",
											 "18 - 64 QAM - R 822",
											 "21 - 256 QAM - R 711",
											 "27 - 256 QAM - R 948" };
		double              TransmissionMode_Bitrate[SERVICES_5G] = { 0, 0, 0, 0, 0, 0, 0 };
		double              TransmissionMode_SNIR[SERVICES_5G] = { -3.0, 2, 7.9, 13.0, 17.5, 22.0, 27.0 };
		int                 TransmissionMode_Coderate_K[SERVICES_5G] = { 120, 602, 553, 517, 822, 711, 948 };
		int                 TransmissionMode_Coderate_N[SERVICES_5G] = { 1024, 1024, 1024, 1024, 1024, 1024, 1024 };
		int                 TransmissionMode_MCS[SERVICES_5G] = { 2, 2, 4, 6, 6, 8, 8 };
		double              TransmissionMode_Backoff[SERVICES_5G] = { 0.0, 0.0, 3.0, 3.0, 3.0, 3.0, 3.0 };
		int                 TransmissionMode_Resource_Blocks_DL[SERVICES_5G] = { 1, 1, 1, 1, 1, 1, 1 };
		int                 TransmissionMode_Resource_Blocks_UL[SERVICES_5G] = { 1, 1, 1, 1, 1, 1, 1 };
		double              TransmissionMode_Overhead_Ratio_DL[SERVICES_5G] = { 14., 14., 14., 14., 14., 14., 14. };
		double              TransmissionMode_Overhead_Ratio_UL[SERVICES_5G] = { 8., 8., 8., 8., 8., 8., 8. };

		for (int CurrentService = 0; CurrentService < SERVICES_5G; CurrentService++)
		{

            // Add new service. 
            char ServiceName[100];
			sprintf(ServiceName, "%s", TransmissionMode_Name[CurrentService]);
			Error = WinProp_Net_TransmissionMode_Add(ProjectHandle, ServiceName, CurrentService);

			// Set position/priority.
			ParaValueInt = SERVICES_5G - CurrentService;
			Error = WinProp_Net_TransmissionMode_Para_Set(
				ProjectHandle, CurrentService, NET_PARA_TRANS_MODE_POSITION, NULL, &ParaValueInt, NULL);

			// Set bitrate.
			ParaValueDouble = TransmissionMode_Bitrate[CurrentService];
			Error = WinProp_Net_TransmissionMode_Para_Set(
				ProjectHandle, CurrentService, NET_PARA_TRANS_MODE_BITRATE_DL, &ParaValueDouble, NULL, NULL);
			Error = WinProp_Net_TransmissionMode_Para_Set(
				ProjectHandle, CurrentService, NET_PARA_TRANS_MODE_BITRATE_UL, &ParaValueDouble, NULL, NULL);

			// Set code rate.
			ParaValueInt = TransmissionMode_Coderate_K[CurrentService];
			Error = WinProp_Net_TransmissionMode_Para_Set(
				ProjectHandle, CurrentService, NET_PARA_TRANS_MODE_CODERATE_K_DL, NULL, &ParaValueInt, NULL);
			Error = WinProp_Net_TransmissionMode_Para_Set(
				ProjectHandle, CurrentService, NET_PARA_TRANS_MODE_CODERATE_K_UL, NULL, &ParaValueInt, NULL);

			ParaValueInt = TransmissionMode_Coderate_N[CurrentService];
			Error = WinProp_Net_TransmissionMode_Para_Set(
				ProjectHandle, CurrentService, NET_PARA_TRANS_MODE_CODERATE_N_DL, NULL, &ParaValueInt, NULL);
			Error = WinProp_Net_TransmissionMode_Para_Set(
				ProjectHandle, CurrentService, NET_PARA_TRANS_MODE_CODERATE_N_UL, NULL, &ParaValueInt, NULL);

			// Set number of resource blocks.
			ParaValueInt = TransmissionMode_Resource_Blocks_DL[CurrentService];
			Error = WinProp_Net_TransmissionMode_Para_Set(
				ProjectHandle, CurrentService, NET_PARA_TRANS_MODE_RESOURCE_BLOCKS_DL, NULL, &ParaValueInt, NULL);
			ParaValueInt = TransmissionMode_Resource_Blocks_UL[CurrentService];
			Error = WinProp_Net_TransmissionMode_Para_Set(
				ProjectHandle, CurrentService, NET_PARA_TRANS_MODE_RESOURCE_BLOCKS_UL, NULL, &ParaValueInt, NULL);

			// Set overhead ratio. 
			ParaValueDouble = TransmissionMode_Overhead_Ratio_UL[CurrentService];
			Error = WinProp_Net_TransmissionMode_Para_Set(
				ProjectHandle, CurrentService, NET_PARA_TRANS_MODE_OVERHEAD_RATIO_UL, &ParaValueDouble, NULL, NULL);
			ParaValueDouble = TransmissionMode_Overhead_Ratio_DL[CurrentService];
			Error = WinProp_Net_TransmissionMode_Para_Set(
				ProjectHandle, CurrentService, NET_PARA_TRANS_MODE_OVERHEAD_RATIO_DL, &ParaValueDouble, NULL, NULL);

			// Set required SNIR.
			ParaValueDouble = TransmissionMode_SNIR[CurrentService];
			Error = WinProp_Net_TransmissionMode_Para_Set(
				ProjectHandle, CurrentService, NET_PARA_TRANS_MODE_SNIR_DL, &ParaValueDouble, NULL, NULL);
			Error = WinProp_Net_TransmissionMode_Para_Set(
				ProjectHandle, CurrentService, NET_PARA_TRANS_MODE_SNIR_UL, &ParaValueDouble, NULL, NULL);

			// Set modulation scheme.
			ParaValueInt = TransmissionMode_MCS[CurrentService];
			Error = WinProp_Net_TransmissionMode_Para_Set(
				ProjectHandle, CurrentService, NET_PARA_TRANS_MODE_MODULATION_UL, NULL, &ParaValueInt, NULL);
			Error = WinProp_Net_TransmissionMode_Para_Set(
				ProjectHandle, CurrentService, NET_PARA_TRANS_MODE_MODULATION_DL, NULL, &ParaValueInt, NULL);

			// Set power backoff. 
			ParaValueDouble = TransmissionMode_Backoff[CurrentService];
			Error = WinProp_Net_TransmissionMode_Para_Set(
				ProjectHandle, CurrentService, NET_PARA_TRANS_MODE_POWER_BACKOFF_UL, &ParaValueDouble, NULL, NULL);
			Error = WinProp_Net_TransmissionMode_Para_Set(
				ProjectHandle, CurrentService, NET_PARA_TRANS_MODE_POWER_BACKOFF_DL, &ParaValueDouble, NULL, NULL);

			// Get bitrate in kBit/s for current transmission mode. Bitrate is automatically computed based
			// on parameters previously set and general air interface parameters.
			Error = WinProp_Net_TransmissionMode_Para_Get(
				ProjectHandle, CurrentService, NET_PARA_TRANS_MODE_BITRATE_DL, &ParaValueDouble, NULL, NULL);
			TransmissionMode_Bitrate[CurrentService] = ParaValueDouble;
		}
	}

	// Check if number of services is correct.
	if (Error == 0)
	{
		int NrServices = 0;
		Error = WinProp_Net_Project_Para_Get(ProjectHandle, NET_PARA_SERVICES, NULL, &NrServices, NULL);
		if (Error == 0)
		{
			if (NrServices != SERVICES_5G)
				Error = 1;
		}
	}

	// --------------------------  Project parameters  -------------------------- //
	// Set MS maximum power 
	if (Error == 0)
	{
		ParaValueDouble = 23.; // Power in dBm
		Error = WinProp_Net_Project_Para_Set(ProjectHandle, NET_PARA_MS_MAX_TX_POWER, &ParaValueDouble, NULL, NULL);
	}

	// Set resolution for result matrix.
	if (Error == 0)
	{
		ParaValueDouble = GeneralParameters.Resolution;
		Error = WinProp_Net_Project_Para_Set(ProjectHandle, NET_PARA_RESOLUTION, &ParaValueDouble, NULL, NULL);
	}
	// set the simulation area
	if (Error == 0)
	{
		ParaValueDouble = GeneralParameters.UrbanLowerLeftX;
		Error = WinProp_Net_Project_Para_Set(ProjectHandle, NET_PARA_AREA_LL_X, &ParaValueDouble, NULL, NULL);
	}
	if (Error == 0)
	{
		ParaValueDouble = GeneralParameters.UrbanLowerLeftY;
		Error = WinProp_Net_Project_Para_Set(ProjectHandle, NET_PARA_AREA_LL_Y, &ParaValueDouble, NULL, NULL);
	}
	if (Error == 0)
	{
		ParaValueDouble = GeneralParameters.UrbanUpperRightX;
		Error = WinProp_Net_Project_Para_Set(ProjectHandle, NET_PARA_AREA_UR_X, &ParaValueDouble, NULL, NULL);
	}
	if (Error == 0)
	{
		ParaValueDouble = GeneralParameters.UrbanUpperRightY;
		Error = WinProp_Net_Project_Para_Set(ProjectHandle, NET_PARA_AREA_UR_Y, &ParaValueDouble, NULL, NULL);
	}

	// Set paths for additional output in WinProp file format.
	if (Error == 0)
	{
		Error = WinProp_Net_Project_Para_Set(ProjectHandle, NET_PARA_OUTPUT_WINPROP, NULL, NULL, API_DATA_FOLDER "output/");
	}

	// ------------------------  Cell assignment   ------------------------------ //
	
	// The default cell assignment for the selected air interface (i.e. NET_AIRINTERFACE_5G_FDD_GENERIC)
	//  is highest power for all carries, definition of min. required SNIR and power  

	// Set Min required SNIR
	if (Error == 0)
	{
		ParaValueDouble = -3.;
		Error = WinProp_Net_Project_Para_Set(ProjectHandle, NET_PARA_CELL_ASSIGNMENT_MIN_REQ_SNIR, &ParaValueDouble, NULL, NULL);
	}
	
	// Set Min required power
	if (Error == 0)
	{
		ParaValueDouble = -95.;
		Error = WinProp_Net_Project_Para_Set(ProjectHandle, NET_PARA_CELL_ASSIGNMENT_MIN_REQ_POWER, &ParaValueDouble, NULL, NULL);
	}

	// -------------------  numerology and  masking mode  ----------------------- //
	// Activate antenna masking for network planning results (propagation results with omni-pattern)
	if (Error == 0)
	{
		ParaValueInt = 1;
		Error = WinProp_Net_Project_Para_Set(ProjectHandle, NET_PARA_ANTENNA_MASKING_MODE, NULL, &ParaValueInt, NULL);
	}

	// Set numerology and bandwidth
	if (Error == 0)
	{
		ParaValueInt = NET_PARA_5G_NUMEROLOGY0_20MHZ_FR1;
		Error = WinProp_Net_Project_Para_Set(ProjectHandle, NET_PARA_BANDWIDTH_MODE_5G, NULL, &ParaValueInt, NULL);
	}

	// -----------------------  OFDM parameters  -------------------------------- //
	// Set control sub-carriers 
	if (Error == 0)
	{
		ParaValueInt = 1000;
		Error = WinProp_Net_Project_Para_Set(ProjectHandle, NET_PARA_SUBCARRIERS_CONTROL, NULL, &ParaValueInt, NULL);
	}

	// Set reference sub-carriers 
	if (Error == 0)
	{
		ParaValueInt = 200;
		Error = WinProp_Net_Project_Para_Set(ProjectHandle, NET_PARA_SUBCARRIERS_REFERENCE, NULL, &ParaValueInt, NULL);
	}
	
	// Set Guard sub-carriers
	if (Error == 0)
	{
		ParaValueInt = 31;
		Error = WinProp_Net_Project_Para_Set(ProjectHandle, NET_PARA_SUBCARRIERS_GUARD, NULL, &ParaValueInt, NULL);
	}

	// Set data symbols 
	if (Error == 0)
	{
		ParaValueInt = 10;
		Error = WinProp_Net_Project_Para_Set(ProjectHandle, NET_PARA_SYMBOLS_DATA, nullptr, &ParaValueInt, nullptr);
	}

	// Set control symbols 
	if (Error == 0)
	{
		ParaValueInt = 1;
		Error = WinProp_Net_Project_Para_Set(ProjectHandle, NET_PARA_SYMBOLS_CONTROL, NULL, &ParaValueInt, NULL);
	}
	
	// Set reference symbols
	if (Error == 0)
	{
		ParaValueInt = 3;
		Error = WinProp_Net_Project_Para_Set(ProjectHandle, NET_PARA_SYMBOLS_REFERENCE, NULL, &ParaValueInt, NULL);
	}

	// Set FFT order 
	if (Error == 0)
	{
		ParaValueInt = 2048;
		Error = WinProp_Net_Project_Para_Set(ProjectHandle, NET_PARA_FFT_SIZE, NULL, &ParaValueInt, NULL);
	}
	
	// ----------------------  Monte Carlo Parameters  -------------------------- //
	// Set traffic mode to Monte Carlo
	if (Error == 0)
	{
		ParaValueInt = NET_TRAFFIC_MODE_STATIC_MONTE_CARLO;
		Error = WinProp_Net_Project_Para_Set(ProjectHandle, NET_PARA_TRAFFIC_MODE, nullptr, &ParaValueInt, nullptr);
	}

	// Set Monte Carlo max loops
	if (Error == 0)
	{
		ParaValueInt = 5;
		Error = WinProp_Net_Project_Para_Set(ProjectHandle, NET_PARA_TRAFFIC_MONTE_CARLO_LOOPS, nullptr, &ParaValueInt, nullptr);
	}

	// Set users distributed per square kilometer
	if (Error == 0)
	{
		ParaValueInt = NET_APPLICATION_AREA_MODE_KILOMETER;
		Error = WinProp_Net_Project_Para_Set(ProjectHandle, NET_PARA_APPLICATION_AREA_MODE, nullptr, &ParaValueInt, nullptr);
	}
	
	// Consider priority of an application in a cell
	if (Error == 0)
	{
		ParaValueInt = 1;
		Error = WinProp_Net_Project_Para_Set(ProjectHandle, NET_PARA_TRAFFIC_CELL_SELECT_CONSIDER_PRIO, nullptr, &ParaValueInt, nullptr);
	}

	//  Number of iterations for traffic simulations
	if (Error == 0)
	{
		ParaValueInt = 20;
		Error = WinProp_Net_Project_Para_Set(ProjectHandle, NET_PARA_TRAFFIC_MAX_ITERATIONS, nullptr, &ParaValueInt, nullptr);
	}

	
	// Add clutter from the database file
	if (Error == 0)
	{
		char ClutterTraffic[200];
		sprintf(ClutterTraffic, "%s.odb", my_odb_database);
		Error = WinProp_Net_Clutter_Set(ProjectHandle, CLUTTERMODE_FILE, ClutterTraffic, false, "", nullptr);
	}

	// Add applications
	if (Error == 0)
	{
		int   ApplID = 0;
		int   ApplPriority = 0;
		float ApplActivity = 1.f;
		int   ApplTrafficMode = NET_APPLICATION_TRAFFIC_MODE_OFFERED_TRAFFIC;
		const char* ApplName = "Browsing";
		Error = WinProp_Net_Application_Add(ProjectHandle, ApplID, ApplPriority, ApplActivity, ApplTrafficMode, ApplName);

		// add traffic clutter classes to the application
		if (Error == 0)
		{
			int       ClutterClassID = 1;        // ID of clutter class  1 = corresponds to OUTDOOR clutter as defined in the data base             
			float     ArrivalRate    = 0.f;      // Arrival rate in 1/(km^2/s) (not relevant for NET_APPLICATION_TRAFFIC_MODE_OFFERED_TRAFFIC)       
			float     HoldTime       = 0.f;      // Hold Time in [sec] (not relevant for NET_APPLICATION_TRAFFIC_MODE_OFFERED_TRAFFIC)  
			float     OfferedTraffic = 50.f;     // Offered Traffic in Erl/km^2           
			WinProp_Net_Application_ClutterClass_Link(ProjectHandle, ApplID, ClutterClassID, ArrivalRate, HoldTime, OfferedTraffic);
		}

		// add transmission modes to the application
		if (Error == 0)
		{
			int TransModeID = 5; // corresponds to "18 - 64 QAM - R 822" as defined above.
			Error = WinProp_Net_Application_TransmissionMode_Link(ProjectHandle, ApplID, TransModeID, 0);
		}
	}

	// Callback functions.
    WinProp_Callback Callback;
    WinProp_Structure_Init_Callback(&Callback);
	Callback.Percentage = CallbackProgress;
	Callback.Message = CallbackMessage;
	Callback.Error = CallbackError;

	//--------------------------------------------------------------------------
    // Compute wave propagation for three cells
    //--------------------------------------------------------------------------
	for (int cell = 0; cell < MAX_CELLS; cell++)
	{
		/*--------------------- Compute outdoor prediction ------------------------*/
		Error = OutdoorPlugIn_ComputePrediction(
			&Antenna[cell], &GeneralParameters, NULL, 0, NULL, NULL, NULL, NULL, &Callback, &PropResults[cell], NULL, NULL, NULL);

		/*---------------------- Do something with results ------------------------*/
		if (Error == 0) 
		{
			char NameForOutput[200];
			sprintf(NameForOutput, "%s%s%s%s", API_DATA_FOLDER, "output/", Antenna[cell].Name,".txt");

			write_ascii(&PropResults[cell], NameForOutput);
		}
		else 
		{
			/* Error during prediction. Print error message. */
			CallbackError(GeneralParameters.ErrorMessageMain, Error);
		}
	}

	// Add all propagation maps which have been computed.
	// Set the antenna pattern for masking the already calculated omni-propagation results
	if (Error == 0)
	{
		int MapIndex[MAX_CELLS];
		for (int Count = 0; Count < MAX_CELLS; Count++)
		{
			if (Error == 0)
			{
				// Masking mode -->  set antenna pattens for network planning results 
				Antenna[Count].Pattern = &WinPropPattern;

				Error = WinProp_Net_PropagationMap_Add(
					ProjectHandle, &MapIndex[Count], &Antenna[Count], &PropResults[Count]);
			}
		}
	}

	// Desired network planning results in WinProp result format
	Error = WinProp_Net_Project_Result_Switch(ProjectHandle, NET_RESULT_BEST_SERVER, 1);
	Error = WinProp_Net_Project_Result_Switch(ProjectHandle, NET_RESULT_MAX_DATARATE, 1);
	Error = WinProp_Net_Project_Result_Switch(ProjectHandle, NET_RESULT_MAX_REC_POWER, 1);
	Error = WinProp_Net_Project_Result_Switch(ProjectHandle, NET_RESULT_REC_POWER, 1);
	Error = WinProp_Net_Project_Result_Switch(ProjectHandle, NET_RESULT_SNIR, 1);
	Error = WinProp_Net_Project_Result_Switch(ProjectHandle, NET_RESULT_TRAFFIC_OFFERED, 1);
	Error = WinProp_Net_Project_Result_Switch(ProjectHandle, NET_RESULT_TRAFFIC_SERVED, 1);
	Error = WinProp_Net_Project_Result_Switch(ProjectHandle, NET_RESULT_TRAFFIC_BLOCKED, 1);
	Error = WinProp_Net_Project_Result_Switch(ProjectHandle, NET_RESULT_TRAFFIC_STATE, 1);

	//--------------------------------------------------------------------------
    //  Compute network planning results
    //--------------------------------------------------------------------------
	if (Error == 0)
	{
		char HeightString[200];
		sprintf(HeightString, "%s", "");

		// Generate string with height values, e.g. a string like "1.5 2.5 3.5". 
		for (int Height = 0; Height < NrPredictionHeights; Height++)
		{
			// Add current height to string.
			char thisHeightStr[50];
			sprintf(thisHeightStr, "%.2f ", PredictionHeights[Height]);
			strcat(HeightString, thisHeightStr);
		}

		// Send heights to WinProp API.
		Error = WinProp_Net_Project_Para_Set(ProjectHandle, NET_PARA_HEIGHT_MULTIPLE, NULL, NULL, HeightString);

		// Start network computation.
		if (Error == 0)
		{
			Error = WinProp_Net_Project_Compute(ProjectHandle, &Callback);
		}
	}

	// ------------------------------------------------------------------------
	// Retrieve Monte-Carlo statistics  
	// ------------------------------------------------------------------------
	if (Error == 0)
	{
		WinProp_ResultMonteCarlo* ResultMonteCarlo = NULL;
		Error = WinProp_Net_NetworkMonteCarloStats_Get(ProjectHandle, &ResultMonteCarlo);

		if (Error == 0)
		{
			char NameForOutput[200];
			sprintf(NameForOutput, "%s%s", API_DATA_FOLDER, "output/MonteCarloStatistics.txt");
			write_ascii_monte_carlo_statistic(ResultMonteCarlo, NameForOutput);

		}
	}

	// ------------------------------------------------------------------------
	// Retrieve some other results 
	// ------------------------------------------------------------------------
	// As an example: retrieve max. throughput (kbps) per pixel.
	if (Error == 0)
	{
		WinProp_Result* MaxThroughput = NULL;
		Error = WinProp_Net_NetworkMap_Get(ProjectHandle, -1, NET_RESULT_MAX_THROUGHPUT, &MaxThroughput);

		// Write max. throughput result to ASCII file.
		if (Error == 0)
		{
			char NameForOutput[200];
			sprintf(NameForOutput, "%s%s", API_DATA_FOLDER, "output/Max Throughput DownLink.txt");
			write_ascii(MaxThroughput, NameForOutput);
		}

	}

	// As another example: retrieve max. received power (dBm) per pixel.
	if (Error == 0)
	{
		WinProp_Result* MaxPower = NULL;
		Error = WinProp_Net_NetworkMap_Get(ProjectHandle, -1, NET_RESULT_MAX_REC_POWER, &MaxPower);

		// Write max. throughput result to ASCII file.
		if (Error == 0)
		{
			char NameForOutput[200];
			sprintf(NameForOutput, "%s%s", API_DATA_FOLDER, "output/Max Received Power.txt");
			write_ascii(MaxPower, NameForOutput);
		}

	}
	

	// As another example: retrieve SNIR (dB) per pixel.
	if (Error == 0)
	{
		WinProp_Result* MaxSNIR = NULL;
		Error = WinProp_Net_NetworkMap_Get(ProjectHandle, -1, NET_RESULT_SNIR, &MaxSNIR);

		// Write max. throughput result to ASCII file.
		if (Error == 0)
		{
			char NameForOutput[200];
			sprintf(NameForOutput, "%s%s", API_DATA_FOLDER, "output/Max SNIR.txt");
			write_ascii(MaxSNIR, NameForOutput);
		}
	}

	// ------------------------------------------------------------------------
	// Free memory
	// ------------------------------------------------------------------------
	for (int Count = 0; Count < MAX_CELLS; Count++)
	{
		// Free propagation results.
		WinProp_FreeResult(&PropResults[Count]);
	}

	// Close network project.
	Error = WinProp_Net_Project_Close(ProjectHandle);

	return 0;
}

int _STD_CALL CallbackMessage(const char * Text)
{
	if (Text == nullptr)
		return 0;

	std::cout << "\n" << Text;

	return(0);
}

int _STD_CALL CallbackError(const char * Text, int Error)
{
	if (Text == nullptr)
		return 0;

	std::cout << "\n";

#ifdef __LINUX
	std::cout << "\033[31m" << "Error (" << Error << "): "; // highlight error in red color
#else
	HANDLE hConsole = GetStdHandle(STD_OUTPUT_HANDLE);
	SetConsoleTextAttribute(hConsole, FOREGROUND_RED);
	std::cout << "Error (" << Error << "): ";
#endif // __LINUX
	std::cout << Text;

#ifdef __LINUX
	std::cout << "\033[0m"; // highlight error in red color
#else
	SetConsoleTextAttribute(hConsole, FOREGROUND_RED | FOREGROUND_BLUE | FOREGROUND_GREEN);
#endif // __LINUX

	return 0;
}

int _STD_CALL CallbackProgress(int value, const char* text)
{
	char Line[200];

	sprintf(Line, "\n%d%% %s", value, text);
	std::cout << Line;

	return(0);
}

// Helper functions
void write_ascii(const WinProp_Result *Resultmatrix, const char* Filename) {
	
	if (Resultmatrix)
	{
		FILE* OutputFile = fopen(Filename, "w");
		if (OutputFile)
		{
			/* Loop through WinPropall pixels. */
			for (int x = 0; x < Resultmatrix->Columns; x++)
			{
				for (int y = 0; y < Resultmatrix->Lines; y++)
				{
					/* Compute real coordinates. */
					double Coordinate_X = Resultmatrix->LowerLeftX + ((double)x + 0.5) * Resultmatrix->Resolution;
					double Coordinate_Y = Resultmatrix->LowerLeftY + ((double)y + 0.5) * Resultmatrix->Resolution;

					/* Check if pixel was computed or not */
					if (Resultmatrix->Matrix[0][x][y] > -1000)
					{
						fprintf(OutputFile, "%.5f\t%.5f\t%.4f\n", Coordinate_X, Coordinate_Y, Resultmatrix->Matrix[0][x][y]);
					}
					else
					{
						fprintf(OutputFile, "%.5f\t%.5f\t%s\n", Coordinate_X, Coordinate_Y, "N.C.");
					}
				}
			}

			/* Close file. */
			fclose(OutputFile);
		}
		else
		{
			printf("\nCould not open the File: %s for writing.\n", Filename);
		}
	}
}

void AntennaPropertiesSet(
	WinProp_Antenna *Antenna,
	double CoordinateX,
	double CoordinateY,
	double Height,
	double Frequency,
	const char *Name,
	double Power,
	int PowerMode,
	int Model,
	double Azimuth,
	double Downtilt,
	int Id)
{
	Antenna->Longitude_X = CoordinateX;
	Antenna->Latitude_Y = CoordinateY;
	Antenna->Height = Height;
	Antenna->Power = Power;
	Antenna->PowerMode = PowerMode;
	Antenna->Frequency = Frequency;
	sprintf(Antenna->Name, "%s", Name);
	Antenna->Model = Model;
	Antenna->Azimuth = Azimuth;
    Antenna->Downtilt = Downtilt;
	Antenna->Id = Id;

}

void CarrierPropertiesSet(WinProp_Antenna *Antenna, const WinProp_Carrier *Carrier) {

	Antenna->Carriers.CarrierID = Carrier->CarrierID;
	Antenna->Carriers.SystemID = Carrier->SystemID;
	Antenna->Carriers.CellLoad = Carrier->CellLoad;
	Antenna->Carriers.MimoID = Carrier->MimoID;

	Antenna->Carriers.NoiseRiseUL = Carrier->NoiseRiseUL;
	Antenna->Carriers.PowerBackoffPilotDL = Carrier->PowerBackoffPilotDL;

	// 5G Carrier parameters
	Antenna->Carriers.Numerology = Carrier->Numerology;
	Antenna->Carriers.SymbolsPerSlot = Carrier->SymbolsPerSlot;
	Antenna->Carriers.NrAntennaBeams = Carrier->NrAntennaBeams;
	Antenna->Carriers.BeamGainCtrlServer = Carrier->BeamGainCtrlServer;
	Antenna->Carriers.BeamGainCtrlInterferer = Carrier->BeamGainCtrlInterferer;
	Antenna->Carriers.BeamGainDataServer = Carrier->BeamGainDataServer;
	Antenna->Carriers.BeamGainDataInterferer = Carrier->BeamGainDataInterferer;
	Antenna->Carriers.TDD_Slots_DL = Carrier->TDD_Slots_DL;
	Antenna->Carriers.TDD_Slots_UL = Carrier->TDD_Slots_UL;
	Antenna->Carriers.TDD_Slots_Flex = Carrier->TDD_Slots_Flex;
}

/**
 * Writes Monte Carlo statistics in an ASCII file 
 *
 * @param [in,out]	ResultMonteCarlo	If non-null, the result to be written.
 * @param [in,out]	Filename			If non-null, filename of the file.
 */

void write_ascii_monte_carlo_statistic(
	const WinProp_ResultMonteCarlo* ResultMonteCarlo,
	const char* Filename)
{
	if (ResultMonteCarlo == NULL)
		return;

	FILE* Outfile = fopen(Filename, "w+");

	if (Outfile)
	{
		char         sepChar = ' ';
		char         format1[100];
		char         format2[100];
		char         text[400];

		fprintf(Outfile, "%s", "-------------------------------------------------------------------\n");
		fprintf(Outfile, "Number of evaluated snapshots:%c%d\n", sepChar, ResultMonteCarlo->nbrSnapshots);
		fprintf(Outfile, "Number of evaluated cells:%c%d\n", sepChar, ResultMonteCarlo->nbrCells);
		fprintf(Outfile, "Number of evaluated applications:%c%d\n", sepChar, ResultMonteCarlo->nbrApplications);
		fprintf(Outfile, "%s", "-------------------------------------------------------------------\n");
		fprintf(Outfile, "%s", "\n");

		/* assign width of columns */
		sprintf(format1, "%%%ds", 15);
		sprintf(format2, "%%%ds", 14);

		/* loop over all cells (index -1 is for total) */
		for (int cell = -1; cell < (int)ResultMonteCarlo->nbrCells; cell++)
		{
			/* write header of table */
			fprintf(Outfile, "%s", "********************************\n");
			if (cell >= 0)
			{
				fprintf(Outfile, "***%cCell %d", sepChar, (cell + 1));
				fprintf(Outfile, "%s", "\n********************************");
			}
			else
			{
				sprintf(text, "***%cTotal simulation area", sepChar);
				fprintf(Outfile, "%-30s", text);
				fprintf(Outfile, "%s", "\n********************************");
			}

			/* Write snapshots */
			for (int snapshot = (int)ResultMonteCarlo->nbrSnapshots; snapshot >= 0; snapshot--)
			{
				if (snapshot == (int)ResultMonteCarlo->nbrSnapshots)
					sprintf(text, "%cAverage", sepChar);
				else
					sprintf(text, "%cSnapshot %d", sepChar, (snapshot + 1));
				if (snapshot == (int)ResultMonteCarlo->nbrSnapshots)
					fprintf(Outfile, format2, text);
				else
					fprintf(Outfile, format1, text);
			}
			fprintf(Outfile, "%s", "\n");


			/* write cell load (Tx Power Downlink) */
			if (cell >= 0)
			{
				fprintf(Outfile, "%-31s", "    Tx Power Load (Downlink):   ");
				for (int snapshot = (int)ResultMonteCarlo->nbrSnapshots; snapshot >= 0; snapshot--)
				{
					// average value of tx power 
					if (snapshot == (int)ResultMonteCarlo->nbrSnapshots)
					{
						float average = 0.f;
						for (unsigned int s = 0; s < ResultMonteCarlo->nbrSnapshots; s++)
						{
							average += ResultMonteCarlo->trxLoad[s][cell].LoadPowerDL;
						}

						if (ResultMonteCarlo->nbrSnapshots > 0)
						{
							average /= ResultMonteCarlo->nbrSnapshots;
						}
						sprintf(text, "%c%.1f%c%%", sepChar, average * 100.f, sepChar);
						fprintf(Outfile, format2, text);
					}
					else
					{
						// values for each snapshot
						sprintf(text, "%c%.1f%c%%", sepChar, ResultMonteCarlo->trxLoad[snapshot][cell].LoadPowerDL * 100.f, sepChar);
						fprintf(Outfile, format1, text);
					}
				}
				fprintf(Outfile, "%s", "\n");
			}

			/* write noise rise (uplink) */
			if (cell >= 0)
			{
				fprintf(Outfile, "%-33s", "    Noise Rise (Uplink):        ");
				for (int snapshot = (int)ResultMonteCarlo->nbrSnapshots; snapshot >= 0; snapshot--)
				{
					// average value of noise rise 
					if (snapshot == (int)ResultMonteCarlo->nbrSnapshots)
					{
						float average = 0.f;
						for (unsigned int s = 0; s < ResultMonteCarlo->nbrSnapshots; s++)
						{
							average += ResultMonteCarlo->trxLoad[s][cell].NoiseRiseUL;
						}

						if (ResultMonteCarlo->nbrSnapshots > 0)
						{
							average /= ResultMonteCarlo->nbrSnapshots;
						}
						sprintf(text, "%c%.1f%cdB", sepChar, average, sepChar);
						fprintf(Outfile, format2, text);
					}
					else
					{
						// values for each snapshot
						sprintf(text, "%c%.1f%cdB", sepChar, ResultMonteCarlo->trxLoad[snapshot][cell].NoiseRiseUL, sepChar);
						fprintf(Outfile, format1, text);
					}
				}
				fprintf(Outfile, "%s", "\n");
			}

			/* write resources load (downlink) */
			if (cell >= 0)
			{
				fprintf(Outfile, "%-32s", "    Resources Load (Downlink): ");
				for (int snapshot = (int)ResultMonteCarlo->nbrSnapshots; snapshot >= 0; snapshot--)
				{
					// average value of DL resources load
					if (snapshot == (int)ResultMonteCarlo->nbrSnapshots)
					{
						float average = 0.f;
						for (unsigned int s = 0; s < ResultMonteCarlo->nbrSnapshots; s++)
						{
							average += ResultMonteCarlo->trxLoad[s][cell].LoadResourcesDL;
						}

						if (ResultMonteCarlo->nbrSnapshots > 0)
						{
							average /= ResultMonteCarlo->nbrSnapshots;
						}
						sprintf(text, "%c%.1f%c%%", sepChar, average * 100.f, sepChar);
						fprintf(Outfile, format2, text);
					}
					else
					{
						// values for each snapshot
						sprintf(text, "%c%.1f%c%%", sepChar, ResultMonteCarlo->trxLoad[snapshot][cell].LoadResourcesDL * 100.f, sepChar);
						fprintf(Outfile, format1, text);
					}
				}
				fprintf(Outfile, "%s", "\n");
			}

			/* write resources load (downlink) */
			if (cell >= 0)
			{
				fprintf(Outfile, "%-32s", "    Resources Load (Uplink): ");
				for (int snapshot = (int)ResultMonteCarlo->nbrSnapshots; snapshot >= 0; snapshot--)
				{
					// average value of UL resources load
					if (snapshot == (int)ResultMonteCarlo->nbrSnapshots)
					{
						float average = 0.f;
						for (unsigned int s = 0; s < ResultMonteCarlo->nbrSnapshots; s++)
						{
							average += ResultMonteCarlo->trxLoad[s][cell].LoadResourcesUL;
						}

						if (ResultMonteCarlo->nbrSnapshots > 0)
						{
							average /= ResultMonteCarlo->nbrSnapshots;
						}
						sprintf(text, "%c%.1f%c%%", sepChar, average * 100.f, sepChar);
						fprintf(Outfile, format2, text);
					}
					else
					{
						// values for each snapshot
						sprintf(text, "%c%.1f%c%%", sepChar, ResultMonteCarlo->trxLoad[snapshot][cell].LoadResourcesUL * 100.f, sepChar);
						fprintf(Outfile, format1, text);
					}
				}
				fprintf(Outfile, "%s", "\n");
			}

			/* write throughput (downlink) */
			if (cell >= 0)
			{
				fprintf(Outfile, "%-33s", "    Throughput (Downlink):      ");
				for (int snapshot = ResultMonteCarlo->nbrSnapshots; snapshot >= 0; snapshot--)
				{
					float ThroughputDL = 0.f;
					if (snapshot == (int)ResultMonteCarlo->nbrSnapshots)
					{
						float average = 0.f;
						for (unsigned int s = 0; s < ResultMonteCarlo->nbrSnapshots; s++)
						{
							average += ResultMonteCarlo->trxLoad[s][cell].ThroughputUL;
						}
						if (ResultMonteCarlo->nbrSnapshots > 0)
						{
							average /= ResultMonteCarlo->nbrSnapshots;
						}
						// average throughput
						ThroughputDL = average;
					}
					else
					{
						// values for each snapshot
						ThroughputDL = ResultMonteCarlo->trxLoad[snapshot][cell].ThroughputUL;
					}

					// format throughput 
					if (ThroughputDL > 1e9)
						sprintf(text, "%c%.1f%cGB/s", sepChar, ThroughputDL / 1e9, sepChar);
					else if (ThroughputDL > 1e6)
						sprintf(text, "%c%.1f%cMB/s", sepChar, ThroughputDL / 1e6, sepChar);
					else if (ThroughputDL > 1e3)
						sprintf(text, "%c%.1f%ckB/s", sepChar, ThroughputDL / 1e3, sepChar);
					else
						sprintf(text, "%c%.1f%cB/s", sepChar, ThroughputDL, sepChar);

					if (snapshot == (int)ResultMonteCarlo->nbrSnapshots)
						fprintf(Outfile, format2, text);
					else
						fprintf(Outfile, format1, text);
				}
				fprintf(Outfile, "%s", "\n");
			}

			/* write throughput (uplink) */
			if (cell >= 0)
			{
				fprintf(Outfile, "%-33s", "    Throughput (uplink):      ");
				for (int snapshot = ResultMonteCarlo->nbrSnapshots; snapshot >= 0; snapshot--)
				{
					float ThroughputUL = 0.f;
					if (snapshot == (int)ResultMonteCarlo->nbrSnapshots)
					{
						float average = 0.f;
						for (unsigned int s = 0; s < ResultMonteCarlo->nbrSnapshots; s++)
						{
							average += ResultMonteCarlo->trxLoad[s][cell].ThroughputUL;
						}
						if (ResultMonteCarlo->nbrSnapshots > 0)
						{
							average /= ResultMonteCarlo->nbrSnapshots;
						}
						// average throughput
						ThroughputUL = average;
					}
					else
					{
						// values for each snapshot
						ThroughputUL = ResultMonteCarlo->trxLoad[snapshot][cell].ThroughputUL;
					}

					// format throughput 
					if (ThroughputUL > 1e9)
						sprintf(text, "%c%.1f%cGB/s", sepChar, ThroughputUL / 1e9, sepChar);
					else if (ThroughputUL > 1e6)
						sprintf(text, "%c%.1f%cMB/s", sepChar, ThroughputUL / 1e6, sepChar);
					else if (ThroughputUL > 1e3)
						sprintf(text, "%c%.1f%ckB/s", sepChar, ThroughputUL / 1e3, sepChar);
					else
						sprintf(text, "%c%.1f%cB/s", sepChar, ThroughputUL, sepChar);

					if (snapshot == (int)ResultMonteCarlo->nbrSnapshots)
						fprintf(Outfile, format2, text);
					else
						fprintf(Outfile, format1, text);
				}
				fprintf(Outfile, "%s", "\n");
			}

			/* write number of served, blocked, and not assigned mobiles for each application */
			for (unsigned int app = 0; app <= ResultMonteCarlo->nbrApplications; app++)
			{
				/* write name of application */
				if (app < ResultMonteCarlo->nbrApplications)
					sprintf(text, "%s %d", "\n Applications id: ", app);
				else
					sprintf(text, "%s", "\nAll Applications");

				fprintf(Outfile, "%-20s\n", text);

				for (int MobileType = 0; MobileType < 4; MobileType++)
				{
					// Mobiles served  = 0
					// Mobiles blocked(traffic) = 1
					// Mobiles blocked(quality) = 2
					// Mobiles not assigned = 3
					switch (MobileType)
					{
					case 0:
						fprintf(Outfile, "   %cMobiles served:           ", sepChar);
						break;
					case 1:
						fprintf(Outfile, "   %cMobiles blocked (traffic):", sepChar);
						break;
					case 2:
						fprintf(Outfile, "   %cMobiles blocked (quality):", sepChar);
						break;
					case 3:
						fprintf(Outfile, "   %cMobiles not assigned:     ", sepChar);
						break;
					default:
						break;
					}

					for (int snapshot = (int)ResultMonteCarlo->nbrSnapshots; snapshot >= 0; snapshot--)
					{
						float nbr = 0.f;

						// All Applications case 
						if (app == ResultMonteCarlo->nbrApplications)
						{
							for (unsigned int a = 0; a < ResultMonteCarlo->nbrApplications; a++)
							{
								// specific cell
								if (cell >= 0)
								{
									if (snapshot == (int)ResultMonteCarlo->nbrSnapshots)
									{
										// average number (over snapshots) for all apps  for a specific cell
										for (unsigned int s = 0; s < ResultMonteCarlo->nbrSnapshots; s++)
										{
											switch (MobileType)
											{
											case 0:
												nbr += ResultMonteCarlo->statisticCell[s][cell][a].nbrServed;
												break;
											case 1:
												nbr += ResultMonteCarlo->statisticCell[s][cell][a].nbrBlocked;
												break;
											case 2:
												nbr += ResultMonteCarlo->statisticCell[s][cell][a].nbrNotConnected;
												break;
											case 3:
												nbr += ResultMonteCarlo->statisticCell[s][cell][a].nbrNotAssigned;
												break;
											default:
												break;
											}
										}
									}
									else
									{
										// number for all apps for a specific cell and a specific snapshot
										switch (MobileType)
										{
										case 0:
											nbr += ResultMonteCarlo->statisticCell[snapshot][cell][a].nbrServed;
											break;
										case 1:
											nbr += ResultMonteCarlo->statisticCell[snapshot][cell][a].nbrBlocked;
											break;
										case 2:
											nbr += ResultMonteCarlo->statisticCell[snapshot][cell][a].nbrNotConnected;
											break;
										case 3:
											nbr += ResultMonteCarlo->statisticCell[snapshot][cell][a].nbrNotAssigned;
											break;
										default:
											break;
										}
									}

								}
								else // all cells
								{
									// average number (over snapshots) for all apps for all cells
									if (snapshot == (int)ResultMonteCarlo->nbrSnapshots)
									{
										for (unsigned int s = 0; s < ResultMonteCarlo->nbrSnapshots; s++)
										{
											switch (MobileType)
											{
											case 0:
												nbr += ResultMonteCarlo->statisticTotal[s][a].nbrServed;
												break;
											case 1:
												nbr += ResultMonteCarlo->statisticTotal[s][a].nbrBlocked;
												break;
											case 2:
												nbr += ResultMonteCarlo->statisticTotal[s][a].nbrNotConnected;
												break;
											case 3:
												nbr += ResultMonteCarlo->statisticTotal[s][a].nbrNotAssigned;
												break;
											default:
												break;
											}
										}
									}
									else
									{
										// number for all apps for all cells per snapshot
										switch (MobileType)
										{
										case 0:
											nbr += ResultMonteCarlo->statisticTotal[snapshot][a].nbrServed;
											break;
										case 1:
											nbr += ResultMonteCarlo->statisticTotal[snapshot][a].nbrBlocked;
											break;
										case 2:
											nbr += ResultMonteCarlo->statisticTotal[snapshot][a].nbrNotConnected;
											break;
										case 3:
											nbr += ResultMonteCarlo->statisticTotal[snapshot][a].nbrNotAssigned;
											break;
										default:
											break;
										}
									}
								}
							}
						}
						else // individual application case 
						{
							// specific cell
							if (cell >= 0)
							{
								// average number (over snapshots) for a specific apps and a specific cell
								if (snapshot == (int)ResultMonteCarlo->nbrSnapshots)
								{
									for (unsigned int s = 0; s < ResultMonteCarlo->nbrSnapshots; s++)
									{
										switch (MobileType)
										{
										case 0:
											nbr += ResultMonteCarlo->statisticCell[s][cell][app].nbrServed;
											break;
										case 1:
											nbr += ResultMonteCarlo->statisticCell[s][cell][app].nbrBlocked;
											break;
										case 2:
											nbr += ResultMonteCarlo->statisticCell[s][cell][app].nbrNotConnected;
											break;
										case 3:
											nbr += ResultMonteCarlo->statisticCell[s][cell][app].nbrNotAssigned;
											break;
										default:
											break;
										}
									}
								}
								else
								{
									// number for a specific app,  a specific cell, and a specific snapshot
									switch (MobileType)
									{
									case 0:
										nbr += ResultMonteCarlo->statisticCell[snapshot][cell][app].nbrServed;
										break;
									case 1:
										nbr += ResultMonteCarlo->statisticCell[snapshot][cell][app].nbrBlocked;
										break;
									case 2:
										nbr += ResultMonteCarlo->statisticCell[snapshot][cell][app].nbrNotConnected;
										break;
									case 3:
										nbr += ResultMonteCarlo->statisticCell[snapshot][cell][app].nbrNotAssigned;
										break;
									default:
										break;
									}
								}
							}
							else // all cells

								// average number (over snapshots) for a specific app and for all cells
								if (snapshot == (int)ResultMonteCarlo->nbrSnapshots)
								{
									for (unsigned int s = 0; s < ResultMonteCarlo->nbrSnapshots; s++)
									{
										switch (MobileType)
										{
										case 0:
											nbr += ResultMonteCarlo->statisticTotal[s][app].nbrServed;
											break;
										case 1:
											nbr += ResultMonteCarlo->statisticTotal[s][app].nbrBlocked;
											break;
										case 2:
											nbr += ResultMonteCarlo->statisticTotal[s][app].nbrNotConnected;
											break;
										case 3:
											nbr += ResultMonteCarlo->statisticTotal[s][app].nbrNotAssigned;
											break;
										default:
											break;
										}
									}
								}
								else
								{
									// number  for a specific app, a specif specific and for all cells
									switch (MobileType)
									{
									case 0:
										nbr += ResultMonteCarlo->statisticTotal[snapshot][app].nbrServed;
										break;
									case 1:
										nbr += ResultMonteCarlo->statisticTotal[snapshot][app].nbrBlocked;
										break;
									case 2:
										nbr += ResultMonteCarlo->statisticTotal[snapshot][app].nbrNotConnected;
										break;
									case 3:
										nbr += ResultMonteCarlo->statisticTotal[snapshot][app].nbrNotAssigned;
										break;
									default:
										break;
									}
								}
						}
						if (snapshot == (int)ResultMonteCarlo->nbrSnapshots)
						{
							double nbrDouble = ((double)nbr) / ((double)ResultMonteCarlo->nbrSnapshots);
							sprintf(text, "%c%.1lf", sepChar, nbrDouble);
							fprintf(Outfile, format2, text);
						}
						else
						{
							sprintf(text, "%c%.1f", sepChar, nbr);
							fprintf(Outfile, format1, text);
						}
					}
					fprintf(Outfile, "%s", "\n");
				}
			}
			/* next cell */
			fprintf(Outfile, "%s", "\n\n\n");
		}
		/* close file */
		fclose(Outfile);
	}
	else
	{
		printf("\nCould not open the File: %s for writing.\n", Filename);
	}

}