Imported Upstream version 4.6.0
[debian/atlc] / src / non_gui / design_coupler.c
1 /* atlc - arbitrary transmission line calculator, for the analysis of
2 transmission lines are directional couplers. 
3
4 Copyright (C) 2002. Dr. David Kirkby, PhD (G8WRB).
5
6 This program is free software; you can redistribute it and/or
7 modify it under the terms of the GNU General Public License
8 as published by the Free Software Foundation; either package_version 2
9 of the License, or (at your option) any later package_version.
10
11 This program is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14 GNU General Public License for more details.
15
16 You should have received a copy of the GNU General Public License
17 along with this program; if not, write to the Free Software
18 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307,
19 USA.
20
21 Dr. David Kirkby, e-mail drkirkby at ntlworld.com 
22
23 */
24
25 #include "config.h"
26
27 #include "definitions.h"
28 #include "exit_codes.h"
29
30 #ifdef HAVE_STDLIB_H
31 #include <stdlib.h>
32 #endif
33
34 #ifdef SYS_TYPES_H
35 #include <sys/types.h>
36 #endif
37
38 #ifdef HAVE_STRINGS_H
39 #include <strings.h>
40 #endif
41
42 #ifdef HAVE_STRING_H
43 #include <string.h>
44 #endif
45
46 #ifdef HAVE_TIME_H
47 #include <time.h>
48 #endif
49
50 #ifdef HAVE_MATH_H
51 #include <math.h>
52 #endif
53
54
55 extern int errno;
56 int verbose=2;
57
58 /* desgin_coupler does two very different things in the one program
59 1) Given a frequency range, the required coupling factor, it calcuates
60 the odd and even mode impedances needed for a coupler. It does this
61 assuming the length of the coupler is lambda/4, although you can vary
62 that on the command line with the -l option. 
63
64 2) Once the optimal values for the even and odd mode impedances are
65 found, it itteratively looks up the odd and even mode impedances for two
66 think lines of various widths (w) and spacings (s), looking for the
67 combination that gives the best rms error between the required
68 impedances and those that will result with the coupler design as
69 presented. 
70
71 It is assumed by default that the height of the box is of one unit (1
72 mm, 1" etc, but this may be changed on the command line. This will scale
73 the parameters w and s by the same multiple.
74
75 */
76
77 int main(int argc, char **argv) /* Read parameters from command line */
78 {
79   int q, Hflag=FALSE;
80   int calculate_physical_dimensions=FALSE;
81   int calculate_physical_dimensions_to_high_accuracy=FALSE;
82   double er;
83   double Zo=-1, length=-1, fmin, fmax, fmean, fstep=-1, cf,  Zodd, Zeven; 
84   double f, vcf, vcf_for_quarter_wave_line, w, s, error, error_max=1e30;
85   double wanted_coupling_factor_in_dB, step=0.02, fq;
86   double Zeven_x=-1, Zodd_x=-1, best_s=-1, best_w=-1;
87   double height_of_box=1.0;
88   double best_Zodd=-1, best_Zeven=-1, best_Zo=-1;
89
90   /* SGI's MipsPro compiler is very fussy. The following line, along
91   with one right at the end, forces Zo_x to be set and used,
92   alhtough it serves no other userful purpose but to keep the 
93   compiler happy */
94   double Zo_x=1;
95   while((q=get_options(argc,argv,"DeQqdCL:s:Z:H:")) != -1)
96   switch (q) 
97   {
98     case 'd':
99     calculate_physical_dimensions=TRUE;
100     break;
101     case 'D':
102     calculate_physical_dimensions=TRUE;
103     calculate_physical_dimensions_to_high_accuracy=TRUE;
104     break;
105     case 'e':
106     give_examples_of_using_design_coupler();
107     break;
108     case 'C':
109     print_copyright((char *) "2002");
110     Hflag=TRUE;
111     exit_with_msg_and_exit_code("",OKAY);
112     break;
113     case 'L':    
114     length=atof(my_optarg); /* Sets the length of coupler */
115     break;
116     case 'H':
117     height_of_box=atof(my_optarg); /* Set height of coupler's enclosure */
118     Hflag=TRUE;
119     break;
120     case 's': /* Set frequncy steps in which coupling is computed */
121     fstep=atof(my_optarg);
122     break;
123     case 'Z': /* Set the characteristic impedance - default is 50 Ohms */
124     Zo=atof(my_optarg);
125     break;
126     case 'q': /* Run in quite mode, giving less output */
127     verbose--;
128     break;
129     case '?':
130       usage_design_coupler();
131     break;
132   } /* End of the switch statement */
133
134   if(argc-my_optind != 3)  /* This should be so hopefully !! */
135   {
136     usage_design_coupler();
137     exit_with_msg_and_exit_code("",PROGRAM_CALLED_WITH_WRONG_NUMBER_OF_ARGUMENTS);
138   }
139   wanted_coupling_factor_in_dB=atof(argv[my_optind]);
140   fmin=atof(argv[my_optind+1]);
141   fmax=atof(argv[my_optind+2]);
142   fmean=(fmin+fmax)/2.0;
143   if(fstep <0 )
144     fstep=(fmax-fmin)/4.0;
145   
146   if (wanted_coupling_factor_in_dB <= 0.0 ) /* Only 0 can happen */
147   {
148     /* I don't think this can happen unless the user enter 0 as the
149     first parameter, as a negative number entered will be taken as a
150     command line option */
151     fprintf(stderr,"\nThe coupled power must be less than the input power.");
152     fprintf(stderr," But please enter a\n*positive* number in dB for the");
153     fprintf(stderr," first command line parameter. If you want a \ncoupler");
154     fprintf(stderr," with a coupled port that is 12 dB down on the input");
155     fprintf(stderr," power, covering\n144-146 MHz, enter this as:\n\n");
156     fprintf(stderr,"design_coupler 12 144 146\n\n");
157     fprintf(stderr,"If you want the physical dimensions of the coupler");
158     fprintf(stderr," designed for you, add the\n-d option on the command");
159     fprintf(stderr," line, like this:\n\ndesign_coupler -q -12 144 146\n\n");
160     fprintf(stderr,"If you run design_coupler with no command line");
161     fprintf(stderr," arguments, like this:\n\ndesign_coupler\n\n");
162     fprintf(stderr,"then design_coupler will print some information,");
163     fprintf(stderr,"  showing *all* the options. \nIf you run design_coupler");
164     fprintf(stderr," with the -e option like this:\n\n");
165     fprintf(stderr,"design_coupler -e\n\n");
166     fprintf(stderr,"lots of examples will be shown of the correct usage.\n");
167     exit_with_msg_and_exit_code("",IMPOSSIBLE_COUPLING_COEFFICIENT);
168   } 
169   if (fmax <= fmin)
170   {
171     fprintf(stderr,"The second command line argumentent you gave, which");
172     fprintf(stderr," is for the *minimum*\noperating frequenncy in MHz,");
173     fprintf(stderr," is less than the third argument, which is the\n");
174     fprintf(stderr,"*maximum* operating frequency in MHz.\n\n");
175     fprintf(stderr,"If you want a coupler");
176     fprintf(stderr," with a coupled port that is 12 dB down on the input\n");
177     fprintf(stderr,"power, covering 144-146 MHz, enter this as:\n\n");
178     fprintf(stderr,"design_coupler 12 144 146\n\n");
179     fprintf(stderr,"If you want the physical dimensions of the coupler");
180     fprintf(stderr," designed for you, add the\n-d option on the command");
181     fprintf(stderr," line, like this:\n\ndesign_coupler -d 12 144 146\n\n");
182     fprintf(stderr,"If you run design_coupler with no command line arguments,");
183     fprintf(stderr," then design_coupler\nwill print some information,");
184     fprintf(stderr," showing *all* the options. If you run\ndesign_coupler");
185     fprintf(stderr," with the -e option like this:\n\n");
186     fprintf(stderr,"design_coupler -e\n\n");
187     fprintf(stderr,"lots of examples will be shown of the correct usage.\n");
188     fprintf(stderr,"Exiting ...\n");
189     exit_with_msg_and_exit_code("",FMAX_NOT_ABOVE_FMIN);
190   }
191   if (Zo < 0.0)
192     Zo=50.0; 
193   if(length<0.0)
194     length=75.0/fmean;  /* By default, make it a quarter wave long */
195   /* The following sent in an email by Paul AA1L, sums the theory up 
196   You make Zo=50=sqrt(Zoo*Zoe) and
197   c=(Zoe-Zoo)/(Zoe+Zoo), c being the voltage coupling coefficient.
198   I.e., for a 20dB coupler c=0.1 is the midband
199   coupling.  
200   Coupling varies as sin^2(f/fq), fq being frequency where the coupled
201   length is a quarter wave.  
202
203   HOWEVER, the above is not quite the full story, as that says coupling
204   peaks at sin(1), when in fact its sin(Pi/2)
205   */
206   
207   /* vfc stands for 'voltage coupling factor' */
208
209   /* I need to find values for Zodd and Zeven to use, but first convert
210   the coupling factor on the command line into the voltage coupling
211   factor c */
212   
213   /* vfc stands for 'voltage coupling factor' */
214
215   /* When the line is a quarter wave, one can get any amount of coupling
216   you want, including a vfc of 1, in which case all the power transfers
217   to the coupled port. Normally, the vcf will be less than 1.0. for a 20
218   dB couplier is it 0.1 */
219
220   vcf_for_quarter_wave_line=1.0/pow(10.0,wanted_coupling_factor_in_dB/20.0); 
221
222   fq=75/length; /* frequency at which line is a quarter wave long */
223
224   /* If the line is less than a quarter wave long, then less power is
225   coupled, so to compensate we need to increase the voltage coupling
226   factor 'vcf above that of the value for a quarter wave line.
227   Since the 'vcf' varies as sin(0.5 *PI * f/fq)^2, where 
228   fq is the frequency at which the line is a quarter-wave long, we must
229   divide the vcf_for_quarter_wave_line by sin(0.5 *PI*f/fq)^2 to get
230   the required vcf. */
231   vcf=vcf_for_quarter_wave_line*(1.0/sin(0.5*M_PI*fmean/fq));
232   /* Check that the voltage coupling factor does not exceed one */
233   if ( vcf > 1.0 )
234   {
235     fprintf(stderr,"\n*****ERROR****\n");
236     fprintf(stderr,"Sorry, you can't make a %6.3f dB coupler with a coupled line of %7.4f m long.\n",wanted_coupling_factor_in_dB, length);
237     fprintf(stderr,"Either couple off a smaller fraction of the main power to the coupled port,\n");
238     fprintf(stderr,"or make the line closer to an odd multiple of a quarter wave.\n");
239     fprintf(stderr,"Odd mulitples of a quarter wave are: %.4f, %.4f, %.4f, %.4f .. m\n", 75/fmean, 3*75/fmean, 5*75/fmean, 7*75/fmean);
240     exit_with_msg_and_exit_code("",IMPOSSIBLE_TO_MAKE_COUPLER_THAT_LENGTH);
241   }
242
243   /* After mucking around with Mathematica a bit, I found it was
244   possible to invert the equations */
245   
246   Zodd = sqrt(1-vcf)*Zo/sqrt(1+vcf);
247   Zeven=Zo*Zo/Zodd;
248
249   printf("\nFor a %.3f dB %.3f Ohm coupler with a length of %.4f m,\n",wanted_coupling_factor_in_dB, Zo, length);
250   printf("you need to have an odd-mode impedance Zodd of %.3f Ohms and\n",Zodd);
251   printf("an even mode impedance Zeven of %.3f Ohms\n\n",Zeven);
252   if(verbose >=1) /* Only print if user does not specifiy and -qq options */
253   {
254     printf("%.3f dB down <-- ************************** ---> %3.3f Ohm termination\n\n",wanted_coupling_factor_in_dB,Zo);
255     printf("Drive this port --> ************************** ---> %3.3f Ohm termination\n",Zo);
256     printf("                    <------- %8.4f m ----->\n",length);
257     printf("\nDrive Port 1, coupler out of port 2 and terminate the other ports in Zo\n");
258     printf("Such a coupler will have the response indicated below.\n\n");
259   /*printf("length =%.4f mean=%.3f vcf=%.3f vcf_for_quarter_wave_line=%.3f \n",length, fmean, vcf, vcf_for_quarter_wave_line);*/
260   }
261   for(f=fmin; f<=fmax; f+=fstep)
262   {
263     cf=20*log10(vcf*sin(0.5*M_PI*f/fq)); /* This is what is now needed for some given length (and so fq) */
264     if(verbose == 2)
265       printf("f = %7.3f MHz   coupling is %.3f dB down on the main arm\n",f,cf);
266   }
267   printf("\nYou may force the length to be any value you want using the -L option - it does\nnot have to be %.4f metres long\n",length);
268   if(calculate_physical_dimensions==FALSE)
269   {
270     printf("You may try to find a coupler with these dimensions using the -d option\n\n");
271     printf("Currently the -d option is not that fast, as it uses a brain-dead algorithm\n");
272     printf("Hopefully one day the algorithm will be speeded up.\n");
273   }
274   if(calculate_physical_dimensions==TRUE)
275   {
276     er=1.0;
277     printf("Please be patient - this will take a few minutes or so\n");
278     for(s = 0.02; s<=100; s+=step)
279     {
280       for(w = 0.02; w<= 11.0; w += step)
281       {
282         /* Results are calculated assuming the box is one unit (mm, inch
283         etc) high and later scaled */
284
285         calculate_Zodd_and_Zeven(&Zodd_x, &Zeven_x, &Zo_x, w, 1.0, s, er);
286         error=pow(Zodd-Zodd_x,2.0) + pow(Zeven-Zeven_x,2.0);
287         if( error < error_max )
288         {
289           best_s=s; 
290           best_w=w; 
291           best_Zo=sqrt(best_Zo * best_Zeven);
292           best_Zodd=Zodd;
293           best_Zeven=Zeven;
294           error_max=error;
295         }
296       }
297     }
298     printf("w = %.4f s = %.4f which gives Zo = %.4f Zodd = %.4f Zeven = %.4f\n",best_w, best_s, best_Zo, best_Zodd, best_Zeven);
299     /* Now try to get closer, if -D option given */
300     if (calculate_physical_dimensions_to_high_accuracy == TRUE)
301     {
302       for(s = best_s-step; s<=best_s+step; s+=step/1000)
303       {
304         for(w = best_w-step; w<= best_w+step; w += step/1000)
305         {
306           calculate_Zodd_and_Zeven(&Zodd_x, &Zeven_x, &Zo_x, w, 1.0, s, er);
307           error=fabs(Zodd-Zodd_x) + fabs(Zeven-Zeven_x);
308           if( error < error_max )
309           {    
310             best_s=s; 
311             best_w=w; 
312             best_Zodd=Zodd;
313             best_Zeven=Zeven;
314             error_max=error;
315           }
316         }
317       }
318     }
319     best_Zo=sqrt(best_Zodd * best_Zeven);
320     if(verbose <= 0)
321     {
322       printf("|-----------^------------------------------------------------------------------|\n");
323       printf("|           |                                                                  |\n");
324       printf("|           |              <---w---><-----s----><---w-->                       |\n");
325       printf("|           H              ---------            --------                       |\n");
326       printf("|           |                                                                  |\n");
327       printf("|           |   Er=1.0 (air)                                                   |\n");
328       printf("------------v------------------------------------------------------------------\n");
329       printf("<-----------------------------------------W----------------------------------->\n");
330     }
331     printf("H =%.4f w = %.4f s = %.4f\n",height_of_box, height_of_box*best_w, height_of_box*best_s);
332     printf("W must be *at least* %.4f, but larger does not matter.\n",5*height_of_box+ 2*best_w*height_of_box + height_of_box*best_s);
333     printf("These dimensions give Zo = %.4f Zodd = %.4f Zeven = %.4f Ohms\n", best_Zo, best_Zodd, best_Zeven);
334     if(Hflag==FALSE)
335     {
336       printf("****NOTE ****\n");
337       printf("Although H is shown as 1.0, it can be 1 mm, 1 cm or even 1 m. It is important\n");
338       printf("that w is %.4f times whatever H is, and that s is %.4f times whatever H is, but the absolute numbers are irrelavant.\n",best_w, best_s);
339       printf("If you know the height H of your enclosure, use the -H option to indicate\n");
340       printf("its value. This will ensure all the dimensions are scaled automatically for you.\n"); 
341     }
342     printf("****NOTE 2****\n");
343     printf("The length *must* be %.4f m if you use these dimensions for W, H, w and s.\n",length); 
344     printf("If %.4f m is inconvenient, change it with the -L option and recalculate\n to get new values of W, H, w and s\n",length);
345     printf("See: http://atlc.sourceforge.net\n");
346     printf("See: http://atlc.sourceforge.net/couplers.html\n");
347   }
348   return(OKAY);
349 }