1- <!-- This is an experemental antenna calculation tool written largely by Generative AI. Use at your own risk. As this is written by Generative AI, I am disavowing any copyright on it. -->
21<!DOCTYPE html>
32< html lang ="en ">
43< head >
8483</ head >
8584< body >
8685 < h1 > Experimental Antenna Calculator</ h1 >
87- < p > This is an experemental antenna calculation tool written largely by Generative AI. Use at your own risk.</ p >
86+ < p > This is an experimental antenna calculation tool written largely by Generative AI. Use at your own risk.</ p >
8887 < div class ="section ">
8988 < h2 > Input Parameters</ h2 >
9089 < div id ="targetBands ">
@@ -499,7 +498,7 @@ <h2>Results</h2>
499498 const margin = 50 ;
500499 const plotWidth = canvas . width - 2 * margin ;
501500 const plotHeight = canvas . height - 2 * margin ;
502- const maxSWR = Math . max ( ...swrValues , 5 ) ; // Cap at 5 for visibility
501+ const maxSWR = Math . max ( ...swrValues , 10 ) ; // Cap at 10 for visibility
503502
504503 // Sort targetBands and swrValues together
505504 const sortedPairs = targetBands . map ( ( freq , index ) => [ freq , swrValues [ index ] ] )
@@ -517,7 +516,7 @@ <h2>Results</h2>
517516 ctx . lineTo ( x , canvas . height - margin ) ;
518517 ctx . stroke ( ) ;
519518 } ) ;
520- [ 1 , 2 , 3 , 4 , 5 ] . forEach ( swr => {
519+ [ 1 , 3 , 5 , 7 , 9 ] . forEach ( swr => {
521520 const y = canvas . height - margin - ( swr / maxSWR ) * plotHeight ;
522521 ctx . beginPath ( ) ;
523522 ctx . moveTo ( margin , y ) ;
@@ -553,7 +552,7 @@ <h2>Results</h2>
553552 ctx . stroke ( ) ;
554553 ctx . fillText ( freq . toFixed ( freq < 1 ? 3 : 1 ) , x , canvas . height - margin + 15 ) ;
555554 } ) ;
556- [ 1 , 2 , 3 , 4 , 5 ] . forEach ( swr => {
555+ [ 1 , 3 , 5 , 7 , 9 ] . forEach ( swr => {
557556 const y = canvas . height - margin - ( swr / maxSWR ) * plotHeight ;
558557 ctx . beginPath ( ) ;
559558 ctx . moveTo ( margin - 5 , y ) ;
@@ -622,7 +621,7 @@ <h2>Results</h2>
622621 return ;
623622 }
624623 if ( wireGauge < 6 || wireGauge > 30 ) {
625- alert ( 'Wire gauge should be between 6 and 30 AWG' ) ;
624+ alert ( 'Wire gauge should be between 6 WHOLE WIRE GAUGE HERE and 30 AWG' ) ;
626625 return ;
627626 }
628627
@@ -642,69 +641,87 @@ <h2>Results</h2>
642641 return `${ length . toFixed ( 2 ) } m (${ feet . toFixed ( 2 ) } ft, ${ inches . toFixed ( 2 ) } in)` ;
643642 }
644643
645- function estimateSWR ( lengths , freq , type , balunRatio , traps , designFreqs ) {
646- // For single-element antennas, use the single length and design frequency
647- const length = Array . isArray ( lengths ) ? lengths [ 0 ] : lengths ;
648- const designFreq = Array . isArray ( designFreqs ) ? designFreqs [ 0 ] : designFreqs ;
644+ function estimateSWR ( lengths , freq , type , balunRatio , traps , designFreqs , velocityFactor ) {
649645 const isMultiElement = Array . isArray ( lengths ) && lengths . length > 1 ;
646+ const singleLength = isMultiElement ? lengths : [ lengths ] ;
647+ const singleType = isMultiElement ? type : [ type ] ;
648+ const singleDesignFreq = isMultiElement ? designFreqs : [ designFreqs ] ;
649+ const c = 299792458 ; // Speed of light in m/s
650650
651- // Base SWR from resonance
652651 let baseSWR = 1 ;
653- if ( isMultiElement ) {
654- // Multi-element: find closest resonant frequency
655- const deviations = designFreqs . map ( ( df , i ) => {
656- const idealLength = type [ i ] === 'quarterVertical' ?
657- ( speedOfLight / ( df * 1000000 ) ) * 0.25 * velocityFactor :
658- ( speedOfLight / ( df * 1000000 ) ) * 0.5 * velocityFactor ;
659- return Math . abs ( lengths [ i ] - idealLength ) / idealLength ;
660- } ) ;
661- const minDeviation = Math . min ( ...deviations ) ;
662- baseSWR = 1 + minDeviation * 10 ;
663- } else {
664- const idealLength = type === 'quarterVertical' ?
665- ( speedOfLight / ( designFreq * 1000000 ) ) * 0.25 * velocityFactor :
666- ( speedOfLight / ( designFreq * 1000000 ) ) * 0.5 * velocityFactor ;
667- const deviation = Math . abs ( length - idealLength ) / idealLength ;
668- baseSWR = 1 + deviation * 10 ;
669- }
670652
671- // Balun factor
672- let balunFactor = 1 ;
673- if ( useBalun && balunRatio ) {
674- const ratios = { '1:1' : 1 , '4:1' : 4 , '9:1' : 9 } ;
675- const impedanceFactor = isMultiElement ? 1 :
676- ( type === 'foldedDipole' ? 4 :
677- ( type === 'offCenterDipole' ? 4 :
678- ( type === 'endFed' || type === 'randomWire' ? 9 : 1 ) ) ) ;
679- balunFactor = Math . abs ( ratios [ balunRatio ] - impedanceFactor ) / impedanceFactor + 1 ;
680- }
681-
682- // Trap factor
683- let trapFactor = 1 ;
684- if ( useTraps && traps . length > 0 ) {
653+ if ( singleType [ 0 ] === 'randomWire' ) {
654+ // Random wire: SWR depends on length and frequency, with multiple resonances
655+ const lambda = c / ( freq * 1e6 ) * velocityFactor ;
656+ const lengthInWavelengths = singleLength [ 0 ] / lambda ;
657+ // Avoid lengths near multiples of 0.5 wavelengths (high impedance)
658+ const fractionalPart = lengthInWavelengths - Math . floor ( lengthInWavelengths ) ;
659+ if ( Math . abs ( fractionalPart - 0.5 ) < 0.05 ) {
660+ baseSWR = 10 ; // Very high SWR near half-wave multiples
661+ } else if ( fractionalPart < 0.25 || fractionalPart > 0.75 ) {
662+ baseSWR = 2 + 5 * Math . abs ( fractionalPart - 0.5 ) ; // Lower SWR near quarter-wave multiples
663+ } else {
664+ baseSWR = 5 + 5 * Math . abs ( fractionalPart - 0.5 ) ; // Moderate SWR elsewhere
665+ }
666+ } else if ( ! useTraps || traps . length === 0 ) {
667+ // Resonant antennas without traps
668+ if ( isMultiElement ) {
669+ // Fan dipole or hybrid: minimum SWR across all elements
670+ let minSWR = Infinity ;
671+ singleDesignFreq . forEach ( ( df , i ) => {
672+ const idealLength = singleType [ i ] === 'quarterVertical' ?
673+ ( c / ( df * 1e6 ) ) * 0.25 * velocityFactor :
674+ ( c / ( df * 1e6 ) ) * 0.5 * velocityFactor ;
675+ const freqDeviation = Math . abs ( freq - df ) / df ;
676+ const bwFactor = singleType [ i ] === 'foldedDipole' ? 0.1 : 0.05 ; // Folded dipole has wider bandwidth
677+ const elementSWR = 1 + ( freqDeviation / bwFactor ) ** 2 * 9 ; // Quadratic rise, max 10
678+ minSWR = Math . min ( minSWR , elementSWR ) ;
679+ } ) ;
680+ baseSWR = minSWR ;
681+ } else {
682+ // Single resonant antenna
683+ const designFreq = singleDesignFreq [ 0 ] ;
684+ const idealLength = singleType [ 0 ] === 'quarterVertical' ?
685+ ( c / ( designFreq * 1e6 ) ) * 0.25 * velocityFactor :
686+ ( c / ( designFreq * 1e6 ) ) * 0.5 * velocityFactor ;
687+ const freqDeviation = Math . abs ( freq - designFreq ) / designFreq ;
688+ const bwFactor = singleType [ 0 ] === 'foldedDipole' ? 0.1 : 0.05 ; // Wider bandwidth for folded dipole
689+ baseSWR = 1 + ( freqDeviation / bwFactor ) ** 2 * 9 ; // Quadratic rise, max 10
690+ }
691+ } else {
692+ // With traps: SWR rises sharply above trap frequencies
693+ let trapFactor = 1 ;
685694 traps . forEach ( trapFreq => {
686- const freqRatio = freq / trapFreq ;
687- if ( freqRatio > 1 ) {
688- // Sharp rise above trap frequency, peaking at 10x base SWR within 5%
689- const proximity = Math . min ( 1 , Math . max ( 0 , ( freqRatio - 1 ) / 0.05 ) ) ;
690- trapFactor += 9 * proximity ; // Adds up to 9 at 5% above trap
695+ if ( freq > trapFreq ) {
696+ const freqRatio = ( freq - trapFreq ) / trapFreq ;
697+ trapFactor = Math . min ( 10 , 1 + 50 * freqRatio ) ; // Sharp rise, max 10
691698 }
692699 } ) ;
700+ if ( isMultiElement ) {
701+ let minSWR = Infinity ;
702+ singleDesignFreq . forEach ( ( df , i ) => {
703+ const freqDeviation = Math . abs ( freq - df ) / df ;
704+ const elementSWR = 1 + ( freqDeviation / 0.05 ) ** 2 * 9 ;
705+ minSWR = Math . min ( minSWR , elementSWR ) ;
706+ } ) ;
707+ baseSWR = minSWR * trapFactor ;
708+ } else {
709+ const designFreq = singleDesignFreq [ 0 ] ;
710+ const freqDeviation = Math . abs ( freq - designFreq ) / designFreq ;
711+ baseSWR = ( 1 + ( freqDeviation / 0.05 ) ** 2 * 9 ) * trapFactor ;
712+ }
693713 }
694714
695- // Resonance dips for multi-element
696- let resonanceFactor = 1 ;
697- if ( isMultiElement ) {
698- designFreqs . forEach ( df => {
699- const freqDiff = Math . abs ( freq - df ) / df ;
700- if ( freqDiff < 0.05 ) {
701- // Dip towards 1 near resonant frequency
702- resonanceFactor = Math . min ( resonanceFactor , 1 + ( freqDiff / 0.05 ) * ( baseSWR - 1 ) ) ;
703- }
704- } ) ;
715+ // Balun mismatch factor
716+ let balunFactor = 1 ;
717+ if ( useBalun && balunRatio ) {
718+ const ratios = { '1:1' : 1 , '4:1' : 4 , '9:1' : 9 } ;
719+ const expectedRatio = singleType [ 0 ] === 'foldedDipole' || singleType [ 0 ] === 'offCenterDipole' ? 4 :
720+ ( singleType [ 0 ] === 'endFed' || singleType [ 0 ] === 'randomWire' ? 9 : 1 ) ;
721+ balunFactor = 1 + Math . abs ( ratios [ balunRatio ] - expectedRatio ) / expectedRatio ;
705722 }
706723
707- return Math . max ( 1 , baseSWR * balunFactor * trapFactor * resonanceFactor ) ;
724+ return Math . max ( 1 , Math . min ( 10 , baseSWR * balunFactor ) ) ; // Cap SWR at 10
708725 }
709726
710727 function determineBalunRatio ( type ) {
@@ -774,7 +791,7 @@ <h2>Results</h2>
774791 } ) ;
775792 }
776793 if ( balunRatio ) results . push ( `- Balun (${ balunRatio } ): At center feedpoint` ) ;
777- swrValues = targetBands . map ( f => estimateSWR ( length , f , 'dipole' , balunRatio , trapFrequencies , freq ) ) ;
794+ swrValues = targetBands . map ( f => estimateSWR ( length , f , 'dipole' , balunRatio , trapFrequencies , freq , velocityFactor ) ) ;
778795
779796 calcResults . push ( `<b>Element 1: Half-Wave Dipole at ${ freq } MHz</b>` ) ;
780797 calcResults . push ( `- Wavelength (λ) = c / f = ${ speedOfLight } / (${ freq } × 10⁶) = ${ ( speedOfLight / ( freq * 1000000 ) ) . toFixed ( 2 ) } m` ) ;
@@ -795,7 +812,7 @@ <h2>Results</h2>
795812 } ) ;
796813 }
797814 if ( balunRatio ) results . push ( `- Balun (${ balunRatio } ): At base feedpoint` ) ;
798- swrValues = targetBands . map ( f => estimateSWR ( length * 2 , f , 'quarterVertical' , balunRatio , trapFrequencies , freq ) ) ;
815+ swrValues = targetBands . map ( f => estimateSWR ( length , f , 'quarterVertical' , balunRatio , trapFrequencies , freq , velocityFactor ) ) ;
799816
800817 calcResults . push ( `<b>Element 1: Quarter-Wave Vertical at ${ freq } MHz</b>` ) ;
801818 calcResults . push ( `- Wavelength (λ) = c / f = ${ speedOfLight } / (${ freq } × 10⁶) = ${ ( speedOfLight / ( freq * 1000000 ) ) . toFixed ( 2 ) } m` ) ;
@@ -818,7 +835,7 @@ <h2>Results</h2>
818835 } ) ;
819836 }
820837 if ( balunRatio ) results . push ( `- Balun (${ balunRatio } ): At top center feedpoint` ) ;
821- swrValues = targetBands . map ( f => estimateSWR ( length , f , 'foldedDipole' , balunRatio , trapFrequencies , freq ) ) ;
838+ swrValues = targetBands . map ( f => estimateSWR ( length , f , 'foldedDipole' , balunRatio , trapFrequencies , freq , velocityFactor ) ) ;
822839
823840 calcResults . push ( `<b>Element 1: Folded Dipole at ${ freq } MHz</b>` ) ;
824841 calcResults . push ( `- Wavelength (λ) = c / f = ${ speedOfLight } / (${ freq } × 10⁶) = ${ ( speedOfLight / ( freq * 1000000 ) ) . toFixed ( 2 ) } m` ) ;
@@ -838,7 +855,7 @@ <h2>Results</h2>
838855 } ) ;
839856 }
840857 if ( balunRatio ) results . push ( `- UnUn (${ balunRatio } ): At end feedpoint` ) ;
841- swrValues = targetBands . map ( f => estimateSWR ( length , f , 'endFed' , balunRatio , trapFrequencies , freq ) ) ;
858+ swrValues = targetBands . map ( f => estimateSWR ( length , f , 'endFed' , balunRatio , trapFrequencies , freq , velocityFactor ) ) ;
842859
843860 calcResults . push ( `<b>Element 1: End-Fed Wire at ${ freq } MHz</b>` ) ;
844861 calcResults . push ( `- Wavelength (λ) = c / f = ${ speedOfLight } / (${ freq } × 10⁶) = ${ ( speedOfLight / ( freq * 1000000 ) ) . toFixed ( 2 ) } m` ) ;
@@ -861,7 +878,7 @@ <h2>Results</h2>
861878 } ) ;
862879 }
863880 if ( balunRatio ) results . push ( `- UnUn (${ balunRatio } ): At end feedpoint` ) ;
864- swrValues = targetBands . map ( f => estimateSWR ( length , f , 'randomWire' , balunRatio , trapFrequencies , targetBands [ 0 ] ) ) ;
881+ swrValues = targetBands . map ( f => estimateSWR ( length , f , 'randomWire' , balunRatio , trapFrequencies , targetBands [ 0 ] , velocityFactor ) ) ;
865882
866883 calcResults . push ( `<b>Element 1: Random Wire</b>` ) ;
867884 calcResults . push ( `- User-Specified Length: ${ length . toFixed ( 2 ) } m` ) ;
@@ -882,7 +899,7 @@ <h2>Results</h2>
882899 } ) ;
883900 }
884901 if ( balunRatio ) results . push ( `- Balun (${ balunRatio } ): At off-center feedpoint` ) ;
885- swrValues = targetBands . map ( f => estimateSWR ( length , f , 'offCenterDipole' , balunRatio , trapFrequencies , freq ) ) ;
902+ swrValues = targetBands . map ( f => estimateSWR ( length , f , 'offCenterDipole' , balunRatio , trapFrequencies , freq , velocityFactor ) ) ;
886903
887904 calcResults . push ( `<b>Element 1: Off-Center Fed Dipole at ${ freq } MHz</b>` ) ;
888905 calcResults . push ( `- Wavelength (λ) = c / f = ${ speedOfLight } / (${ freq } × 10⁶) = ${ ( speedOfLight / ( freq * 1000000 ) ) . toFixed ( 2 ) } m` ) ;
@@ -910,7 +927,7 @@ <h2>Results</h2>
910927 } ) ;
911928 }
912929 if ( balunRatio ) results . push ( `- Balun (${ balunRatio } ): At common center feedpoint` ) ;
913- swrValues = targetBands . map ( f => estimateSWR ( lengths , f , types , balunRatio , trapFrequencies , frequencies ) ) ;
930+ swrValues = targetBands . map ( f => estimateSWR ( lengths , f , types , balunRatio , trapFrequencies , frequencies , velocityFactor ) ) ;
914931 } else if ( antennaType === 'hybrid' ) {
915932 const hybridTypes = [ ] ;
916933 for ( let i = 0 ; i < hybridElementCount ; i ++ ) {
@@ -958,7 +975,7 @@ <h2>Results</h2>
958975 } ) ;
959976 }
960977 if ( balunRatio ) results . push ( `- Balun (${ balunRatio } ): At common feedpoint` ) ;
961- swrValues = targetBands . map ( f => estimateSWR ( lengths , f , types , balunRatio , trapFrequencies , frequencies ) ) ;
978+ swrValues = targetBands . map ( f => estimateSWR ( lengths , f , types , balunRatio , trapFrequencies , frequencies , velocityFactor ) ) ;
962979 }
963980
964981 if ( useTraps && trapFrequencies . length > 0 ) {
0 commit comments