1 /*
2  *  xnec2c - GTK2-based version of nec2c, the C translation of NEC2
3  *
4  *  This program is free software; you can redistribute it and/or modify
5  *  it under the terms of the GNU General Public License as published by
6  *  the Free Software Foundation; either version 2 of the License, or
7  *  (at your option) any later version.
8  *
9  *  This program is distributed in the hope that it will be useful,
10  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
11  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  *  GNU Library General Public License for more details.
13  *
14  *  You should have received a copy of the GNU General Public License
15  *  along with this program; if not, write to the Free Software
16  *  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
17  */
18 
19 /* plot_freqdata.c
20  *
21  * Code for plotting graphs of structure
22  * data (VSWR, impedance, gain etc)
23  */
24 
25 /*
26  * Net gain added by Mark Whitis http://www.freelabs.com/~whitis/
27  * References:
28  *   http://www.digitalhome.ca/forum/showpost.php?p=744018&postcount=47
29  *      NetGain = RawGain+10*log(Feed-pointGain)
30  *      where Feed-point Gain = 4*Zr*Zo/((Zr+Zo)^2+Zi^2)
31  *   http://www.avsforum.com/avs-vb/showthread.php?p=14086104#post14086104
32  *      NetGain = RawGain+10*log(4*Zr*Zo/((Zr+Zo)^2+Zi^2)
33  *   Where log is log10.
34  */
35 
36 #include "plot_freqdata.h"
37 #include "shared.h"
38 
39 /* Graph plot bounding rectangle */
40 static GdkRectangle plot_rect;
41 
42 /* Frequency scale max, min, num of values */
43 static double max_fscale, min_fscale;
44 static int nval_fscale;
45 
46 /*-----------------------------------------------------------------------*/
47 
48 /* Plot_Frequency_Data()
49  *
50  */
51   void
Plot_Frequency_Data(void)52 Plot_Frequency_Data( void )
53 {
54   /* Abort plotting if main window is to be closed
55    * or when plots drawing area not available */
56   if(
57 	  isFlagSet(MAIN_QUIT) ||
58 	  isFlagClear(PLOT_ENABLED) ||
59 	  isFlagClear(ENABLE_EXCITN) )
60 	return;
61 
62   /* Titles for plots */
63   char *titles[3];
64 
65   int
66 	idx,
67 	posn,  /* Position num of plot in drawingarea */
68 	fstep; /* Freq step number */
69 
70   static double
71 	*gmax     = NULL, /* Max gain buffer */
72 	*vgain    = NULL, /* Viewer direction gain buffer */
73 	*netgain  = NULL, /* Viewer direction net gain buffer */
74 	*gdir_tht = NULL, /* Direction in theta of gain */
75 	*gdir_phi = NULL, /* Direction in phi of gain */
76 	*fbratio  = NULL; /* Front to back ratio */
77 
78   /* Used to calculate net gain */
79   double Zr, Zo, Zi;
80 
81   /* Cairo context */
82   cairo_t *cr = gdk_cairo_create( freqplots_pixmap );
83 
84   /* Clear pixmap */
85   cairo_set_source_rgb( cr, BLACK );
86   cairo_rectangle(
87 	  cr, 0.0, 0.0,
88 	  (double)freqplots_pixmap_width,
89 	  (double)freqplots_pixmap_height );
90   cairo_fill( cr );
91 
92   /* Abort if plotting is not possible */
93   if( (calc_data.fstep < 1)				||
94 	  (isFlagClear(FREQ_LOOP_RUNNING)	&&
95 	   isFlagClear(FREQ_LOOP_DONE))		||
96 	  (isFlagClear(PLOT_GMAX)			&&
97 	   isFlagClear(PLOT_GVIEWER)		&&
98 	   isFlagClear(PLOT_VSWR)			&&
99 	   isFlagClear(PLOT_ZREAL_ZIMAG)	&&
100 	   isFlagClear(PLOT_ZMAG_ZPHASE)) )
101   {
102 	/* Render pixmap to screen */
103 	gdk_window_set_back_pixmap(
104 		freqplots_drawingarea->window, freqplots_pixmap, FALSE );
105 	gdk_window_clear( freqplots_drawingarea->window );
106 	cairo_destroy( cr );
107 	return;
108   }
109 
110   /* Fit frequency range to scale */
111   min_fscale  = (double)save.freq[0];
112   if( isFlagSet(FREQ_LOOP_RUNNING) )
113 	max_fscale = (double)save.freq[calc_data.fstep];
114   else
115 	max_fscale = (double)save.freq[calc_data.lastf];
116   nval_fscale = freqplots_pixmap_width / 75;
117   Fit_to_Scale( &max_fscale, &min_fscale, &nval_fscale );
118 
119   /* Graph position */
120   posn = 0;
121 
122   /* Limit freq stepping to last freq step */
123   fstep = calc_data.lastf + 1;
124 
125   /* Plot max gain vs frequency, if possible */
126   if( isFlagSet(PLOT_GMAX) && isFlagSet(ENABLE_RDPAT) )
127   {
128 	int nth, nph, pol;
129 	gboolean no_fbr;
130 
131 	/* Allocate max gmax and directions */
132 	size_t mreq = (size_t)fstep * sizeof(double);
133 	mem_realloc( (void **)&gmax,     mreq, "in plot_freqdata.c" );
134 	mem_realloc( (void **)&gdir_tht, mreq, "in plot_freqdata.c" );
135 	mem_realloc( (void **)&gdir_phi, mreq, "in plot_freqdata.c" );
136 	mem_realloc( (void **)&fbratio,  mreq, "in plot_freqdata.c" );
137 
138 	if( isFlagSet(PLOT_NETGAIN) )
139 	  mem_realloc( (void **)&netgain, mreq, "in plot_freqdata.c" );
140 
141 	/* Find max gain and direction, F/B ratio */
142 	no_fbr = FALSE;
143 
144 	/* Polarization type and impedance */
145 	pol = calc_data.pol_type;
146 	Zo = calc_data.zo;
147 
148 	/* When freq loop is done, calcs are done for all freq steps */
149 	for( idx = 0; idx < fstep; idx++ )
150 	{
151 	  double fbdir;
152 	  int fbidx, mgidx;
153 
154 	  /* Index to gtot buffer where max gain
155 	   * occurs for given polarization type */
156 	  mgidx = rad_pattern[idx].max_gain_idx[pol];
157 
158 	  /* Max gain for given polarization type */
159 	  gmax[idx] = rad_pattern[idx].gtot[mgidx] +
160 		Polarization_Factor(pol, idx, mgidx);
161 
162 	  /* Net gain if selected */
163 	  if( isFlagSet(PLOT_NETGAIN) )
164 	  {
165 		Zr = impedance_data.zreal[idx];
166 		Zi = impedance_data.zimag[idx];
167 		netgain[idx] = gmax[idx] +
168 		  10*log10(4*Zr*Zo/(pow(Zr+Zo,2)+pow(Zi,2)));
169 	  }
170 
171 	  /* Radiation angle/phi where max gain occurs */
172 	  gdir_tht[idx] = 90.0 - rad_pattern[idx].max_gain_tht[pol];
173 	  gdir_phi[idx] = rad_pattern[idx].max_gain_phi[pol];
174 
175 	  /* Find F/B ratio if possible or net gain not required */
176 	  if( no_fbr || isFlagSet(PLOT_NETGAIN) )
177 		continue;
178 
179 	  /* Find F/B direction in theta */
180 	  fbdir = 180.0 - rad_pattern[idx].max_gain_tht[pol];
181 	  if( fpat.dth == 0.0 )
182 		nth = 0;
183 	  else
184 		nth = (int)( fbdir/fpat.dth + 0.5 );
185 
186 	  /* If the antenna is modelled over ground, then use the same
187 	   * theta as the max gain direction, relying on phi alone to
188 	   * take us to the back. Patch supplied by Rik van Riel AB1KW
189 	   */
190 	  if( (nth >= fpat.nth) || (nth < 0) )
191 	  {
192 		fbdir = rad_pattern[idx].max_gain_tht[pol];
193 		if( fpat.dth == 0.0 )
194 		  nth = 0;
195 		else
196 		  nth = (int)( fbdir/fpat.dth + 0.5 );
197 	  }
198 
199 	  /* Find F/B direction in phi */
200 	  fbdir = gdir_phi[idx] + 180.0;
201 	  if( fbdir >= 360.0 ) fbdir -= 360.0;
202 	  nph = (int)( fbdir/fpat.dph + 0.5 );
203 
204 	  /* No F/B calc. possible if no phi step at +180 from max gain */
205 	  if( (nph >= fpat.nph) || (nph < 0) )
206 	  {
207 		no_fbr = TRUE;
208 		continue;
209 	  }
210 
211 	  /* Index to gtot buffer for gain in back direction */
212 	  fbidx = nth + nph*fpat.nth;
213 
214 	  /* Front to back ratio */
215 	  fbratio[idx]  = pow( 10.0, gmax[idx] / 10.0 );
216 	  fbratio[idx] /= pow( 10.0,
217 		  (rad_pattern[idx].gtot[fbidx] +
218 		   Polarization_Factor(pol, idx, fbidx)) / 10.0 );
219 	  fbratio[idx] = 10.0 * log10( fbratio[idx] );
220 
221 	} /* for( idx = 0; idx < fstep; idx++ ) */
222 
223 	/*** Plot gain and f/b ratio (if possible) graph(s) */
224 	if( no_fbr || isFlagSet(PLOT_NETGAIN) )
225 	{
226 	  /* Plotting frame titles */
227 	  titles[0] = _("Raw Gain dbi");
228 	  if( isFlagSet(PLOT_NETGAIN) )
229 	  {
230 		titles[1] = _("Max Gain & Net Gain vs Frequency");
231 		titles[2] = _("Net Gain dbi");
232 		if( fstep > 1 )
233 		  Plot_Graph2( gmax, netgain, save.freq, fstep,
234 			  titles, calc_data.ngraph, ++posn );
235 	  }
236 	  else
237 	  {
238 		titles[1] = _("Max Gain & F/B Ratio vs Frequency");
239 		titles[2] = "        ";
240 		if( fstep > 1 )
241 		  Plot_Graph( gmax, save.freq, fstep,
242 			  titles, calc_data.ngraph, ++posn );
243 	  }
244 	}
245 	else
246 	{
247 	  /* Plotting frame titles */
248 	  titles[0] = _("Raw Gain dbi");
249 	  titles[1] = _("Max Gain & F/B Ratio vs Frequency");
250 	  titles[2] = _("F/B Ratio db");
251 	  if( fstep > 1 )
252 		Plot_Graph2( gmax, fbratio, save.freq, fstep,
253 			titles, calc_data.ngraph, ++posn );
254 	}
255 
256 	/* Plot max gain direction if enabled */
257 	if( isFlagSet(PLOT_GAIN_DIR) )
258 	{
259 	  /* Plotting frame titles */
260 	  titles[0] = _("Rad Angle - deg");
261 	  titles[1] = _("Max Gain Direction vs Frequency");
262 	  titles[2] = _("Phi - deg");
263 	  if( fstep > 1 )
264 		Plot_Graph2( gdir_tht, gdir_phi, save.freq, fstep,
265 			titles, calc_data.ngraph, ++posn );
266 	}
267 
268   } /* if( isFlagSet(PLOT_GMAX) && isFlagSet(ENABLE_RDPAT) ) */
269 
270   /* Plot gain in direction of viewer vs freq, if possible */
271   if( isFlagSet(PLOT_GVIEWER) && isFlagSet(ENABLE_RDPAT) )
272   {
273 	/* Plotting frame titles */
274 	titles[0] = _("Raw Gain dbi");
275 	titles[1] = _("Gain in Viewer Direction vs Frequency");
276 
277 	/* Allocate viewer gain buffer */
278 	size_t mreq = (size_t)fstep * sizeof(double);
279 	mem_realloc( (void **)&vgain, mreq, "in plot_freqdata.c" );
280 
281 	/* Calcs are done for all freq steps */
282 	for( idx = 0; idx < fstep; idx++ )
283 	  vgain[idx] = Viewer_Gain( structure_proj_params, idx );
284 
285 	/* Plot net gain if selected */
286 	if( isFlagSet(PLOT_NETGAIN) )
287 	{
288 	  mreq = (size_t)fstep * sizeof(double);
289 	  mem_realloc( (void **)&netgain, mreq, "in plot_freqdata.c" );
290 
291 	  Zo = calc_data.zo;
292 	  for( idx = 0; idx < fstep; idx++ )
293 	  {
294 		Zr = impedance_data.zreal[idx];
295 		Zi = impedance_data.zimag[idx];
296 		netgain[idx] = vgain[idx] +
297 		  10*log10(4*Zr*Zo/(pow(Zr+Zo,2)+pow(Zi,2)));
298 	  }
299 
300 	  /* Plot net gain if selected */
301 	  titles[2] = _("Net gain dbi");
302 	  if( fstep > 1 )
303 		Plot_Graph2( vgain, netgain, save.freq, fstep,
304 			titles, calc_data.ngraph, ++posn );
305 	} /* if( isFlagSet(PLOT_NETGAIN) ) */
306 	else
307 	{
308 	  titles[2] = "        ";
309 	  if( fstep > 1 )
310 		Plot_Graph( vgain, save.freq, fstep,
311 			titles, calc_data.ngraph, ++posn );
312 	}
313   } /* isFlagSet(PLOT_GVIEWER) && isFlagSet(ENABLE_RDPAT) */
314 
315   /* Plot VSWR vs freq */
316   if( isFlagSet(PLOT_VSWR) )
317   {
318 	double *vswr = NULL, gamma;
319 	double zrpro2, zrmro2, zimag2;
320 
321 	/* Plotting frame titles */
322 	titles[0] = _("VSWR");
323 	titles[1] = _("VSWR vs Frequency");
324 
325 	/* Calculate VSWR */
326 	mem_alloc( (void **)&vswr,
327 		(size_t)calc_data.nfrq * sizeof(double),
328 		"in Plot_Frequency_Data()" );
329 	if( vswr == NULL )
330 	{
331 	  fprintf( stderr, "xnec2c: Plot_Frequency_Data():"
332 		  "memory allocation for vswr failed\n" );
333 	  stop( _("Plot_Frequency_Data():"
334 			"Memory allocation for vswr failed"), ERR_OK );
335 	  return;
336 	}
337 	for(idx = 0; idx < fstep; idx++ )
338 	{
339 	  zrpro2 = impedance_data.zreal[idx] + calc_data.zo;
340 	  zrpro2 *= zrpro2;
341 	  zrmro2 = impedance_data.zreal[idx] - calc_data.zo;
342 	  zrmro2 *= zrmro2;
343 	  zimag2 = impedance_data.zimag[idx] * impedance_data.zimag[idx];
344 	  gamma = sqrt( (zrmro2 + zimag2)/(zrpro2 + zimag2) );
345 	  vswr[idx] = (1+gamma)/(1-gamma);
346 	  if( vswr[idx] > 10.0 ) vswr[idx] = 10.0;
347 	}
348 
349 	titles[2] = "        ";
350 	if( fstep > 1 )
351 	  Plot_Graph( vswr, save.freq, fstep,
352 		  titles, calc_data.ngraph, ++posn );
353 
354 	free_ptr( (void **)&vswr );
355   } /* if( isFlagSet(PLOT_VSWR) ) */
356 
357   /* Plot z-real and z-imag */
358   if( isFlagSet(PLOT_ZREAL_ZIMAG) )
359   {
360 	/* Plotting frame titles */
361 	titles[0] = _("Z-real");
362 	titles[1] = _("Impedance vs Frequency");
363 	titles[2] = _("Z-imag");
364 	if( fstep > 1 )
365 	  Plot_Graph2(
366 		  impedance_data.zreal, impedance_data.zimag, save.freq,
367 		  fstep, titles, calc_data.ngraph, ++posn );
368 
369   } /* if( isFlagSet(PLOT_ZREAL_ZIMAG) ) */
370 
371   /* Plot z-magn and z-phase */
372   if( isFlagSet(PLOT_ZMAG_ZPHASE) )
373   {
374 	/* Plotting frame titles */
375 	titles[0] = _("Z-magn");
376 	titles[1] = _("Impedance vs Frequency");
377 	titles[2] = _("Z-phase");
378 	if( fstep > 1 )
379 	  Plot_Graph2( impedance_data.zmagn, impedance_data.zphase,
380 		  save.freq, fstep, titles, calc_data.ngraph, ++posn );
381 
382   } /* if( isFlagSet(PLOT_ZREAL_ZIMAG) ) */
383 
384   /* Render pixmap to screen */
385   gdk_window_set_back_pixmap(
386 	  freqplots_drawingarea->window, freqplots_pixmap, FALSE );
387   gdk_window_clear( freqplots_drawingarea->window );
388 
389   /* Display freq data in entry widgets */
390   Display_Frequency_Data();
391 
392   /* Wait for GTK to complete its tasks */
393   while( g_main_context_iteration(NULL, FALSE) );
394 
395   cairo_destroy( cr );
396 } /* Plot_Frequency_Data() */
397 
398 /*-----------------------------------------------------------------------*/
399 
400 /* Display_Frequency_Data()
401  *
402  * Displays freq dependent data (gain, impedance etc)
403  * in the entry widgets in the freq plots window
404  */
405   void
Display_Frequency_Data(void)406 Display_Frequency_Data( void )
407 {
408   int pol, fstep;
409   double vswr, gamma;
410   double zrpro2, zrmro2, zimag2;
411   char txt[11];
412 
413   if( isFlagClear(PLOT_ENABLED) ) return;
414 
415   /* Limit freq stepping to nfrq */
416   fstep = calc_data.fstep;
417   if( fstep >= calc_data.nfrq )
418 	fstep = calc_data.nfrq;
419 
420   /* Polarization type */
421   pol = calc_data.pol_type;
422 
423   /* Index to gtot buffer where max gain
424    * occurs for given polarization type */
425   if( isFlagSet(ENABLE_RDPAT) )
426   {
427 	/* Max gain for given polarization type */
428 	int mgidx = rad_pattern[fstep].max_gain_idx[pol];
429 	double gmax = rad_pattern[fstep].gtot[mgidx] +
430 	  Polarization_Factor(pol, fstep, mgidx);
431 
432 	/* Display max gain */
433 	snprintf( txt, 6, "%5f", gmax );
434 	gtk_entry_set_text( GTK_ENTRY(lookup_widget(
435 			freqplots_window, "freqplots_maxgain_entry")), txt );
436 
437   } /* isFlagSet(ENABLE_RDPAT) */
438 
439   /* Display frequency */
440   snprintf( txt, 11, "%10.3f", (double)calc_data.fmhz );
441   gtk_entry_set_text( GTK_ENTRY(lookup_widget(
442 		  freqplots_window, "freqplots_fmhz_entry")), txt );
443 
444   /* Calculate VSWR */
445   zrpro2 = (double)creal( netcx.zped ) + calc_data.zo;
446   zrpro2 *= zrpro2;
447   zrmro2 = (double)creal( netcx.zped ) - calc_data.zo;
448   zrmro2 *= zrmro2;
449   zimag2 = (double)cimag( netcx.zped );
450   zimag2 *= zimag2;
451   gamma = sqrt( (zrmro2 + zimag2)/(zrpro2 + zimag2) );
452   vswr = (1+gamma)/(1-gamma);
453   if( vswr > 999.0 )
454 	vswr = 999.0;
455 
456   /* Display VSWR */
457   snprintf( txt, 6, "%5f", vswr );
458   gtk_entry_set_text( GTK_ENTRY(lookup_widget(
459 		  freqplots_window, "freqplots_vswr_entry")), txt );
460 
461   /* Display Z real */
462   snprintf( txt, 6, "%5f", (double)creal( netcx.zped ) );
463   gtk_entry_set_text( GTK_ENTRY(lookup_widget(
464 		  freqplots_window, "freqplots_zreal_entry")), txt );
465 
466   /* Display Z imaginary */
467   snprintf( txt, 6, "%5f", (double)cimag( netcx.zped ) );
468   gtk_entry_set_text( GTK_ENTRY(lookup_widget(
469 		  freqplots_window, "freqplots_zimag_entry")), txt );
470 
471 } /* Display_Frequency_Data() */
472 
473 /*-----------------------------------------------------------------------*/
474 
475 /* Draw_Plotting_Frame()
476  *
477  * Draws a graph plotting frame, including
478  * horizontal and vertical divisions
479  */
480 void
Draw_Plotting_Frame(gchar ** title,GdkRectangle * rect,int nhor,int nvert)481 Draw_Plotting_Frame(
482 	gchar **title,
483 	GdkRectangle *rect,
484 	int nhor, int nvert )
485 {
486   int idx, xpw, xps, yph, yps;
487   PangoLayout *layout;
488   int width0, width1, width2, height; /* Layout size */
489 
490   /* Cairo context */
491   cairo_t *cr = gdk_cairo_create( freqplots_pixmap );
492 
493   /* Draw titles (left scale, center and right scale) */
494   cairo_set_source_rgb( cr, MAGENTA );
495   layout = gtk_widget_create_pango_layout(
496 	  freqplots_drawingarea, title[0] );
497   pango_layout_get_pixel_size( layout, &width0, &height);
498   cairo_move_to( cr, rect->x, rect->y );
499   pango_cairo_show_layout( cr, layout );
500 
501   cairo_set_source_rgb( cr, CYAN );
502   pango_layout_set_text( layout, title[2], -1 );
503   pango_layout_get_pixel_size( layout, &width2, &height);
504   xpw = rect->x + rect->width - width2;
505   cairo_move_to( cr, xpw, rect->y );
506   pango_cairo_show_layout( cr, layout );
507 
508   cairo_set_source_rgb( cr, YELLOW );
509   pango_layout_set_text( layout, title[1], -1 );
510   pango_layout_get_pixel_size( layout, &width1, &height);
511   xpw = rect->x + width0/2 + (rect->width-width1-width2)/2;
512   cairo_move_to( cr, xpw, rect->y );
513   pango_cairo_show_layout( cr, layout );
514 
515   /* Move to plot box and divisions */
516   rect->y += height;
517   xpw = rect->x + rect->width;
518   yph = rect->y + rect->height;
519 
520   /* Draw vertical divisions */
521   cairo_set_source_rgb( cr, GREY );
522   nvert--;
523   for( idx = 1; idx <= nvert; idx++ )
524   {
525 	xps = rect->x + (idx * rect->width) / nvert;
526 	Cairo_Draw_Line( cr, xps, rect->y, xps, yph );
527   }
528 
529   /* Draw horizontal divisions */
530   nhor--;
531   for( idx = 1; idx <= nhor; idx++ )
532   {
533 	yps = rect->y + (idx * rect->height) / nhor;
534 	Cairo_Draw_Line( cr, rect->x, yps, xpw, yps );
535   }
536 
537   /* Draw outer box */
538   cairo_rectangle(
539 	  cr, rect->x, rect->y, rect->width, rect->height );
540   cairo_stroke( cr );
541 
542   /* Draw a vertical line to show current freq if it was
543    * changed by a user click on the plots drawingarea */
544   if( isFlagSet(FREQ_LOOP_DONE) && isFlagSet(PLOT_FREQ_LINE) )
545   {
546 	double fr;
547 
548 	fr = ((double)calc_data.fmhz - min_fscale) /
549 	  (max_fscale - min_fscale);
550 	fr = fr * (double)rect->width + 0.5;
551 
552 	cairo_set_source_rgb( cr, GREEN );
553 	Cairo_Draw_Line( cr, rect->x+(int)fr, rect->y, rect->x+(int)fr, yph );
554   }
555 
556   g_object_unref( layout );
557   cairo_destroy( cr );
558 } /* Draw_Plotting_Frame() */
559 
560 /*-----------------------------------------------------------------------*/
561 
562 /* Plot_Vertical_Scale()
563  *
564  * Draws out a vertical scale, between the min
565  * and max value of the variable to be plotted
566  */
567 void
Plot_Vertical_Scale(double red,double grn,double blu,int x,int y,int height,double max,double min,int nval)568 Plot_Vertical_Scale(
569 	double red, double grn, double blu,
570 	int x, int y, int height,
571 	double max, double min,
572 	int nval )
573 {
574   int idx, yps;
575   int min_order, max_order, order;
576   double vstep = 1.0;
577   char value[16], format[6];
578   PangoLayout *layout;
579   int pl_width, pl_height; /* Layout size */
580 
581   /* Abort if not enough values to plot */
582   if( nval <= 1 ) return;
583 
584   /* Cairo context */
585   cairo_t *cr = gdk_cairo_create( freqplots_pixmap );
586   cairo_set_source_rgb( cr, red, grn, blu );
587 
588   /* Calculate step between scale values */
589   vstep = (max-min) / (double)(nval-1);
590 
591   /* Determine format for scale values */
592   /* Find order of magnitude of min and max values */
593   if( min != 0.0 )
594   {
595 	double mo = log10( fabs(min) );
596 	min_order = (int)mo;
597   }
598   else
599 	min_order = 0;
600   if( max != 0.0 )
601   {
602 	double mo = log10( fabs(max) );
603 	max_order = (int)mo;
604   }
605   else max_order = 0;
606 
607   /* Use highest order for format */
608   order = ( max_order > min_order ? max_order : min_order );
609   if( order > 3 ) order = 3;
610   if( order < 0 ) order = 0;
611   snprintf( format, 6, "%%6.%df", (3-order) );
612 
613   /* Create a pango layout */
614   layout = gtk_widget_create_pango_layout(
615 	  freqplots_drawingarea, "X" );
616   pango_layout_get_pixel_size( layout, &pl_width, &pl_height);
617 
618   /* Draw vertical scale values */
619   /* Align with plot box */
620   y += pl_height/4;
621   for( idx = 0; idx < nval; idx++ )
622   {
623 	yps = y + (idx * height) / (nval-1);
624 	snprintf( value, 16, (const char *)format, max );
625 	pango_layout_set_text( layout, value, -1 );
626 	cairo_move_to( cr, x, yps );
627 	pango_cairo_show_layout( cr, layout );
628 	max -= vstep;
629   }
630 
631   g_object_unref( layout );
632   cairo_destroy( cr );
633 } /* Plot_Vertical_Scale() */
634 
635 /*-----------------------------------------------------------------------*/
636 
637 /* Plot_Horizontal_Scale()
638  *
639  * Draws out a horizontal scale, between the min
640  * and max value of the variable to be plotted
641  */
642 void
Plot_Horizontal_Scale(double red,double grn,double blu,int x,int y,int width,double max,double min,int nval)643 Plot_Horizontal_Scale(
644 	double red, double grn, double blu,
645 	int x, int y, int width,
646 	double max, double min,
647 	int nval )
648 {
649   int idx, xps, order;
650   double hstep = 1.0;
651   char value[16], format[6];
652   PangoLayout *layout;
653   int pl_width, pl_height; /* Layout size */
654 
655   /* Abort if not enough values to plot */
656   if( nval <= 1 ) return;
657 
658   /* Cairo context */
659   cairo_t *cr = gdk_cairo_create( freqplots_pixmap );
660   cairo_set_source_rgb( cr, red, grn, blu );
661 
662   /* Calculate step between scale values */
663   hstep = (max-min) / (nval-1);
664 
665   /* Determine format for scale values */
666   /* Use order of horizontal step to determine format of print */
667   double ord = log10( fabs(hstep + 0.0000001) );
668   order = (int)ord;
669   if( order > 0 )  order = 0;
670   if( order < -9 ) order = -9;
671   snprintf( format, 6, "%%6.%df", 1-order );
672 
673   /* Create a pango layout */
674   layout = gtk_widget_create_pango_layout(
675 	  freqplots_drawingarea, "1234.5" );
676   pango_layout_get_pixel_size( layout, &pl_width, &pl_height);
677 
678   /* Draw horizontal scale values */
679   /* Align with plot box */
680   x -= pl_width/2;
681   for( idx = 0; idx < nval; idx++ )
682   {
683 	xps = x + (idx * width) / (nval-1);
684 	snprintf( value, sizeof(value), (const char *)format, min );
685 	pango_layout_set_text( layout, value, -1 );
686 	cairo_move_to( cr, xps, y );
687 	pango_cairo_show_layout( cr, layout );
688 	min += hstep;
689   }
690 
691   g_object_unref( layout );
692   cairo_destroy( cr );
693 } /* Plot_Horizontal_Scale() */
694 
695 /*-----------------------------------------------------------------------*/
696 
697 /* Draw_Graph()
698  *
699  * Plots a graph of a vs b
700  */
701 void
Draw_Graph(double red,double grn,double blu,GdkRectangle * rect,double * a,double * b,double amax,double amin,double bmax,double bmin,int nval,int side)702 Draw_Graph(
703 	double red, double grn, double blu,
704 	GdkRectangle *rect,
705 	double *a, double *b,
706 	double amax, double amin,
707 	double bmax, double bmin,
708 	int nval, int side )
709 {
710   double ra, rb;
711   int idx;
712   GdkPoint *points = NULL, polygn[4];
713 
714   /* Cairo context */
715   cairo_t *cr = gdk_cairo_create( freqplots_pixmap );
716   cairo_set_source_rgb( cr, red, grn, blu );
717 
718   /* Range of values to plot */
719   ra = amax - amin;
720   rb = bmax - bmin;
721 
722   /* Calculate points to plot */
723   mem_alloc( (void **)&points,
724 	  (size_t)calc_data.nfrq * sizeof(GdkPoint),
725 	  "in Plot_Frequency_Data()" );
726   if( points == NULL )
727   {
728 	fprintf( stderr, "xnec2c: Draw_Graph():"
729 		"memory allocation for points failed\n" );
730 	stop( _("Draw_Graph():"
731 		  "Memory allocation for points failed"), ERR_OK );
732 	return;
733   }
734   for( idx = 0; idx < nval; idx++ )
735   {
736 	points[idx].x = rect->x + (int)( (double)rect->width  *
737 		(b[idx]-bmin) / rb + 0.5 );
738 	points[idx].y = rect->y + (int)( (double)rect->height *
739 		(amax-a[idx]) / ra + 0.5 );
740 
741 	/* Plot a small rectangle (left scale) or polygon (right scale) at point */
742 	if( side == LEFT )
743 	{
744 	  cairo_rectangle( cr,
745 		  (double)(points[idx].x-3), (double)(points[idx].y-3),
746 		  6.0, 6.0 );
747 	  cairo_fill( cr );
748 	}
749 	else
750 	{
751 	  polygn[0].x = points[idx].x-4; polygn[0].y = points[idx].y;
752 	  polygn[1].x = points[idx].x;   polygn[1].y = points[idx].y+4;
753 	  polygn[2].x = points[idx].x+4; polygn[2].y = points[idx].y;
754 	  polygn[3].x = points[idx].x;   polygn[3].y = points[idx].y-4;
755 	  Cairo_Draw_Polygon( cr, polygn, 4 );
756 	  cairo_fill( cr );
757 	}
758   }
759 
760   /* Draw the graph */
761   Cairo_Draw_Lines( cr, points, nval );
762 
763   free_ptr( (void **)&points );
764   cairo_destroy( cr );
765 } /* Draw_Graph() */
766 
767 /*-----------------------------------------------------------------------*/
768 
769 /* Set_Rectangle()
770  *
771  * Sets the parameters of a GdkRectangle
772  */
773   void
Set_Rectangle(GdkRectangle * rect,int x,int y,int w,int h)774 Set_Rectangle( GdkRectangle *rect, int x, int y, int w, int h )
775 {
776   rect->x = x;
777   rect->y = y;
778   rect->width  = w;
779   rect->height = h;
780 }
781 
782 /*-----------------------------------------------------------------------*/
783 
784 /* Fit_to_Scale()
785  *
786  * Adjust the max and min value of data to be plotted,
787  * as well as the number of scale sub-divisions, so that
788  * sub-division values are easier to interpolate between.
789  * The chosen scale values are 10, 10/2, 10/4, 10/5 and 1.
790  */
791   void
Fit_to_Scale(double * max,double * min,int * nval)792 Fit_to_Scale( double *max, double *min, int *nval )
793 {
794   /* Acceptable scale values (10/10, 10/5, 10/4, 10/2) */
795   /* Intermediate values are geometric mean of pairs */
796   double scale_val[] =
797   { 1.0, 1.4142, 2.0, 2.2360, 2.5, 3.5355, 5.0, 7.0710, 10.0, 14.142 };
798   double subdiv_val, subdiv_order;
799   int idx;
800 
801   /* Fix input */
802   if( *max == *min )
803   {
804 	if( *max == 0.0 )
805 	{
806 	  *max =  1.0;
807 	  *min = -1.0;
808 	}
809 	else if( *max > 0.0 )
810 	{
811 	  *max *= 1.5;
812 	  *min /= 2.0;
813 	}
814 	else
815 	{
816 	  *max /= 2.0;
817 	  *min *= 1.5;
818 	}
819   }
820 
821   /* Find subdivision's lower order of magnitude */
822   subdiv_val = (*max - *min) / (double)(*nval-1);
823   subdiv_order = 1.0;
824   while( subdiv_order < subdiv_val )
825 	subdiv_order *= 10.0;
826   while( subdiv_order > subdiv_val )
827 	subdiv_order /= 10.0;
828 
829   /* Scale subdivision 1 < subd < 10 */
830   subdiv_val /= subdiv_order;
831 
832   /* Find nearest prefered subdiv value */
833   for( idx = 1; idx <= 9; idx += 2 )
834 	if( scale_val[idx] >= subdiv_val )
835 	  break;
836 
837   /* Scale prefered subdiv value */
838   if( idx > 9 ) idx = 9;
839   subdiv_val = scale_val[idx-1] * subdiv_order;
840 
841   /* Recalculate new max and min value */
842   New_Max_Min( max, min, subdiv_val, nval );
843 
844 } /* Fit_to_Scale() */
845 
846 /*-----------------------------------------------------------------------*/
847 
848 /* Fit_to_Scale2()
849  *
850  * Adjust the max and min value of data to be plotted,
851  * as well as the number of scale sub-divisions, so that
852  * sub-division values are easier to interpolate between.
853  * This is done for two scales (left & right) simultaneously.
854  * The chosen scale values are 10, 10/2, 10/4, 10/5 and 1.
855  */
856   void
Fit_to_Scale2(double * max1,double * min1,double * max2,double * min2,int * nval)857 Fit_to_Scale2( double *max1, double *min1,
858 	double *max2, double *min2, int *nval )
859 {
860   /* Acceptable scale values (10/10, 10/5, 10/4, 10/2) */
861   /* Intermediate values are geometric mean of pairs */
862   double scale_val[] = { 10.0, 5.0, 2.5, 2.0, 1.0, 0.5 };
863 
864   double subdiv_val1, subdiv_order1, subdiv_val2, subdiv_order2;
865   double max_1, min_1, max_2, min_2, range1, range2, min_stretch;
866   double max1sv=0.0, min1sv=0.0, max2sv=0.0, min2sv=0.0;
867   int idx1, idx2, nval1, nval2, nvalsv=0, mx, i1, i2;
868 
869   /* Fix input for both scales */
870   if( *max1 == *min1 )
871   {
872 	if( *max1 == 0.0 )
873 	{
874 	  *max1 =  1.0;
875 	  *min1 = -1.0;
876 	}
877 	else if( *max1 > 0.0 )
878 	{
879 	  *max1 *= 1.5;
880 	  *min1 /= 2.0;
881 	}
882 	else
883 	{
884 	  *max1 /= 2.0;
885 	  *min1 *= 1.5;
886 	}
887   }
888 
889   if( *max2 == *min2 )
890   {
891 	if( *max2 == 0.0 )
892 	{
893 	  *max2 =  1.0;
894 	  *min2 = -1.0;
895 	}
896 	else if( *max2 > 0.0 )
897 	{
898 	  *max2 *= 1.5;
899 	  *min2 /= 2.0;
900 	}
901 	else
902 	{
903 	  *max2 /= 2.0;
904 	  *min2 *= 1.5;
905 	}
906   }
907 
908   /* For each scale */
909   /* Find subdivision's lower order of magnitude */
910   subdiv_val1 = (*max1 - *min1) / (double)(*nval-1);
911   subdiv_order1 = 1.0;
912   while( subdiv_order1 < subdiv_val1 )
913 	subdiv_order1 *= 10.0;
914   while( subdiv_order1 > subdiv_val1 )
915 	subdiv_order1 /= 10.0;
916 
917   /* Scale subdivision 1 < subd < 10 */
918   subdiv_val1 /= subdiv_order1;
919 
920   /* Find nearest prefered subdiv value */
921   idx1 = 1;
922   while( (scale_val[idx1] > subdiv_val1) && (idx1 <= 4) )
923 	idx1++;
924 
925   /* Find subdivision's lower order of magnitude */
926   subdiv_val2 = (*max2 - *min2) / (double)(*nval-1);
927   subdiv_order2 = 1.0;
928   while( subdiv_order2 < subdiv_val2 )
929 	subdiv_order2 *= 10.0;
930   while( subdiv_order2 > subdiv_val2 )
931 	subdiv_order2 /= 10.0;
932 
933   /* Scale subdivision 1 < subd < 10 */
934   subdiv_val2 /= subdiv_order2;
935 
936   /* Find nearest prefered subdiv value */
937   idx2 = 1;
938   while( (scale_val[idx2] > subdiv_val2) && (idx2 <= 4) )
939 	idx2++;
940 
941   /* Search for a compromize in scale stretching */
942   range1 = *max1 - *min1;
943   range2 = *max2 - *min2;
944   min_stretch = 10.0;
945 
946   /* Scale prefered subdiv values */
947   subdiv_val1 = scale_val[idx1] * subdiv_order1;
948   subdiv_val2 = scale_val[idx2] * subdiv_order2;
949 
950   /* Recalculate new max and min values */
951   max_1 = *max1; min_1 = *min1; nval1 = *nval;
952   max_2 = *max2; min_2 = *min2; nval2 = *nval;
953   New_Max_Min( &max_1, &min_1, subdiv_val1, &nval1 );
954   New_Max_Min( &max_2, &min_2, subdiv_val2, &nval2 );
955 
956   /* This is a lucky case */
957   if( (nval1 == nval2) && (nval1 >= *nval) )
958   {
959 	*max1 = max_1; *min1 = min_1;
960 	*max2 = max_2; *min2 = min_2;
961 	*nval = nval1;
962 	return;
963   }
964 
965   /* More likely look for a compromise */
966   for( i1 = 0; i1 < 2; i1++ )
967 	for( i2 = 0; i2 < 2; i2++ )
968 	{
969 	  double stretch;
970 
971 	  /* Scale prefered subdiv values */
972 	  subdiv_val1 = scale_val[idx1-i1] * subdiv_order1;
973 	  subdiv_val2 = scale_val[idx2-i2] * subdiv_order2;
974 
975 	  /* Recalculate new max and min values */
976 	  max_1 = *max1; min_1 = *min1; nval1 = *nval;
977 	  max_2 = *max2; min_2 = *min2; nval2 = *nval;
978 	  New_Max_Min( &max_1, &min_1, subdiv_val1, &nval1 );
979 	  New_Max_Min( &max_2, &min_2, subdiv_val2, &nval2 );
980 
981 	  /* This is a lucky case */
982 	  if( nval1 == nval2 )
983 	  {
984 		*max1 = max_1; *min1 = min_1;
985 		*max2 = max_2; *min2 = min_2;
986 		*nval = nval1;
987 		return;
988 	  }
989 
990 	  /* Stretch scale with the fewer steps */
991 	  if( nval1 > nval2 )
992 	  {
993 		mx = nval1 - nval2;
994 		max_2 += ((mx+1)/2) * subdiv_val2;
995 		min_2 -= (mx/2) * subdiv_val2;
996 		stretch = (max_2-min_2)/range2;
997 		if( (stretch < min_stretch) )
998 		{
999 		  min_stretch = stretch;
1000 		  max2sv = max_2; min2sv = min_2;
1001 		  max1sv = max_1; min1sv = min_1;
1002 		  nvalsv = nval1;
1003 		}
1004 	  }
1005 	  else
1006 	  {
1007 		mx = nval2 - nval1;
1008 		max_1 += ((mx+1)/2) * subdiv_val1;
1009 		min_1 -= (mx/2) * subdiv_val1;
1010 		stretch = (max_1-min_1)/range1;
1011 		if( (stretch < min_stretch) )
1012 		{
1013 		  min_stretch = stretch;
1014 		  max1sv = max_1; min1sv = min_1;
1015 		  max2sv = max_2; min2sv = min_2;
1016 		  nvalsv = nval2;
1017 		}
1018 	  }
1019 
1020 	} /* for( i1 = 0; i1 < 3; i1++ ) */
1021 
1022   *max1 = max1sv; *min1 = min1sv;
1023   *max2 = max2sv; *min2 = min2sv;
1024   *nval = nvalsv;
1025 
1026 } /* Fit_to_Scale2() */
1027 
1028 /*-----------------------------------------------------------------------*/
1029 
1030 /* New_Max_Min()
1031  *
1032  * Calculates new max and min scale
1033  * values and the number of steps
1034  */
1035   void
New_Max_Min(double * max,double * min,double sval,int * nval)1036 New_Max_Min( double *max, double *min, double sval, int *nval )
1037 {
1038   int ix;
1039   double i;
1040 
1041   i = ceil(*max / sval - 0.000001);
1042   ix = (int)i;
1043   *max = (double)ix * sval;
1044   i = floor(*min / sval + 0.000001);
1045   ix = (int)i;
1046   *min = (double)ix * sval;
1047   *nval = (int)((*max - *min) / sval + 0.5) + 1;
1048 
1049 } /* New_Max_Min() */
1050 
1051 /*-----------------------------------------------------------------------*/
1052 
1053 /* Plot_Graph2()
1054  *
1055  * Plots graphs of two functions against a common variable
1056  */
1057 void
Plot_Graph2(double * fa,double * fb,double * fc,int nc,char * titles[],int nplt,int posn)1058 Plot_Graph2(
1059 	double *fa, double *fb, double *fc, int nc,
1060 	char *titles[], int nplt, int posn )
1061 {
1062   double max_fa, min_fa, max_fb, min_fb;
1063   static int first_call = TRUE;
1064   int idx, nval_ab, plot_height, plot_posn;
1065   /* Pango layout size */
1066   static int layout_width, layout_height;
1067 
1068   if( first_call )
1069   {
1070 	/* Create a pango layout to get scale size */
1071 	PangoLayout *layout;
1072 	layout = gtk_widget_create_pango_layout(
1073 		freqplots_drawingarea, "000000" );
1074 	pango_layout_get_pixel_size( layout,
1075 		&layout_width, &layout_height);
1076 	first_call = FALSE;
1077 	g_object_unref( layout );
1078   }
1079 
1080   /* Available height for each graph.
1081    * (np=number of graphs to be plotted) */
1082   plot_height = freqplots_pixmap_height/nplt;
1083   plot_posn   = (freqplots_pixmap_height * (posn-1))/nplt;
1084 
1085   /* Plot box rectangle */
1086   Set_Rectangle(
1087 	  &plot_rect,
1088 	  layout_width+4, plot_posn+2,
1089 	  freqplots_pixmap_width-8 - 2*layout_width,
1090 	  plot_height-8 - 2*layout_height );
1091 
1092   /*** Draw horizontal (freq) scale ***/
1093   Plot_Horizontal_Scale(
1094 	  YELLOW,
1095 	  layout_width+2,
1096 	  plot_posn+plot_height-2 - layout_height,
1097 	  plot_rect.width,
1098 	  max_fscale, min_fscale, nval_fscale );
1099 
1100   /*** Draw left and right scale ***/
1101   /* Find max and min of fa */
1102   max_fa = min_fa = fa[0];
1103   for( idx = 1; idx < nc; idx++ )
1104   {
1105 	if( max_fa < fa[idx] )
1106 	  max_fa = fa[idx];
1107 	if( min_fa > fa[idx] )
1108 	  min_fa = fa[idx];
1109   }
1110 
1111   /* Find max and min of fb */
1112   max_fb = min_fb = fb[0];
1113   for( idx = 1; idx < nc; idx++ )
1114   {
1115 	if( max_fb < fb[idx] )
1116 	  max_fb = fb[idx];
1117 	if( min_fb > fb[idx] )
1118 	  min_fb = fb[idx];
1119   }
1120 
1121   /* Fit ranges to common scale */
1122   nval_ab = plot_height / 50;
1123   Fit_to_Scale2( &max_fa, &min_fa, &max_fb, &min_fb, &nval_ab );
1124 
1125   /* Draw left scale */
1126   Plot_Vertical_Scale(
1127 	  MAGENTA,
1128 	  2, plot_posn+2,
1129 	  plot_rect.height,
1130 	  max_fa, min_fa, nval_ab );
1131 
1132   /* Draw right scale */
1133   Plot_Vertical_Scale(
1134 	  CYAN,
1135 	  freqplots_pixmap_width-2 - layout_width, plot_posn+2,
1136 	  plot_rect.height,
1137 	  max_fb, min_fb, nval_ab );
1138 
1139   /* Draw plotting frame */
1140   Draw_Plotting_Frame( titles, &plot_rect, nval_ab, nval_fscale );
1141 
1142   /* Draw graph */
1143   Draw_Graph(
1144 	  MAGENTA,
1145 	  &plot_rect, fa, fc,
1146 	  max_fa, min_fa,
1147 	  max_fscale, min_fscale,
1148 	  nc, LEFT );
1149 
1150   /* Draw graph */
1151   Draw_Graph(
1152 	  CYAN, &plot_rect,
1153 	  fb, fc,
1154 	  max_fb, min_fb,
1155 	  max_fscale, min_fscale,
1156 	  nc, RIGHT );
1157 
1158 } /* Plot_Graph2() */
1159 
1160 /*-----------------------------------------------------------------------*/
1161 
1162 /* Plot_Graph()
1163  *
1164  * Plots graph of a functions against a variable
1165  */
1166 void
Plot_Graph(double * fa,double * fb,int nb,char * titles[],int nplt,int posn)1167 Plot_Graph(
1168 	double *fa, double *fb, int nb,
1169 	char *titles[], int nplt, int posn )
1170 {
1171   double max_fa, min_fa;
1172   static int first_call = TRUE;
1173   int idx, nval_fa, plot_height, plot_posn;
1174   /* Pango layout size */
1175   static int layout_width, layout_height;
1176 
1177 
1178   if( first_call )
1179   {
1180 	/* Create a pango layout to get scale size */
1181 	PangoLayout *layout;
1182 	layout = gtk_widget_create_pango_layout(
1183 		freqplots_drawingarea, "000000" );
1184 	pango_layout_get_pixel_size(
1185 		layout, &layout_width, &layout_height);
1186 	first_call = FALSE;
1187 	g_object_unref( layout );
1188   }
1189 
1190   /* Available height for each graph.
1191    * (np=number of graphs to be plotted) */
1192   plot_height = freqplots_pixmap_height/nplt;
1193   plot_posn   = (freqplots_pixmap_height * (posn-1))/nplt;
1194 
1195   /* Plot box rectangle */
1196   Set_Rectangle(
1197 	  &plot_rect,
1198 	  layout_width + 4, plot_posn+2,
1199 	  freqplots_pixmap_width-8 - 2*layout_width,
1200 	  plot_height-8 - 2*layout_height );
1201 
1202   /*** Draw horizontal (freq) scale ***/
1203   Plot_Horizontal_Scale(
1204 	  YELLOW,
1205 	  layout_width+2,
1206 	  plot_posn+plot_height-2 - layout_height,
1207 	  plot_rect.width,
1208 	  max_fscale, min_fscale, nval_fscale );
1209 
1210   /*** Draw left scale ***/
1211   /* Find max and min of fa */
1212   max_fa = min_fa = fa[0];
1213   for( idx = 1; idx < nb; idx++ )
1214   {
1215 	if( max_fa < fa[idx] )
1216 	  max_fa = fa[idx];
1217 	if( min_fa > fa[idx] )
1218 	  min_fa = fa[idx];
1219   }
1220 
1221   /* Fit fa range to scale */
1222   nval_fa = plot_height / 50;
1223   Fit_to_Scale( &max_fa, &min_fa, &nval_fa );
1224 
1225   /* Draw left scale */
1226   Plot_Vertical_Scale(
1227 	  MAGENTA,
1228 	  2, plot_posn+2,
1229 	  plot_rect.height,
1230 	  max_fa, min_fa, nval_fa );
1231 
1232   /* Draw plotting frame */
1233   Draw_Plotting_Frame( titles, &plot_rect, nval_fa, nval_fscale );
1234 
1235   /* Draw graph */
1236   Draw_Graph(
1237 	  MAGENTA,
1238 	  &plot_rect, fa, fb,
1239 	  max_fa, min_fa,
1240 	  max_fscale, min_fscale,
1241 	  nb, LEFT );
1242 
1243 } /* Plot_Graph() */
1244 
1245 /*-----------------------------------------------------------------------*/
1246 
1247 /* Plots_Window_Killed()
1248  *
1249  * Cleans up after the plots window is closed
1250  */
1251   void
Plots_Window_Killed(void)1252 Plots_Window_Killed(void)
1253 {
1254   if( isFlagSet(PLOT_ENABLED) )
1255   {
1256 	ClearFlag( PLOT_FLAGS );
1257 	freqplots_drawingarea = NULL;
1258 	freqplots_window = NULL;
1259 
1260 	gtk_check_menu_item_set_active(
1261 		GTK_CHECK_MENU_ITEM(lookup_widget(
1262 			main_window, "main_freqplots")), FALSE );
1263   }
1264 
1265 } /* Plots_Window_Killed() */
1266 
1267 /*-----------------------------------------------------------------------*/
1268 
1269 /* Set_Frequecy_On_Click()
1270  *
1271  * Sets the current freq after click by user on plots drawingarea
1272  */
1273   void
Set_Frequency_On_Click(GdkEventButton * event)1274 Set_Frequency_On_Click( GdkEventButton *event )
1275 {
1276   gdouble fmhz = 0.0;
1277   gdouble x, w;
1278   int idx;
1279 
1280 
1281   if( isFlagClear(FREQ_LOOP_DONE) )
1282 	return;
1283 
1284   /* Width of plot bounding rectangle */
1285   w = (double)plot_rect.width;
1286 
1287   /* 'x' posn of click refered to plot bounding rectangle's 'x' */
1288   x = event->x - (double)plot_rect.x;
1289   if( x < 0.0 ) x = 0.0;
1290   else if( x > w ) x = w;
1291 
1292   /* Set freq corresponding to click 'x', to freq spinbuttons */
1293   idx = calc_data.lastf;
1294   switch( event->button )
1295   {
1296 	case 1: /* Calculate frequency corresponding to mouse position in plot */
1297 	  /* Enable drawing of freq line */
1298 	  SetFlag( PLOT_FREQ_LINE );
1299 
1300 	  fmhz = max_fscale - min_fscale;
1301 	  fmhz = min_fscale + fmhz * x/w;
1302 	  break;
1303 
1304 	case 2: /* Disable drawing of freq line */
1305 	  ClearFlag( PLOT_FREQ_LINE );
1306 	  Plot_Frequency_Data();
1307 	  return;
1308 
1309 	case 3: /* Calculate frequency corresponding to mouse position in plot */
1310 	  /* Enable drawing of freq line */
1311 	  SetFlag( PLOT_FREQ_LINE );
1312 
1313 	  fmhz = max_fscale - min_fscale;
1314 	  fmhz = min_fscale + fmhz * x/w;
1315 
1316 	  /* Find nearest freq step */
1317 	  idx = (int)( (double)idx * (fmhz - save.freq[0]) /
1318 		  (save.freq[idx] - save.freq[0]) + 0.5 );
1319 
1320 	  if( idx > calc_data.lastf )
1321 		idx = calc_data.lastf;
1322 	  else if( idx < 0 ) idx = 0;
1323 
1324 	  fmhz = save.freq[idx];
1325 
1326   } /* switch( event->button ) */
1327 
1328   /* Set frequency spinbuttons on new freq */
1329   if( fmhz != gtk_spin_button_get_value(mainwin_frequency) )
1330   {
1331 	gtk_spin_button_set_value( mainwin_frequency, fmhz );
1332 	if( isFlagSet(DRAW_ENABLED) )
1333 	  gtk_spin_button_set_value( rdpattern_frequency, fmhz );
1334   }
1335   else /* Replot data */
1336   {
1337 	calc_data.fmhz = (double)fmhz;
1338 	g_idle_add( Redo_Currents, NULL );
1339   }
1340 
1341 } /* Set_Freq_On_Click() */
1342 
1343 /*-----------------------------------------------------------------------*/
1344 
1345