view pathloss/main.c @ 68:0cfea66a15f3

pathloss: implement Egli model
author Mychaela Falconia <falcon@freecalypso.org>
date Fri, 25 Oct 2024 16:09:52 +0000
parents 599fac1b882d
children
line wrap: on
line source

/*
 * This program reads a file of input parameters for a GSM radio link
 * and computes maximum reach distances per several common models.
 */

#include <ctype.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <strings.h>
#include "range_func.h"

#define	MAX_FIELDS	5
static char linebuf[256], *fields[MAX_FIELDS];
static unsigned nfields;

static float freq_dl, freq_ul;
static float bts_power, bts_rxs, bts_ant_gain;
static float ms_power, ms_rxs;
static int band_set, bts_param_set, ms_param_set;

static float bts_height, ms_height;
static int bts_height_set, ms_height_set;

static float misc_loss;

static void
parse_into_fields(filename, lineno)
	char *filename;
{
	char *cp;

	nfields = 0;
	for (cp = linebuf; ; ) {
		while (isspace(*cp))
			cp++;
		if (*cp == '\0' || *cp == '#')
			break;
		if (nfields >= MAX_FIELDS) {
			fprintf(stderr, "%s line %d: too many fields\n",
				filename, lineno);
			exit(1);
		}
		fields[nfields++] = cp;
		while (*cp && !isspace(*cp))
			cp++;
		if (*cp)
			*cp++ = '\0';
	}
}

static void
set_band(filename, lineno)
	char *filename;
{
	if (nfields != 2) {
		fprintf(stderr, "%s line %d: band setting takes 1 argument\n",
			filename, lineno);
		exit(1);
	}
	if (!strcasecmp(fields[1], "PCS")) {
		freq_dl = 1990.0f;
		freq_ul = 1910.0f;
	} else if (!strcasecmp(fields[1], "B5")) {
		freq_dl = 894.0f;
		freq_ul = 849.0f;
	} else if (!strcasecmp(fields[1], "ISM")) {
		freq_dl = 925.2f;
		freq_ul = 880.2f;
	} else {
		fprintf(stderr, "%s line %d: unknown band name \"%s\"\n",
			filename, lineno, fields[1]);
		exit(1);
	}
	band_set = 1;
}

static void
set_bts_param(filename, lineno)
	char *filename;
{
	if (nfields != 4) {
		fprintf(stderr,
			"%s line %d: bts-param setting takes 3 arguments\n",
			filename, lineno);
		exit(1);
	}
	bts_power = atof(fields[1]);
	bts_rxs = atof(fields[2]);
	bts_ant_gain = atof(fields[3]);
	bts_param_set = 1;
}

static void
set_bts_height(filename, lineno)
	char *filename;
{
	float val, mult;

	if (nfields != 3) {
		fprintf(stderr,
			"%s line %d: bts-height setting takes 2 arguments\n",
			filename, lineno);
		exit(1);
	}
	val = atof(fields[1]);
	if (!strcmp(fields[2], "m"))
		mult = 1.0f;
	else if (!strcmp(fields[2], "ft"))
		mult = 0.3048f;
	else {
		fprintf(stderr,
			"%s line %d: height unit must be either m or ft\n",
			filename, lineno);
		exit(1);
	}
	bts_height = val * mult;
	bts_height_set = 1;
}

static void
set_ms_param(filename, lineno)
	char *filename;
{
	if (nfields != 3) {
		fprintf(stderr,
			"%s line %d: ms-param setting takes 2 arguments\n",
			filename, lineno);
		exit(1);
	}
	ms_power = atof(fields[1]);
	ms_rxs = atof(fields[2]);
	ms_param_set = 1;
}

static void
set_ms_height(filename, lineno)
	char *filename;
{
	float val, mult;

	if (nfields != 3) {
		fprintf(stderr,
			"%s line %d: ms-height setting takes 2 arguments\n",
			filename, lineno);
		exit(1);
	}
	val = atof(fields[1]);
	if (!strcmp(fields[2], "m"))
		mult = 1.0f;
	else if (!strcmp(fields[2], "ft"))
		mult = 0.3048f;
	else {
		fprintf(stderr,
			"%s line %d: height unit must be either m or ft\n",
			filename, lineno);
		exit(1);
	}
	ms_height = val * mult;
	ms_height_set = 1;
}

static void
add_loss_line(filename, lineno)
	char *filename;
{
	float val;

	if (nfields != 2) {
		fprintf(stderr, "%s line %d: loss setting takes 1 argument\n",
			filename, lineno);
		exit(1);
	}
	val = atof(fields[1]);
	misc_loss += val;
}

static void
process_line(filename, lineno)
	char *filename;
{
	if (!strcmp(fields[0], "band"))
		set_band(filename, lineno);
	else if (!strcmp(fields[0], "bts-param"))
		set_bts_param(filename, lineno);
	else if (!strcmp(fields[0], "bts-height"))
		set_bts_height(filename, lineno);
	else if (!strcmp(fields[0], "ms-param"))
		set_ms_param(filename, lineno);
	else if (!strcmp(fields[0], "ms-height"))
		set_ms_height(filename, lineno);
	else if (!strcmp(fields[0], "loss"))
		add_loss_line(filename, lineno);
	else {
		fprintf(stderr, "%s line %d: non-understood setting \"%s\"\n",
			filename, lineno, fields[0]);
		exit(1);
	}
}

static void
check_required_params(filename)
	char *filename;
{
	if (!band_set) {
		fprintf(stderr, "error: %s failed to set band\n", filename);
		exit(1);
	}
	if (!bts_param_set) {
		fprintf(stderr, "error: %s failed to set bts-param\n",
			filename);
		exit(1);
	}
	if (!bts_height_set) {
		fprintf(stderr, "error: %s failed to set bts-height\n",
			filename);
		exit(1);
	}
	if (!ms_param_set) {
		fprintf(stderr, "error: %s failed to set ms-param\n", filename);
		exit(1);
	}
	if (!ms_height_set) {
		fprintf(stderr, "error: %s failed to set ms-height\n",
			filename);
		exit(1);
	}
}

static void
read_param_file(filename)
	char *filename;
{
	FILE *inf;
	int lineno;

	inf = fopen(filename, "r");
	if (!inf) {
		perror(filename);
		exit(1);
	}
	for (lineno = 1; fgets(linebuf, sizeof linebuf, inf); lineno++) {
		if (!index(linebuf, '\n')) {
			fprintf(stderr,
				"%s line %d: too long or missing newline\n",
				filename, lineno);
			exit(1);
		}
		parse_into_fields(filename, lineno);
		if (nfields == 0)
			continue;
		process_line(filename, lineno);
	}
	fclose(inf);
	check_required_params(filename);
}

main(argc, argv)
	char **argv;
{
	float path_loss_dl, path_loss_ul;

	if (argc != 2) {
		fprintf(stderr, "usage: %s param-file\n", argv[0]);
		exit(1);
	}
	read_param_file(argv[1]);
	/* do the math */
	path_loss_dl = bts_power + bts_ant_gain - ms_rxs - misc_loss;
	path_loss_ul = ms_power + bts_ant_gain - bts_rxs - misc_loss;
	printf("Path loss budget: %.2f dB DL, %.2f dB UL\n", path_loss_dl,
		path_loss_ul);
	/* distance per various models */
	printf("FSL: %.2f m DL, %.2f m UL\n",
		rf_range_freespace(freq_dl, path_loss_dl),
		rf_range_freespace(freq_ul, path_loss_ul));
	printf("Egli: %.2f m DL, %.2f m UL\n",
		rf_range_egli(freq_dl, path_loss_dl, bts_height, ms_height),
		rf_range_egli(freq_ul, path_loss_ul, bts_height, ms_height));
	/* Hata model coming next */
	exit(0);
}