/*
 * Work out good lengths for EFRW antennas
 * (aka work out bad ones, and list what's left)
 *
 */
#define VERSION "_$Id: rw20-efrw-len.c,v 1.6 2026/05/13 07:23:01 r Exp $_";

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <float.h>
#include <math.h>
#include <assert.h>
#include <ctype.h>

// Standard defines
#define PI 3.14159265358979323846264338327950288419716939937510582097494459230781640628620899
#define SPEED_OF_LIGHT 299792458	// Speed of light in m/s

#ifndef BYTE
typedef unsigned char BYTE;
#endif
#ifdef _countof
#define NENTRIES(var) _countof(var)
#else
#define NENTRIES(var) ((unsigned)(sizeof(var)/sizeof(var[0])))
#endif


// Frequency bands of interest, these are based on the UK band plans, edit for your region
struct
{	double freq_min;
	double freq_max;
	unsigned uWavelength;
} const asBands[]=
{	{ 1.800, 2.000, 160 },	//	 0	160	164.27
	{ 3.500, 3.800, 80 },	//	 1	 80	82.13
	{ 5.2585,5.4065, 60 },	//	 2	 60	56.22
	{ 7.000, 7.200, 40 },	//	 3	 40	42.22
	{10.100,10.150, 30 },	//	 4	 30	29.61
	{14.000,14.350, 20 },	//	 5	 20	21.15
	{18.068,18.168, 17 },	//	 6	 17	16.55
	{21.000,21.450, 15 },	//	 7	 15	14.12
	{24.890,24.990, 12 },	//	 8	 12	12.02
	{28.000,29.700, 10 }	//	 9	 10	10.39
};


// What range for this run
#define MAX_LEN		100	// Max length in meters
#define LEN_STEP	1000	// Steps in fractions of a meter
#define FREQ_STEP	0.0005	// Steps through the band (In MHz, so 0.0005=500Hz)
#define MAX_HARMON	10
#define VEL_MIN		0.94
#define VEL_MAX		0.98


// Main results store
BYTE abyLengths[MAX_LEN*LEN_STEP];


// Display meters as feet, uses a local static buffers, so can only use MAX_SBUF times in a single printf type call
#define MAX_SBUF 10
#define MAX_SLEN 16
char const *disp_m_as_ft(double dMeters)
{	static char acBuf[MAX_SBUF][MAX_SLEN];
	static BYTE byBufNum=0;
	double dInches=dMeters/0.0254;
	unsigned uFeet=(unsigned)(dInches/12.0);
	dInches-=uFeet*12.0;
	byBufNum++;
	byBufNum%=MAX_SBUF;
	snprintf(acBuf[byBufNum],MAX_SLEN,"%u\'%.1f\"",uFeet,dInches);
	return acBuf[byBufNum];
}


// Main program, arg is max band to include
int main(int argc,char *argv[])
{	unsigned uMinBand=0;
	if (argc>1 && isdigit(argv[1][0]))
	{	// Parse arg to find max band wavelength, default is top-band (160m)
		unsigned uMeters=atoi(argv[1]);
		while (asBands[uMinBand].uWavelength>uMeters)
		{	uMinBand++;
			if (uMinBand+1>=NENTRIES(asBands))
				break;
		}
	}

	// Disp parameters, to help understand the results
	printf("Max len=%um, steps of %gm, freq steps of %gMHz, max harmonic=%u, velocity %g-%g, for bands %u and shorter\n",
		MAX_LEN,1.0/LEN_STEP,(double)FREQ_STEP,MAX_HARMON,VEL_MIN,VEL_MAX,asBands[uMinBand].uWavelength);

	// For each band sweep through the frequencies, wire velocities, and harmonics marking those that have clashes
	unsigned uMin=0;
	for (unsigned uBand=uMinBand ; uBand<NENTRIES(asBands) ; uBand++)
	{	for (double dFreq=asBands[uBand].freq_min-FREQ_STEP ; dFreq<=asBands[uBand].freq_max ; dFreq+=FREQ_STEP)
		{	for (double dWireVel=VEL_MIN ; dWireVel<=VEL_MAX ; dWireVel+=0.005)
			{	double dWavelength=(SPEED_OF_LIGHT/(dFreq*1000000))*dWireVel;
				// Sortout min length based on 1/4 wave of the tested frequencies
				unsigned uPos=(unsigned)(dWavelength*LEN_STEP/4);
				if (uPos>uMin)
					uMin=uPos;
				for (unsigned uHarmonic=1 ; uHarmonic<MAX_HARMON ; uHarmonic++)
				{	uPos=(unsigned)(dWavelength*LEN_STEP*(double)uHarmonic);
					if (uPos>=NENTRIES(abyLengths)) break;
					//if (abyLengths[uPos]!=0) printf("%u=%.f x %u\n",uPos,dFreq,uHarmonic);
					abyLengths[uPos]=uBand+1;
				}
			}
		}
	}

#if 0
	// Raw dump (useful while debugging)
	for (unsigned uPos=uMin ; uPos<NENTRIES(abyLengths) ; uPos++)
		if (abyLengths[uPos]==0)
			printf("%u ",uPos);
	printf("\n");
#endif

	// Dump what's left
	for (unsigned uPos=uMin ; (uPos+1)<NENTRIES(abyLengths) ; uPos++)
		if (abyLengths[uPos]==0)
		{	unsigned uStop;
			// Found starting length, find max length
			for (uStop=uPos+1 ; uStop<NENTRIES(abyLengths) ; uStop++)
				if (abyLengths[uStop]!=0) break;
			if ((uPos+(LEN_STEP/4))<uStop)	// Ignore options under 25cm
			{	double fLenMin=uPos/(double)LEN_STEP;
				double fLenMax=(uStop-1)/(double)LEN_STEP;
				double fLenMid=(fLenMin+fLenMax)/2.0;
				// Watch out with disp_feet, as it only has 1 string buffer!
				printf("%gm-%gm=%gm mid=%.2fm ",fLenMin,fLenMax,fLenMax-fLenMin,fLenMid);
				printf("%s-%s=%s mid=%s\n",disp_m_as_ft(fLenMin),disp_m_as_ft(fLenMax),disp_m_as_ft(fLenMax-fLenMin),disp_m_as_ft(fLenMid));
			}
			uPos=uStop+1;	// Continue from where we left off
		}
	return 0;
}
