FMCW Radar Postprocessing in Time-variant Indoor Scenarios

Detailed Description

This is an example of how to use the WInProp API for FMCW radar postprocessing in a time-variant indoor scenario. The full example is distributed with the installation.
#include <cstdio>
#include <string>
#include <iostream>

#include "indoor_propagation_fmcw.h"

#include "../SuperposeMS/winprop_superposems.hpp"
#include "../SuperposeMS/winprop_superposems_engine.h"

#include "../Interface/Init.h"     
#include "../FMCW/winprop_fmcw_radar.h"

#ifndef API_DATA_FOLDER
#define API_DATA_FOLDER "../../api/winprop/data/"
#endif // !API_DATA_FOLDER

int main(int argc, char** argv)
{
	int Error = 0;

	/* ------------------ Initialization of scenario --------------------------- */
	WinProp_Scenario WinPropScenario;
	WinProp_Structure_Init_Scenario(&WinPropScenario);
	/* ---------- Load indoor vector database and initialize scenario ------------ */
	/* Assign database name. */
	WinPropScenario.Scenario = WINPROP_SCENARIO_INDOOR;
	char VectorDatabase[512];
	sprintf(VectorDatabase, "%s", API_DATA_FOLDER "../data/indoor/RadarScenario.idb");
	WinPropScenario.VectorDatabase = VectorDatabase;

	/* Define callback functions. */
	WinProp_Callback WinPropCallback;
	WinProp_Structure_Init_Callback(&WinPropCallback);
	WinPropCallback.Percentage = CallbackProgress;
	WinPropCallback.Message = CallbackMessage;
	WinPropCallback.Error = CallbackError;

	/* ------------------------- Set up prediction ------------------------------- */
	if (Error == 0)
	{
		/* Definition of prediction points. */
		int NrPoints = 1;
		WinProp_Receiver WinPropReceiver;
		WinProp_Structure_Init_Receiver(&WinPropReceiver);
		WinPropReceiver.Location.x = 5.8;
		WinPropReceiver.Location.y = -2.;
		WinPropReceiver.Location.z = 0.5;
		WinPropReceiver.GroupID = 230; // Group id of the radar car

		/* Definition of antenna pattern. */
		WinProp_Pattern WinPropPattern;
		WinProp_Structure_Init_Pattern(&WinPropPattern);
		WinPropPattern.Mode = PATTERN_MODE_FILE; // Load pattern from file
		char PatternFileName[500];
		sprintf(PatternFileName, "%s", API_DATA_FOLDER "antennas/RadarAntenna.ffe");
		WinPropPattern.Filename = PatternFileName; // Pattern file (including extension)

		/* Definition of antenna properties. */
		WinProp_Antenna WinPropAntenna;
		WinProp_Structure_Init_Antenna(&WinPropAntenna);
		WinPropAntenna.Enabled = 1;
		WinPropAntenna.Id = 1;
		WinPropAntenna.SiteId = 1;
		WinPropAntenna.Longitude_X = 5.8;
		WinPropAntenna.Latitude_Y = -2.0;
		WinPropAntenna.Height = 0.5; // Antenna height 0.5 meter
		WinPropAntenna.Model = WINPROP_MODEL_SRT; // Use the standard ray tracing
		WinPropAntenna.DataType = PROP_RESULT_POWER; // Compute received power
		WinPropAntenna.Power = 13.; // Power in dBm
		WinPropAntenna.PowerMode = WINPROP_POWER_OUTPUT; // Power mode
		WinPropAntenna.Frequency = 77000.; // Frequency 77 GHz
		WinPropAntenna.Pattern = &WinPropPattern; // Use pattern defined above
		WinPropAntenna.Azimuth = 90.; // Antenna points in eastern direction
		WinPropAntenna.Downtilt = 0.; // Downtilt of 0 degree
		WinPropAntenna.GroupIDMounted = 230; // Group id of the radar car

		char AntennaName[500];
		sprintf(AntennaName, "%s", "radar_antenna");
		WinPropAntenna.Name = AntennaName; // name of the antenna

		/* Definition of ray tracing parameters. */
		Model_RAYTRACING ModelRT;
		WinProp_Structure_Init_Model_RayTracing(&ModelRT);
		ModelRT.MaxNrDiffractions = 1;
		ModelRT.MaxNrReflections = 1;
		ModelRT.MaxNrTransmissions = 0;
		ModelRT.MaxNrReflDiffr = 1;
		ModelRT.Superposition = 1;
		ModelRT.ComputeAlwaysDirectRay = 2;

		/* Definition of outputs to be computed and written in WinProp format. */
		WinProp_Additional WinPropMore;
		WinProp_Propagation_Results OutputResults;
		WinProp_Structure_Init_Additional(&WinPropMore);
		WinProp_Structure_Init_Propagation_Results(&OutputResults);
		WinPropMore.OutputResults = &OutputResults;
		OutputResults.RayFilePropPaths = 1;
		OutputResults.StrFilePropPaths = 1;

		/* Further parameters: */
		WinPropMore.InteractionModel = WINPROP_INTERACTION_MODEL_GTDUTD;
		WinPropMore.MaxNumberPathsUsed = 1;
		WinPropMore.MaxNumberPaths = 64;
		WinPropMore.MaxDynamicPathsEnabled = 1;
		WinPropMore.MaxDynamicPaths = 100.;
		WinPropMore.MaxPathLossEnabled = 1;
		WinPropMore.MaxPathLoss = 200.;

		/* Set time instance(s) and output folder */
		double timeInstances[1] = { 1.0 }; // 1 s
		WinPropMore.TimeInstances = timeInstances;
		WinPropMore.NbrTimeInstances = 1;
		OutputResults.ResultPath = API_DATA_FOLDER "output/prop";

		/* Call the WinProp API to open a project and load the vector database. */
		int ProjectHandle = 0;
		Error = WinProp_Open(&ProjectHandle, &WinPropScenario, &WinPropCallback);

		/* ----- Start prediction (including loop over all time instances) ------ */
		WinProp_ResultPointsList* PowerResult = nullptr;
		Error = WinProp_Predict_Points(
			ProjectHandle,
			&WinPropAntenna,
			&WinPropReceiver,
			NrPoints,
			&ModelRT,
			&WinPropMore,
			&PowerResult,
			nullptr,
			nullptr);

		
		// Optionally, create range-angle heat maps
		bool generateRangeAngle = true; 
		int nbrRxElements = 16; // nr of RX elements in a linear array, relevant for range-angle heat maps
		float rxSpacing = 0.5; // elments spacing in [lambda], relevant for range-angle heat maps
		WinProp_RayMatrixElement subStreamRays[16]; // ray matices for sub-stream results, relevant for range-angle heat maps
		for (int i = 0; i < nbrRxElements; i++)
			WinProp_Structure_Init_RayMatrixElement(&subStreamRays[i]);

		if (generateRangeAngle)
		{
			/*----------------------- Rx antenna superposition (RunMS) --------------------*/

			// Create project
			WinProp_SuperposeMS SuperposeMS;

			// Setup tx array
			WINPROP_MS_ARRAY_TYPE txArrayAPI = WINPROP_MS_ARRAY_SINGLE_ANTENNA;
			WINPROP_MS_POLARIZATION_TYPE pol = WINPROP_MS_POLARIZATION_VERTICAL;

			if (Error == 0)
				Error = SuperposeMS.setArray(false, txArrayAPI, 1, 0.f, 0.f, 0.f, false);

			if (Error == 0)
				// Changing the azimuth from north over east in RunPRO to east over north orientation, 
				// 90deg tilt is equal to horizontal orientation.
				Error = SuperposeMS.setArrayElement(
					false, 0, nullptr, nullptr,
					(float)(-WinPropAntenna.Azimuth + 90. + 360.),
					(float)(WinPropAntenna.Downtilt + 90.), pol, { 0., 0., 0. });

			// Setup rx array
			WINPROP_MS_ARRAY_TYPE rxArrayAPI = WINPROP_MS_ARRAY_LINEAR;
			if (Error == 0) {
				Error = SuperposeMS.setArray(true,
					rxArrayAPI, /* array Type*/
					nbrRxElements /* nrElements */,
					270.f /* array azimuth */,
					rxSpacing /*rx spacing */,
					0.f /* radius*/,
					false /* considerAntennaCoupling */);
			}

			// set individual rx elements
			for (int rx = 0; rx < nbrRxElements && Error == 0; rx++)
			{
				// element azimuth with east over north orientation (overall direction: east when adding array Azimuth),
				// 90deg tilt is equal to horizontal orientation.
				Error = SuperposeMS.setArrayElement(
					true, rx, &WinPropPattern, nullptr,
					90.f/*azimuth*/, 90.f/*tilt*/,
					pol, { 0., 0., 0. }/*offset*/);
			}

			// Set computation parameter 
			WinProp_MS_Para msPara;
			WinProp_Structure_Init_MS_Para(&msPara);
			msPara.coherentSuperposition = 1;
			msPara.nrRays = 64;

			// Enable ouputs
			WinProp_MS_AdditionalResults msAdditionalResult;
			WinProp_Structure_Init_MS_AdditionalResults(&msAdditionalResult);
			msAdditionalResult.propagationPaths = 1;
			msAdditionalResult.perSubChannelPower = 1;
			sprintf(msAdditionalResult.outputPath, "%s%s", API_DATA_FOLDER, "output/runMS");

			// set propagtion points
			if (Error == 0)
				Error = SuperposeMS.setPropagationPoints(0, WinPropAntenna, *PowerResult);

			// compute RunMS
			if (Error == 0)
				Error = SuperposeMS.compute(msPara, &msAdditionalResult, &WinPropCallback);

			// get sub-stream results
			for (int rx = 0; rx < nbrRxElements && Error == 0; rx++)
			{
				WinProp_ResultPointsList subStreamRes;
				WinProp_Structure_Init_ResultPointsList(&subStreamRes);
				char subStreamResPower[1024];
				char subStreamResRays[1024];
				sprintf(subStreamResPower, "%s/%s Sub-Channel Rx-%d Power.fpp", msAdditionalResult.outputPath, AntennaName, rx + 1);
				sprintf(subStreamResRays, "%s/%s Sub-Channel Rx-%d Rays.ray", msAdditionalResult.outputPath, AntennaName, rx + 1);

				Error = WinProp_ResultPoints_Read(&subStreamRes, subStreamResPower, subStreamResRays);
				
				if (Error == 0)
				{
					// move rays
					subStreamRays[rx] = subStreamRes.ResultPoints[0].Rays;
					subStreamRes.ResultPoints[0].Rays.NrRays = 0;
					subStreamRes.ResultPoints[0].Rays.Rays = nullptr;
				}

				WinProp_Structure_Free_ResultPointsList(&subStreamRes);
			}

		}

		if (Error == 0)
		{
			/* ------------------------- FMCW radar ------------------------------- */

			/* Definition of radar parameters */
			WinProp_FMCW_Para RadarPara;
			RadarPara.Mode = WinProp_FMCW_Para::ParametersMode::SET_PARAMETERS; // set parameters mode
			RadarPara.Freq = 77e9; // radar frequency
			RadarPara.Tc = 20e-6; // chirp duration
			RadarPara.B = 1e9; // sweep bandwidth
			RadarPara.NrChirps = 64; // number of chirps
			RadarPara.Cohrent = true; // coherent superposition of rays
			RadarPara.NrBins = WinProp_FMCW_Para::FreqBins::FFT_512; // FFT Samples (range domain)
			RadarPara.NrRx = generateRangeAngle ? nbrRxElements : 1;
			RadarPara.RxSpacing = rxSpacing * 2.99792458e8 / (WinPropAntenna.Frequency * 1.e6);

			/* Parameters relevant to output results */
			RadarPara.WindFunc = WinProp_FMCW_Para::Windowing::HANNING;
			RadarPara.GenerateRangeDoppler = true;
			RadarPara.GenerateRangeAngle = generateRangeAngle;
			RadarPara.GenerteIQ = false;

			/* Range-Doppler heatmap result */
			WinProp_ResultPlane RangeVelocity;
			WinProp_Structure_Init_ResultPlane(&RangeVelocity);

			/* Range-Angle heatmap result */
			WinProp_ResultPlane RangeAngle;
			WinProp_Structure_Init_ResultPlane(&RangeAngle);

			/*------- FMCW radar post-processing -------*/
			WinProp_FMCW_Radar FMCW_Radar;
			Error = FMCW_Radar.compute(&RadarPara, 
				RadarPara.NrRx,
				generateRangeAngle ? subStreamRays : &PowerResult->ResultPoints[0].Rays,
				&WinPropCallback, 
				&RangeVelocity, 
				generateRangeAngle ? &RangeAngle : nullptr);

			/* Use RangeVelocity to find the range and velocity of the targets */

			if (Error == 0 && RadarPara.GenerateRangeDoppler)
			{
				/* Save values in ASCII file */
				const char* Filename = API_DATA_FOLDER "output/rangeDopplerHeatmap.txt";
				FILE* OutputFile = fopen(Filename, "w");
				for (size_t i = 0; i < RangeVelocity.Columns; i++)
				{
					for (size_t j = 0; j < RangeVelocity.Lines; j++)
					{
						fprintf(OutputFile, "%.3f\t%.3f\t%.3f\n",
							RangeVelocity.Matrix[i][j].Location.x,
							RangeVelocity.Matrix[i][j].Location.y,
							RangeVelocity.Matrix[i][j].Value);
					}
				}

				/* Close file. */
				fclose(OutputFile);
			}

			if (Error == 0 && RadarPara.GenerateRangeAngle)
			{
				/* Save values in ASCII file */
				const char* Filename = API_DATA_FOLDER "output/rangeAngleHeatmap.txt";
				FILE* OutputFile = fopen(Filename, "w");
				for (size_t i = 0; i < RangeAngle.Columns; i++)
				{
					for (size_t j = 0; j < RangeAngle.Lines; j++)
					{
						fprintf(OutputFile, "%.3f\t%.3f\t%.3f\n",
							RangeAngle.Matrix[i][j].Location.x,
							RangeAngle.Matrix[i][j].Location.y,
							RangeAngle.Matrix[i][j].Value);
					}
				}

				/* Close file. */
				fclose(OutputFile);
			}

			/* Free memory */
			WinProp_Structure_Free_ResultPlane(&RangeVelocity);
			WinProp_Structure_Free_ResultPlane(&RangeAngle);
		}

		/* Free memory */
		WinProp_Structure_Free_ResultPointsList(PowerResult);

		for (int i = 0; i < nbrRxElements; i++)
			WinProp_FreeRayMatrixElement(&subStreamRays[i]);

		/* Close project */
		WinProp_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);
}