I decided to add another answer. This because I was curious about some of the calculators which would calculate the ideal length of wire. These calculators can be found by simple searches.
Some of those simply take the mid of the band, and calculates lengths to avoid.
I decided to create a simple calculator in python3
as that is my preferred scripting language. See code below.
Usage parameters:
-b:a,b,c
-b:a
This runs the calculation just on bands a, b, c specified, comma-separated, no spaces
-b:a-b
This runs the calculation on all bands a to b (including a, including b), ensure a is lower in frequency than b, and no spaces. Example -b:80-20
which will run for 80,60,40,30,20 m bands.
-b:all
This runs the calculation on all defined bands. This is the default and can be omitted
-margin:x
This defines the x% margin to add to the edge of the bands, to ensure that the calculation stays away from half wave on band edges. Example: if x is given as 10, and band is the 40m band, and this band is defined as 7000-7200 kHz, then the band edges are + and - the 10% of the bandwidth: e.g. 6980-7220 kHz. If this x is given as -50, then it calculates only the exact middle of the band, e.g. 7100 kHz. if this is given as 50, then it will be 6900-7300 kHz. By default this is 10%, and can be omitted.
-min:y
This is the minimum wire length to be calculated, based on factor y of the lowest frequency wavelength. By default this is 0.25 which is a quarter-wave on the lowest frequency as a minimum for wire length. Default is 0.25 and can be omitted.
-max:z
This is the maximum wire length to be calculated based on factor z of the lowest frequency wavelength. By default this is 2 and can be omitted.
-res:2
This is the (metric) resolution of the results, indicating decimal places after the decimal point for all calculations. 2 will indicate a resolution of centimeters, 1 will indicate a resolution of 10-cm, 0 will indicate a resolution of 1 meter. Default is 2 and can be omitted.
Example usage:
To calculate 160,80,40 meter bands, with default resolution, default minimum and default maximum, and a default margin:
longwire.py -b:160,80,40
Result:
-------------------------------------------------------------------
Random Longwire Antenna Calculation
Input parameters:
-------------------------------------------------------------------
Bands : ['160', '80', '40']
Minimum factor: 0.25 (of lowest frequency)
Maximum factor: 2 (of lowest frequency)
Margin : 10 % (band edge adjustment)
-------------------------------------------------------------------
Avoidance for band: 160 [[74.26, 84.27], [148.52, 168.54], [222.78, 252.81], [297.04, 337.08], [371.3, 421.35]]
Avoidance for band: 80 [[39.16, 43.23], [78.32, 86.46], [117.48, 129.69], [156.64, 172.92], [195.8, 216.15], [234.96, 259.38], [274.12, 302.61], [313.28, 345.84], [352.44, 389.07]]
Avoidance for band: 40 [[20.78, 21.49], [41.56, 42.98], [62.34, 64.47], [83.12, 85.96], [103.9, 107.45], [124.68, 128.94], [145.46, 150.43], [166.24, 171.92], [187.02, 193.41], [207.8, 214.9], [228.58, 236.39], [249.36, 257.88], [270.14, 279.37], [290.92, 300.86], [311.7, 322.35], [332.48, 343.84], [353.26, 365.33]]
Start Lengths [[42.13, 337.08]]
Take away avoids [[74.26, 84.27], [148.52, 168.54], [222.78, 252.81], [297.04, 337.08], [371.3, 421.35]]
New Lengths [[42.13, 74.25], [84.28, 148.51], [168.55, 222.77], [252.82, 297.03]]
Take away avoids [[39.16, 43.23], [78.32, 86.46], [117.48, 129.69], [156.64, 172.92], [195.8, 216.15], [234.96, 259.38], [274.12, 302.61], [313.28, 345.84], [352.44, 389.07]]
New Lengths [[43.24, 74.25], [86.47, 117.47], [129.7, 148.51], [172.93, 195.79], [216.16, 222.77], [259.39, 274.11]]
Take away avoids [[20.78, 21.49], [41.56, 42.98], [62.34, 64.47], [83.12, 85.96], [103.9, 107.45], [124.68, 128.94], [145.46, 150.43], [166.24, 171.92], [187.02, 193.41], [207.8, 214.9], [228.58, 236.39], [249.36, 257.88], [270.14, 279.37], [290.92, 300.86], [311.7, 322.35], [332.48, 343.84], [353.26, 365.33]]
New Lengths [[43.24, 62.33], [64.48, 74.25], [86.47, 103.89], [107.46, 117.47], [129.7, 145.45], [172.93, 187.01], [193.42, 195.79], [216.16, 222.77], [259.39, 270.13]]
-------------------------------------------------------------------
Results:
-------------------------------------------------------------------
from 43.24 to 62.33 meters (mid 52.78 m)
from 64.48 to 74.25 meters (mid 69.37 m)
from 86.47 to 103.89 meters (mid 95.18 m)
from 107.46 to 117.47 meters (mid 112.47 m)
from 129.7 to 145.45 meters (mid 137.57 m)
from 172.93 to 187.01 meters (mid 179.97 m)
from 193.42 to 195.79 meters (mid 194.6 m)
from 216.16 to 222.77 meters (mid 219.47 m)
from 259.39 to 270.13 meters (mid 264.76 m)
EXTRA:
You may specify a frequency range in kHz instead of bands. Example, 15 MHz to 20 MHz, minimum 1/8 wave on lowest frequency, maximum 1 wave on lowest frequency, with a 10cm resolution, and a 20% margin for the band edges:
longwire.py -range:15000-20000 -margin:20 -min:0.125 -max:1 -res:1
Result:
-------------------------------------------------------------------
Random Longwire Antenna Calculation
Input parameters:
-------------------------------------------------------------------
Bands : ['custom']
Range : 15000-20000 kHz
Minimum factor: 0.125 (of lowest frequency)
Maximum factor: 1.0 (of lowest frequency)
Margin : 20.0 % (band edge adjustment)
-------------------------------------------------------------------
Avoidance for band: custom [[7.1, 10.7], [14.2, 21.4], [21.3, 32.1], [28.4, 42.8]]
Start Lengths [[2.7, 21.4]]
Take away avoids [[7.1, 10.7], [14.2, 21.4], [21.3, 32.1], [28.4, 42.8]]
New Lengths [[2.7, 7.0], [10.8, 14.1]]
-------------------------------------------------------------------
Results:
-------------------------------------------------------------------
from 2.7 to 7.0 meters (mid 4.8 m)
from 10.8 to 14.1 meters (mid 12.4 m)
Comments and improvements of the code are very welcome !
Here is the code:
#!python3
# -*- coding: utf-8 -*-
# Author: Edwin van Mierlo
# Call sign: EI2HEB
# No claims of functionality are being made, no warrantees either
# You may re-use this code anyway you see fit
# Just give me some credit
###############################################################################
## IMPORTS ##
###############################################################################
import sys
###############################################################################
## GLOBAL DEFAULTS ##
###############################################################################
# Band definitions
# low is the lower band edge in kHz
# high is the upper band edge in kHz
# the key is the 'band name'
BANDS_D = {
"160":{"low":1800, "high":2000},
"80" :{"low":3500, "high":3800}, ## upto 4000 in USA
"60" :{"low":5250, "high":5450}, ## varies by region
"40" :{"low":7000, "high":7200}, ## upto 7300 in USA
"30" :{"low":10100, "high":10150},
"20" :{"low":14000, "high":14350},
"17" :{"low":18068, "high":18168},
"15" :{"low":21000, "high":21450},
"12" :{"low":24890, "high":24990},
"10" :{"low":28000, "high":29700},
"6" :{"low":50000, "high":54000}
}
# for calculation purposes, you still want
# to stay away from half wave of band edges
# this margin can be expressed in a percentage
# of the span of the band. Example:
# If 10% is used as margin for the 40m band
# and 40m band is defined as "low":7000, "high":7200
# then the calculation will take a lower edge of 6980
# and will take an upper edge of 7220
MARGIN = 10
# the minimum length is at least
# MINIMUM * (lowest frequency wave length)
# By default this is set to 0.25 to ensure
# that the calculated lengths are at least
# a quarter wavelength on the lowest frequency.
MINIMUM = 0.25
# by setting the maximum to a meaningful value
# if forces the calculation to stop at a value of
# MAXIMUM * (lowest frequency wave length)
# this ensures the calculation actually stops
# default is set to two times the wavelength of
# the lowest frequency
MAXIMUM = 2
# you can set the resolution of the calculation
# which by default is set to 2 digits after
# the decimal point. Which is centimeters
# by setting this to 3 it will be millimeters
METRIC_RESOLUTION = 2
###############################################################################
## INTERNAL FUNCTIONS ##
###############################################################################
def _get_band_edges(bands_d, index, margin):
start_freq = int(bands_d[index]['low'])
stop_freq = int(bands_d[index]['high'])
lower_bandedge = start_freq - ((stop_freq - start_freq) * (margin/100))
upper_bandedge = stop_freq + ((stop_freq - start_freq) * (margin/100))
return lower_bandedge, upper_bandedge
def _get_wirelength_limits(frequency, minimum, maximum, metric_res):
wavelength = 300/(frequency/1000)
return (round(wavelength * minimum, metric_res),
round(wavelength * maximum, metric_res))
def _get_band_avoidance(lower_bandedge,
upper_band_edge,
min_wire_length,
max_wire_length,
metric_res):
avoid = []
half_wave_low = round(300/(lower_bandedge/1000)/2, metric_res)
half_wave_high = round(300/(upper_band_edge/1000)/2, metric_res)
avoid.append([half_wave_high, half_wave_low])
factor = 2
current_high = half_wave_high
while current_high < max_wire_length:
current_low = round(half_wave_low * factor, metric_res)
current_high = round(half_wave_high * factor, metric_res)
if current_low >= min_wire_length:
avoid.append([current_high, current_low])
factor += 1
return avoid
def _get_good_lenghts(lengths, avoid, metric_res):
my_lengths = []
step = 1/(10**metric_res)
for length_range in lengths:
my_lengths += _recursive_check(length_range, avoid, step, metric_res)
return my_lengths
def _recursive_check(good, bad, step, metric_res):
# end of good is smaller than start of bad
if good[1] < bad[0][0]:
if good[0] < good[1]:
return [good]
else:
return []
l = []
if (bad[0][0] > good[0]) and (bad[0][0] < good[1]):
l.append([good[0], round(bad[0][0] - step, metric_res)])
if bad[0][1] < good[1]:
l += _recursive_check([round(bad[0][1] + step, metric_res), good[1]],
bad[1:],
step,
metric_res)
elif ((bad[0][0] < good[0])
and (bad[0][1] < good[1])
and (bad[0][1] > good[0])):
l += _recursive_check([round(bad[0][1] + step, metric_res), good[1]],
bad[1:],
step,
metric_res)
elif (bad[0][0] < good[0]) and (bad[0][1] > good[1]):
return l
elif (bad[0][0] > good[0]) and (bad[0][1] > good[1]):
l.append([good[0], round(bad[0][0] - step, metric_res)])
else:
l += _recursive_check(good,
bad[1:],
step,
metric_res)
return l
def _get_multi_band(bands_d, bands_l, minimum, maximum, margin, metric_res):
lower_bandedge, upper_bandedge = _get_band_edges(bands_d,
bands_l[0],
margin)
min_wire_length, max_wire_length = _get_wirelength_limits(lower_bandedge,
minimum,
maximum,
metric_res)
my_lengths = [[min_wire_length, max_wire_length]]
my_avoids = []
for band in bands_l:
lower_bandedge, upper_bandedge = _get_band_edges(bands_d,
band,
margin)
avoid = _get_band_avoidance(lower_bandedge,
upper_bandedge,
min_wire_length,
max_wire_length,
metric_res)
my_avoids.append(avoid)
print('Avoidance for band:', band, avoid)
print()
print('Start Lengths', my_lengths)
for avoid in my_avoids:
print('Take away avoids', avoid)
my_lengths = _get_good_lenghts(my_lengths, avoid, metric_res)
print('New Lengths', my_lengths)
return my_lengths
###############################################################################
## EXTERNAL FUNCTIONS ##
###############################################################################
def main(bands_d, bands_l, minimum, maximum, margin, metric_res):
"""
bands_d = dictionary of bands, example:
BANDS_D = {
"160":{"low":1800, "high":2000},
"80" :{"low":3500, "high":3800},
"60" :{"low":5250, "high":5450},
"40" :{"low":7000, "high":7200},
"30" :{"low":10100, "high":10150},
"20" :{"low":14000, "high":14350},
"17" :{"low":18068, "high":18168},
"15" :{"low":21000, "high":21450},
"12" :{"low":24890, "high":24990},
"10" :{"low":28000, "high":29700},
"6" :{"low":50000, "high":54000}
}
bands_l = list of the keys of the dictionary of bands to run the
calculation on. This is either 1 of the keys, all of the keys
or a subset of keys
minimum = minimum wire length expressed as a minimum factor of the lowest
frequency wavelength
maximum = maximum wire length expressed as a maximum factor of the lowest
frequency wavelength
margin = band edge correction expressed as a percentage of band width
metric_res = digit precision expressed in number of digits
after decimal point
"""
my_lengths = []
my_lengths = _get_multi_band(bands_d,
bands_l,
minimum,
maximum,
margin,
metric_res)
return my_lengths
###############################################################################
## INTERACTIVE CODE ##
###############################################################################
if __name__ == '__main__':
minimum = MINIMUM
maximum = MAXIMUM
margin = MARGIN
bands_d = BANDS_D
metric_res = METRIC_RESOLUTION
bands_l = [key for key in bands_d]
if len(sys.argv) > 1:
# command line parameters are set
for arg in sys.argv[1:]:
# looping through the command line parameters
if '-b:' in arg:
# bands set in commandline
my_bands = arg.split('-b:')[1]
if my_bands.lower() == 'all':
# bands_l variable already defined
pass
elif '-' in my_bands:
# range of bands in commandline
bands = [str(b) for b in my_bands.split('-') if str(b) in bands_l]
if len(bands) == 2:
start = bands_l.index(bands[0])
stop = bands_l.index(bands[1]) + 1
bands_l = bands_l[start:stop]
elif ',' in my_bands:
# individual bands in commandline
if ',' in my_bands:
# multiple bands specified
bands = [ str(b) for b in my_bands.split(',') if str(b) in bands_l]
bands_l = bands
else:
# single band specified
bands_l = [my_bands]
if '-min:' in arg:
# minimum length set in commandline
minimum = float(arg.split('-min:')[1])
if '-max:' in arg:
# maximum length set in commandline
maximum = float(arg.split('-max:')[1])
if '-margin:' in arg:
# margin set in commandline
margin = float(arg.split('-margin:')[1])
if '-res:' in arg:
metric_res = int(arg.split('-res:')[1])
if '-range:' in arg:
# customer frequency range specified
low = int(arg.split('-range:')[1].split('-')[0])
high = int(arg.split('-range:')[1].split('-')[1])
bands_d = {"custom":{"low":low, "high":high}}
bands_l = ["custom"]
print()
print()
print('-------------------------------------------------------------------')
print('Random Longwire Antenna Calculation')
print('Input parameters:')
print('-------------------------------------------------------------------')
print('Bands : {}'.format(str(bands_l)))
if bands_l == ['custom']:
print('Range : {}-{} kHz'.format(str(bands_d['custom']['low']),
str(bands_d['custom']['high'])))
print('Minimum factor: {} (of lowest frequency)'.format(str(minimum).ljust(5)))
print('Maximum factor: {} (of lowest frequency)'.format(str(maximum).ljust(5)))
print('Margin : {} % (band edge adjustment)'.format(str(margin).ljust(5)))
print('-------------------------------------------------------------------')
print()
my_lengths = main(bands_d, bands_l, minimum, maximum, margin, metric_res)
print()
print('-------------------------------------------------------------------')
print(' Results:')
print('-------------------------------------------------------------------')
if len(my_lengths) == 0:
print('No wirelengths found with current parameters')
print('-------------------------------------------------------------------')
else:
for lengths in my_lengths:
if lengths != []:
print(' from {} to {} meters (mid {} m)'.format(str(lengths[0]).ljust(6),
str(lengths[1]).ljust(6),
str(round((lengths[0] + lengths[1])/2, metric_res)).ljust(6)))
###############################################################################
## END ##
###############################################################################