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