/*
***********************************************************************
* Program to calculate maximum usable frequency and received signal   *
* strength for high-frequency radio circuits.  Uses MINIMUF 3.5.      *
***********************************************************************

input format:
	first line contains five numbers:

	3 1 1 194 20

	1. output format
		1 receiver power (dB/uV)
		2 elevation angle (deg)
		3 delay (ms)
	2. month (1-12)
	3. day (1-31)
	4. smoothed sunspot number
	5. transmitter ERP (dBW)

	second line contains transmitter coordinates and name:

	39.7000 -75.7825 W3HCF Newark 

	third and subsequent lines contain receiver coordinates
	and name:

	39.6808 -75.7486 Evans Hall 
	45.18  -75.45 CHU Ottawa
	40.6803 -105.0408 WWV Ft Collins 
	21.9906 -159.7667 WWVH Hawaii
	52.3667 -1.1833 MSF Rugby

timecode transmitters, frequencies and geographic coordinates

WWV	Ft. Collins, CO	2.5, 5, 10, 15, 20 MHz	40:40N, 105:03W
WWVB    Ft. Collins, CO 60 kHz                  40:40:49N, 105:02:27W
WWVH    Kauai, HI       2.5, 5, 10, 15, 20 MHz  21:59:26N, 159:46:00W
CHU     Ottawa, Canada  3330, 7335, 14670       45:18N, 75:45N
MSF     Rugby, UK       60 kHz, 2.5, 5, 10 MHz  52:22N, 1:11W

*/
#include <stdio.h>
#include <ctype.h>
#include <math.h>
/*
Parameters
*/
#define R 6371.2                /* radius of the Earth (km) */
#define hE 110.                 /* height of E layer (km) */
#define hF 320.                 /* height of F layer (km) */
#define GAMMA 1.42              /* geomagnetic constant */
#define PI 3.141592653589       /* the real thing */
#define PIH PI/2.               /* the real thing/2 */
#define PID 2.*PI               /* the real thing*2 */
#define VOFL 2.9979250e8        /* velocity of light */
#define D2R PI/180.             /* degrees to radians */
#define R2D 180./PI             /* radians to degrees */
#define MINBETA 5.*D2R          /* min elevation angle (rad) */
#define RINZ 50.                /* receiver input impedance (ohms) */
#define BOLTZ 1.380622e-23      /* Boltzmann's constant */
#define NTEMP 273.              /* receiver noise temperature (deg K) */
#define DELTAF 2400.            /* communication bandwidth (Hz) */
#define MPATH 3.                /* multipath threshold (dB) */
#define GLOSS 3.                /* ground-reflection loss (dB) */
#define FMAX 8                  /* max frequencies */
#define HMAX 10                 /* max hops */
/*
Function declarations
*/
extern FILE *fopen();
static double minimuf(void), sgn(double); void edit(int);
/*
Global declarations
*/
int month;                      /* month of year (1-12) */
int day;                        /* day of month*/
int hour;                       /* hour of day (UTC) */
double freq[FMAX];              /* frequencies (MHz) */
double gain[46][FMAX];          /* antenna gain (main lobe) (dB) */
double dB1;                     /* transmitter output power (dBW) */
double ssn;                     /* smoothed sunspot number */
double lat1;                    /* transmitter latitude (deg N) */
double lon1;                    /* transmitter longitude (deg W) */
char site1[30];                 /* transmitter site name */
double lat2;                    /* receiver latitude (deg N) */
double lon2;                    /* receiver longitude (deg W) */
char site2[30];                 /* receiver site name */

double b1;                      /* transmitter bearing (rad) */
double b2;                      /* receiver bearing (rad) */
double d;                       /* great-circle distance (rad) */
double theta;                   /* hour angle (rad) */
int hop;                        /* number of ray hops */
double phi;                     /* angle of incidence (rad) */
FILE *fp_in;                    /* file handle */
char fn_in[12] = "gain.dat";    /* antenna file name */
char fq_in[12] = "ntp.dat";     /* qth file name */
int flag;                       /* output format */
/*
Main program
*/
main() {
/*
Hour variables
*/
	int offset;             /* offset for local time (hours) */
	int time;		/* local time at receiver */
	char daynight[HMAX];    /* day/night indicator (jnx) */
	double mufE[HMAX];      /* max E-layer muf (MHz) */
	double mufF[HMAX];      /* min F-layer muf (MHz) */
	double absorp[HMAX];    /* ionospheric absorption coefficient */
	double dB2[HMAX];       /* receiver input power (dBuV) */
	double path[HMAX];      /* path length (km) */
	double beta[HMAX];      /* elevation angle (rad) */
	double ant[HMAX];       /* antenna gain (dB) */
/*
Path variables
*/
	double delay;           /* path distance (ms) */
	double dhop;            /* hop angle/2 (rad) */
	int daytime, nightime;	/* path indicators */
	double psi;		/* sun zenith angle (rad) */
	double xr, yr;		/* reflection zone coordinates (rad) */
	double xs, ys, yp;	/* subsolar coordinates (rad) */
	double fcE;		/* E-layer critical frequency (MHz) */
	double fcF;		/* F-layer critical frequency (MHz) */
	char cutoff;		/* layer cutoff indicator (EFM) */

	double beta1, phi1, level, muf, dist, Z, floor; /* double temporaries */
	int i,j,h,n;            /* int temporaries */
	float fp1, fp2;         /* float temporaries */
/*
Establish initial conditions
*/
	if ((fp_in = fopen (fn_in, "r")) == NULL) {
		printf ("Antenna file %s not found\n", fn_in); exit (1);
		}
	for (j = 0; j < FMAX; j++) {
		fscanf(fp_in, " %f ", &fp1);
		freq[j] = fp1;
		}
	for (i = 0; i < 46; i++) {
		for (j = 0; j < FMAX; j++) {
			fscanf(fp_in, " %f ", &fp1);
			gain[i][j] = fp1;
			}
		}
	if ((fp_in = fopen (fq_in, "r")) == NULL) {
		printf ("QTH file %s not found\n", fq_in); exit (2);
		}
	if (fscanf(fp_in, " %i %i %i %f %f", &flag, &month, &day, &fp1, &fp2)
		!= 5) exit (0);
	ssn = fp1; dB1 = fp2;
	if (fscanf(fp_in, " %f %f %[^\n]", &fp1, &fp2, site1) != 3) exit (0);
	lat1 = fp1*D2R; lon1 = -fp2*D2R;
	printf("Transmitter %s\n", site1);
L1:     if (fscanf(fp_in, " %f %f %[^\n]", &fp1, &fp2, site2) != 3) exit (0);
	lat2 = fp1*D2R; lon2 = -fp2*D2R;
	printf("Receiver %s\n", site2);
/*
Compute great-circle bearings, great-circle distance, min hops and path delay
*/
	theta = lon1-lon2;
	if (theta >= PI) theta = theta-PID;
	if (theta <= -PI) theta = theta+PID;
	d = acos(sin(lat1)*sin(lat2)+cos(lat1)*cos(lat2)*cos(theta));
	if (d < 0.) d = PI+d;
	b1 = acos((sin(lat2)-sin(lat1)*cos(d))/(cos(lat1)*sin(d)));
	if (b1 < 0.) b1 = PI+b1;
	if (theta < 0) b1 = PID-b1;
	b2 = acos((sin(lat1)-sin(lat2)*cos(d))/(cos(lat2)*sin(d)));
	if (b2 < 0.) b2 = PI+b2;
	if (theta >= 0.) b2 = PID-b2;
	hop = d/(2.*acos(R/(R+hF)));
	beta1 = 0.;
	while (beta1 < MINBETA) {
		hop = hop+1;
		dhop = d/(hop*2.);
		beta1 = atan((cos(dhop)-R/(R+hF))/sin(dhop));
		}
	phi = R*cos(beta1)/(R+hE);
	phi = atan(phi/sqrt(1.-phi*phi));
	delay = 2.*hop*sin(dhop)*(R+hF)/cos(beta1)/VOFL*1e6;
	printf("\nSmoothed sunspot number:%4.0f    Month:%3i     Day:%3i\n",
		ssn, month, day);
	printf("Power:%3.0f dBW    Distance:%6.0f km    Delay:%5.1f ms\n",
		dB1, d*R, delay);
	printf("Location                        Lat      Long    Azim\n");
	printf("%-27s %7.2fN  %7.2fW    %3.0f\n", site1, lat1*R2D, lon1*R2D, b1*R2D);
	printf("%-27s %7.2fN  %7.2fW    %3.0f\n", site2, lat2*R2D, lon2*R2D, b2*R2D);
	printf("UT LT  MUF%8.1f%8.1f%8.1f%8.1f%8.1f%8.1f%8.1f%8.1f\n"
		, freq[0], freq[1], freq[2], freq[3], freq[4], freq[5],
		freq[6], freq[7]);
/*
Hour loop: This loop determines the min-hop path and next two higher-hop
paths. It selects the most likely path for each frequency and calculates the
receiver power. The minimum F-layer MUF is computed directly from MINIMUF 3.5
and regardless of the actual number of hops, geographic coordinates or time
of day.
*/
	offset = lon2*12./PI+.5;
	for (hour = 0; hour < 24; hour++) {
		time = hour-offset;
		if (time < 0) time = time+24;
		if (time >= 24) time = time-24;
		muf = minimuf();
		fcF = muf * cos(phi);
		printf("%2i %2i", hour, time);
/* subsolar coordinates */
		xs = (month-1.)*365.25/12.+day-80.;
		xs = 23.5*D2R*sin(xs/365.25*PID);
		ys = (hour*15.-180.)*D2R;
/*
Path loop: This loop determines the geometry of the min-hop path and the next
two hiher-hop paths. It calculates the minimum F-layer MUF, maximum E-layer
MUF and ionospheric absorption factor for each path.
*/
		for (h = hop; h < hop+2; h++) {
			daytime = 0.;
			nightime = 0.;
			mufE[h] = 0.;
			absorp[h] = 0.;
			daynight[h] = ' ';
/* path geometry, min F-layer MUF */
			dhop = d/(h*2.);
			beta1 = atan((cos(dhop)-R/(R+hF))/sin(dhop));
			phi1 = R*cos(beta1)/(R+hE);
			phi1 = atan(phi1/sqrt(1.-phi1*phi1));
			mufF[h] = fcF/cos(phi1);
/*
Hop loop: This loop calculates the reflection zones and subsolar zenith angle
for each hop along the path. It then computes the minimum E-layer MUF and
ionospheric absorption coeeficient for the total path.
*/
			for (dist = dhop; dist < d; dist = dist+dhop*2) {
/* reflection zone coordinates */
				xr = acos(cos(dist)*sin(lat1)+sin(dist)*cos(lat1)*cos(b1));
				if (xr < 0.) xr = PI+xr;
				xr = PIH-xr;
				yr = acos((cos(dist)-sin(xr)*sin(lat1))/(cos(xr)*cos(lat1)));
				if (yr < 0.) yr = PI+yr;
				if (theta < 0.) yr = -yr;
				yr = lon1-yr;
				if (yr >= PI) yr = yr-PID;
				if (yr <= -PI) yr = yr+PID;
				yp = ys-yr;
				if (yp > PI) yp = PID-yp;
				if (yp < -PI) yp = -PID+yp;
/* E-layer MUF */
				psi = acos(sin(xr)*sin(xs)+cos(xr)*cos(xs)*cos(yp));
				if (psi < 0.) psi = PI+psi;
				Z = cos(psi);
				if (Z > 0.) {
					Z = .9*pow((180.+1.44*ssn)*Z,.25);
					if (Z < .005*ssn) Z = .005*ssn;
					}
				else Z = .005*ssn;
				Z = Z/cos(phi1);
				if (Z > mufE[h]) mufE[h] = Z;
/* ionospheric absorption coeeficient */
				Z = psi;
				if (Z > 100.8*D2R) {
					Z = 100.8*D2R;
					nightime++;
					}
				else daytime++;
				Z = cos(90./100.8*Z);
				if (Z < 0.) Z = 0;
				Z = (1.+.0037*ssn)*pow(Z,1.3);
				if (Z < .1) Z = .1;
				absorp[h] = absorp[h]+Z;
				}
/* path flags */
			if (daytime > 0.) daynight[h] = 'j';
			if (nightime > 0.) daynight[h] = 'n';
			if ((daytime > 0.) && (nightime > 0.)) daynight[h] = 'x';
			}
/*
Frequency loop: This loop calculates the receiver power for each frequency
and path.  It discards paths above the minimum F-layer MUF or below the
maximum E-layer MUF and selects the path with maximum power. It then
calculates the combined power of the remaining paths and determines if
multipath conditions exist.
*/
		printf("%5.1f ", mufF[hop]);
		for (i = 0; i < FMAX; i++) {
			level = -1e6; j = 0;
/* receiver power for each path */
			for (h = hop; h < hop+2; h++) {
				dhop = d/(h*2.);
				switch (daynight[h]) {
					case 'n': Z = 250.; break;
					case 'j': Z = 350.; break;
					default: Z = hF;
					}
				beta[h] = atan((cos(dhop)-R/(R+Z))/sin(dhop));
				phi1 = R*cos(beta[h])/(R+hE);
				phi1 = atan(phi1/sqrt(1.-phi1*phi1));
				path[h] = 2.*h*sin(dhop)*(R+Z)/cos(beta[h]);
				dB2[h] = -1e6;
				if ((freq[i] < mufF[h]) && (freq[i] > mufE[h])) {
					Z = dB1+137.;
					Z = Z-32.44-20.*log10(path[h])-h*GLOSS;
					Z = Z-8.686*log(freq[i]);
					Z = Z-677.2*absorp[h]/cos(phi1)
						/(pow((freq[i]+GAMMA),
						1.98)+10.2);
					muf = modf(beta[h]*R2D/2., &dist);
					n = dist;
					ant[h] = gain[n][i]+muf*(gain[n+1][i]-gain[n][i]);
					Z = Z+ant[h]; dB2[h] = Z;
					if (dB2[h] > level) {
						level = dB2[h]; j = h;;
						}
					}
				}
/* select path and determine multipath */
			floor = 20.*log10(sqrt(4.*RINZ*BOLTZ*NTEMP*DELTAF))+120.;
			muf = 0.;
			if (j > 0) {
				for (h = hop; h < hop+2; h++)
					muf = muf+exp(dB2[h]/10.);
				muf = 10.*log10(muf); cutoff = ' ';
				if (dB2[j] < muf+MPATH) cutoff = 'm';
				if (dB2[j] < floor) cutoff = 's';
				switch (flag) {
					case 1: printf("%5.0f%c%1i%c",
						dB2[j],
						daynight[j], j, cutoff);
						break;
					case 2: printf("%5.1f%c%1i%c",
						beta[j]*R2D,
						daynight[j], j, cutoff);
						break;
					case 3: printf("%5.1f%c%1i%c",
						path[j]/300,
						daynight[j], j, cutoff);
					}
				}
			else {
				printf("        ");
				}
			}
		printf("\n");
		}
	goto L1;
	}
/*
MINIMUF 3.5 (From QST December 1982)

Inputs
lat1 = transmitter latitude, lon1 = transmitter longitude
lat2 = receiver latitude, lon2 = receiver longitude
month = month of year
day = day of month
hour = UTC hour, ssn = sunspot number

Outputs
mufF = F-layer MUF
*/
double minimuf() {

	double MUF;
	double A, B, C, D, P, Q;
	double Y1, Y2;
	double T, T4, T6, T9;
	double G0, G1, G2, G7, G8, G9;
	double K, K1, K5, K6, K7, K8, K9;
	double M9, C0, U, U1, W0, L0;

	K7 = sin(lat1)*sin(lat2)+cos(lat1)*cos(lat2)*cos(lon2-lon1);
	if (K7 < -1.) K7 = -1.; if (K7 > 1.) K7 = 1.; G1 = acos(K7);
	K6 = 1.59*G1; if (K6 < 1.) K6 = 1.; K5 = 1./K6; MUF = 100.;
	for (K1 = 1./(2.*K6); K1 <= 1.-1./(2.*K6); K1 = K1+fabs(.9999-1./K6)) {
		if (K5 != 1.) K5 = .5;
		P = sin(lat2); Q = cos(lat2);
		A = (sin(lat1)-P*cos(G1))/(Q*sin(G1));
		B = G1*K1; C = P*cos(B)+Q*sin(B)*A;
		D = (cos(B)-C*P)/(Q*sqrt(1.-C*C));
		if (D < -1.) D = -1.; if (D > 1.) D = 1.; D = acos(D);
		W0 = lon2+sgn(sin(lon1-lon2))*D;
		if (W0 < 0) W0 = W0+PID; if (W0 >= PID) W0 = W0-PID;
		if (C < -1.) C = -1.; if (C > 1.) C = 1.; L0 = PIH-acos(C);
		Y1 = .0172*(10.+(month-1.)*30.4+day);
		Y2 = .409*cos(Y1);
		K8 = 3.82*W0+12.+.13*(sin(Y1)+1.2*sin(2.*Y1));
		K8 = K8-12.*(1.+sgn(K8-24.))*sgn(fabs(K8-24.));
		if (cos(L0+Y2) > -.26) goto s1520;
		K9 = 0.; G0 = 0.; M9 = 2.5*G1*K5; if (M9 > PIH) M9 = PIH;
		M9 = sin(M9); M9 = 1.+2.5*M9*sqrt(M9);
		goto s1770;

s1520:          K9 = (-.26+sin(Y2)*sin(L0))/(cos(Y2)*cos(L0)+.001);
		K9 = 12.-atan(K9/sqrt(fabs(1.-K9*K9)))*7.639437;
		T = K8-K9/2.+12.*(1.-sgn(K8-K9/2.))*sgn(fabs(K8-K9/2.));
		T4 = K8+K9/2.-12.*(1.+sgn(K8+K9/2.-24.))*sgn(fabs(K8+K9/2-24.));
		C0 = fabs(cos(L0+Y2));
		T9 = 9.7*pow(C0,9.6); if (T9 < .1) T9 = .1;
		M9 = 2.5*G1*K5; if (M9 > PIH) M9 = PIH;
		M9 = sin(M9); M9 = 1.+2.5*M9*sqrt(M9);
		if (T4 < T) goto s1680;
		if ((hour-T)*(T4-hour) > 0.) goto s1690;
		goto s1820;

s1680:          if ((hour-T4)*(T-hour) > 0.) goto s1820;
s1690:          T6 = hour+12.*(1.+sgn(T-hour))*sgn(fabs(T-hour));
		G9 = PI*(T6-T)/K9; G8 = PI*T9/K9; U = (T-T6)/T9;
		G0 = C0*(sin(G9)+G8*(exp(U)-cos(G9)))/(1.+G8*G8);
		G7 = C0*(G8*(exp(-K9/T9)+1.))*exp((K9-24)/2.)/(1.+G8*G8);
		if (G0 < G7) G0 = G7;
		goto s1770;

s1770:          G2 = (1.+ssn/250.)*M9*sqrt(6.+58.*sqrt(G0));
		G2 = G2*(1.-.1*exp((K9-24.)/3.));
		G2 = G2*(1.+(1.-sgn(lat1)*sgn(lat2))*.1);
		G2 = G2*(1.-.1*(1.+sgn(fabs(sin(L0))-cos(L0))));
		goto s1880;

s1820:          T6 = hour+12.*(1.+sgn(T4-hour))*sgn(fabs(T4-hour));
		G8 = PI*T9/K9; U = (T4-T6)/2.; U1 = -K9/T9;
		G0 = C0*(G8*(exp(U1)+1.))*exp(U)/(1.+G8*G8);
		goto s1770;

s1880:          if (MUF > G2) MUF = G2;
		}
	return (MUF);
	}
/*
sgn function (like BASIC)
*/
double sgn(double x) {
	if (x > 0.) return (1.);
	if (x < 0.) return (-1.);
	return (0);
	}
