xnecview-1.35.orig/0002755000175000017500000000000011005115541012776 5ustar afrb2afrb2xnecview-1.35.orig/COPYING0000644000175000017500000004307611004452102014035 0ustar afrb2afrb2 GNU GENERAL PUBLIC LICENSE Version 2, June 1991 Copyright (C) 1989, 1991 Free Software Foundation, Inc. 675 Mass Ave, Cambridge, MA 02139, USA Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. This General Public License applies to most of the Free Software Foundation's software and to any other program whose authors commit to using it. (Some other Free Software Foundation software is covered by the GNU Library General Public License instead.) You can apply it to your programs, too. When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for this service if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs; and that you know you can do these things. To protect your rights, we need to make restrictions that forbid anyone to deny you these rights or to ask you to surrender the rights. These restrictions translate to certain responsibilities for you if you distribute copies of the software, or if you modify it. For example, if you distribute copies of such a program, whether gratis or for a fee, you must give the recipients all the rights that you have. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. We protect your rights with two steps: (1) copyright the software, and (2) offer you this license which gives you legal permission to copy, distribute and/or modify the software. Also, for each author's protection and ours, we want to make certain that everyone understands that there is no warranty for this free software. If the software is modified by someone else and passed on, we want its recipients to know that what they have is not the original, so that any problems introduced by others will not reflect on the original authors' reputations. Finally, any free program is threatened constantly by software patents. We wish to avoid the danger that redistributors of a free program will individually obtain patent licenses, in effect making the program proprietary. To prevent this, we have made it clear that any patent must be licensed for everyone's free use or not licensed at all. The precise terms and conditions for copying, distribution and modification follow. GNU GENERAL PUBLIC LICENSE TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 0. This License applies to any program or other work which contains a notice placed by the copyright holder saying it may be distributed under the terms of this General Public License. The "Program", below, refers to any such program or work, and a "work based on the Program" means either the Program or any derivative work under copyright law: that is to say, a work containing the Program or a portion of it, either verbatim or with modifications and/or translated into another language. (Hereinafter, translation is included without limitation in the term "modification".) Each licensee is addressed as "you". Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running the Program is not restricted, and the output from the Program is covered only if its contents constitute a work based on the Program (independent of having been made by running the Program). Whether that is true depends on what the Program does. 1. You may copy and distribute verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and give any other recipients of the Program a copy of this License along with the Program. You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee. 2. You may modify your copy or copies of the Program or any portion of it, thus forming a work based on the Program, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions: a) You must cause the modified files to carry prominent notices stating that you changed the files and the date of any change. b) You must cause any work that you distribute or publish, that in whole or in part contains or is derived from the Program or any part thereof, to be licensed as a whole at no charge to all third parties under the terms of this License. c) If the modified program normally reads commands interactively when run, you must cause it, when started running for such interactive use in the most ordinary way, to print or display an announcement including an appropriate copyright notice and a notice that there is no warranty (or else, saying that you provide a warranty) and that users may redistribute the program under these conditions, and telling the user how to view a copy of this License. (Exception: if the Program itself is interactive but does not normally print such an announcement, your work based on the Program is not required to print an announcement.) These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Program, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Program, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it. Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Program. In addition, mere aggregation of another work not based on the Program with the Program (or with a work based on the Program) on a volume of a storage or distribution medium does not bring the other work under the scope of this License. 3. You may copy and distribute the Program (or a work based on it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you also do one of the following: a) Accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, b) Accompany it with a written offer, valid for at least three years, to give any third party, for a charge no more than your cost of physically performing source distribution, a complete machine-readable copy of the corresponding source code, to be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, c) Accompany it with the information you received as to the offer to distribute corresponding source code. (This alternative is allowed only for noncommercial distribution and only if you received the program in object code or executable form with such an offer, in accord with Subsection b above.) The source code for a work means the preferred form of the work for making modifications to it. For an executable work, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the executable. However, as a special exception, the source code distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable. If distribution of executable or object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place counts as distribution of the source code, even though third parties are not compelled to copy the source along with the object code. 4. You may not copy, modify, sublicense, or distribute the Program except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense or distribute the Program is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance. 5. You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Program or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Program (or any work based on the Program), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Program or works based on it. 6. Each time you redistribute the Program (or any work based on the Program), the recipient automatically receives a license from the original licensor to copy, distribute or modify the Program subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties to this License. 7. If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Program at all. For example, if a patent license would not permit royalty-free redistribution of the Program by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Program. If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply and the section as a whole is intended to apply in other circumstances. It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system, which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice. This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License. 8. If the distribution and/or use of the Program is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Program under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License. 9. The Free Software Foundation may publish revised and/or new versions of the General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Program specifies a version number of this License which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of this License, you may choose any version ever published by the Free Software Foundation. 10. If you wish to incorporate parts of the Program into other free programs whose distribution conditions are different, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally. NO WARRANTY 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. END OF TERMS AND CONDITIONS Appendix: How to Apply These Terms to Your New Programs If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms. To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively convey the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. Copyright (C) 19yy This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. Also add information on how to contact you by electronic and paper mail. If the program is interactive, make it output a short notice like this when it starts in an interactive mode: Gnomovision version 69, Copyright (C) 19yy name of author Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details. The hypothetical commands `show w' and `show c' should show the appropriate parts of the General Public License. Of course, the commands you use may be called something other than `show w' and `show c'; they could even be mouse-clicks or menu items--whatever suits your program. You should also get your employer (if you work as a programmer) or your school, if any, to sign a "copyright disclaimer" for the program, if necessary. Here is a sample; alter the names: Yoyodyne, Inc., hereby disclaims all copyright interest in the program `Gnomovision' (which makes passes at compilers) written by James Hacker. , 1 April 1989 Ty Coon, President of Vice This General Public License does not permit incorporating your program into proprietary programs. If your program is a subroutine library, you may consider it more useful to permit linking proprietary applications with the library. If this is what you want to do, use the GNU Library General Public License instead of this License. xnecview-1.35.orig/HISTORY0000644000175000017500000002167311005110762014071 0ustar afrb2afrb2X N E C V I E W --------------- A program for visualizing NEC2 input and output data (Unix/X-windows). Copyright (C) 1998-2001, Pieter-Tjerk de Boer ----------------------------------------------------------------------- HISTORY: -------- Version 0.1, 1998-06-06: - first public release - only few input card types supported Version 0.2, 1998-12-13: - almost all input card types supported - added plotting of gain pattern - colour added - made front and back of surfaces distinguishable (by colour) Version 0.3, 1999-1-15 - added PostScript output - added third gain scale, "log" - added buttons in the window - added plotting of gain as slices in coordinate planes - very small mouse dragging is treated as clicking - corrected handling of output files which sweep over only one angle - man page added, thanks to Joop Stakenborg, PA3ABA. Version 0.4, 1999-5-30 - fixed bug in PostScript output - added handling of GS cards - "long" options now need -- instead of - - fixed colours on 1bpp screen Version 0.5, 1999-6-13 - added double buffering support: this reduces flickering by drawing in a separate 'Pixmap' instead of on the screen - postscript now uses a private dictionary - more general NEC output reading routine - uses hinted motion events to speed up dragging - motion+first button now rotates entire gain pattern, motion + second button rotates simplified gain pattern (quicker) - fixed bug in linear gain scale - removed -q/--quick option - added a (too?) simple icon to appear when xnecview is iconified The first 5 of these changes were essentially contributed by Alan Bain, M1BXV; thanks! Version 0.6, 1999-7-4 - added display of wire numbers - added display of location of excitations and loads - added display of 'X', 'Y' and 'Z' at axes - configuration is now separate .h file - corrected "ARRL style" gain scale (now 0.89 per 2 dB, as per the "ARRL Antenna Book"; tnx Don, VE3HUR) - added handling of GM cards with non-zero ITS - xnecview no longer exits when supplied with incorrect input - an error message is printed when writing postscript fails (tnx Alan Bain) - standard X command line options like -geometry now work Version 1.0, 1999-7-25 - GW cards lacking the wire radius are now accepted - GC cards now produce a warning - added a bit of text to the postscript output (tnx to Don VE3HUR for testing and remarks resulting in the above changes) - iconifying now uses own icon again (didn't in 0.6) Version 1.01, 2000-01-21 - card types of structure input ("GW" etc.) now also accepted in lower case Version 1.02, 2000-03-25 - print not only maximum gain, but also direction of maximum gain and front-to-back-ratio. - plot radial lines every 10 degrees when looking along the X/Y/Z axis (essentially contributed by Marco Menchise, IZ8AEB; tnx!) Version 1.10, 2000-04-25 - added a window with plots of SWR, gain, etc. as a function of frequency (inspired by a preliminary implementation by Marco Menchise, IZ8AEB; tnx!) - added support for radiation diagrams at multiple frequencies - added plotting multiple sets of radiation data (= results from RP cards) for a frequency. - added reading of multiple output files. - removed unbuffered option. - minor rewrites of some parts of the code. - revised the documentation, creating new files USAGE and HISTORY, thus shortening README. Version 1.11, 2000-07-15 - added "support" for GC cards (i.e., they are ignored since xnecview does not display the wire thickness). - added calculation and plotting of gain and f/b in direction toward observer. - added a line of text showing frequency and gain in bottom of 3D plot. - added centered titles to the 2D plots. - mostly rewrote the man page on the basis of the USAGE file. - some optimizations by caching often calculated data; however, the Xserver's speed seems to be the limiting factor on my system, so this doesn't bring much. - bugfix: clicking on the gainscale button if no radiation pattern data was available produced a segmentation fault. - bugfix: reading multiple sets of radiation data sometimes gave an empty radiation pattern plot, depending on the order of the data. - bugfix: GW cards with NRPT=0 or ITS>0 were not handled correctly. Note: this version is a snapshot taken just before the GTK version started to diverge too far to keep developing both versions in parallel. Version 1.20, 2000-08-07 - user interface code converted to use the GTK library instead of Xaw/Xmu/Xt. - added display of phase and magnitude of currents in wires (based on original suggestions by Marco Menchise IZ8AEB and Francesco Messineo IW8QPI; tnx!). - changed mouse bindings for dragging and zooming - added support for TL and NT cards - dynamic memory allocation for radiation pattern data, resulting in much smaller memory usage. - dropped the USAGE file, the man page is now the only file with detailed usage information. Version 1.30, 2000-11-05 - added linear-in-voltage scale; the original linear scale was (and still is) linear in power. - animated display of currents, charges, electric and magnetic (near) field vector, Poynting vector. - command-line options; xnecview options in comment line of NEC input - fix some compiler warnings - limit display of Z to 20*Z0 - add display of phi(Z)/abs(Z) - don't draw a square at each data point if there are many (window 2) (latter four changes essentially contributed by Joerg Wunsch DL8DTL; tnx!) - number of grid lines in freqplots adapts to size of graph - output files without currents information no longer cause a segfault - some other minor fixes Version 1.31, 2001-06-24 - changed axes of frequency plots: number of subdivisions can now be variable, resulting in a more readable axis (stepsizes are now always multiples of 1, 2, 2.5, or 5; no longer 4 or 7.5). - if f/b is unknown, '-' is printed (instead of -1e-308) in table - hide 'nan' on axes (window 2) if no relevant data available - impedance (Z0) can now have up to 4 digits (instead of 2) - new option: -aupdate to set the update interval of animation - bugfix: extremely large values of Zi could hang xnecview - some small fixes in choosing which frequency is used for radiation pattern display after reloading Version 1.32, 2002-08-25 - the radiation pattern can now be displayed as a "wire grid" (like in earlier versions) or as an "opaque" surface (i.e., with hidden lines removed). The latter view is usually much easier to interpret, especially when there are many sidelobes. - the gain display can now be limited to one polarization component, or the 3D pattern can be coloured according to the dominant polarization. - changed some of the default colors, which did not contrast enough with the white background. But you can of course change them again, by editing config.h . - keypress 'z' now suspends/resumes the animation - typo & corrections in manpage - fixed bug that could cause segfault if NEC output file initially contained data at one frequency, and at more frequencies upon reread. - reduced memory usage by changing arrays from double to float - bugfix: missing lines in eps-export of freqplot - bugfix: GS card vs. currents data - if only a NEC output file is given and no input file, the structure view now defaults to 'none' rather than 'currents'. Version 1.33, 2002-12-27 - now all memory allocation is dynamic, so no limitations on model size anymore. - output is now also correctly interpreted if numerical Green's function is used (GF card). - added commandline option --pol . - changed rotation "speed": this is now independent of zoom factor - bugfix: if surfaces were present, GS cards were not interpreted properly - bugfix: segfault sometimes when selecting 'opaque' while no radiation data was available - elaborate bugfix of opaque drawing; in practice, this mostly affects drawing of points on the Z axis, when a polarization different from 'total' is selected. - fixes to the Imakefile (tnx Alan Bain) Version 1.34, 2003-09-10 - yet another opaque drawing bugfix. - bugfix: when zooming in very far, coordinates became too large for X11, causing incorrect lines to appear. - converted the last bits of Xlib code to GTK. - changed line ending style from "butt" to "round". - bugfix in parsing xnecview options embedded in NEC input file - minor fixes in handling of window (re)sizing Version 1.35, 2006-04-05 - now supports for rectangular, triangular and quadrilateral surface patches in NEC input (tnx to Predrag Miocinovic for contributing this). - now understands the "other" output format that some versions of NEC apparently produce (tnx to several people for submitting this). - now compiles against GTK+-2 and later, rather than 1.2 (again, tnx to several people). - graphs in window 2 now also have units printed at/near the axes. - main() return values now correctly represent error conditions (tnx Alan Bain) - yet another opaque drawing bugfix. - bugfix: export to PNG of window 2 didn't work - bugfix: ne->rpgpovalid was not always initialized - bugfix: animation didn't work on some systems because do_animation() didn't have an explicit return value xnecview-1.35.orig/Imakefile0000644000175000017500000000056211005110762014610 0ustar afrb2afrb2SYS_LIBRARIES = `pkg-config gtk+-2.0 --libs` -lm -lpng DEFINES = -DHAVE_LIBPNG INCLUDES = `pkg-config gtk+-2.0 --cflags` SRCS = xnecview.h config.h xnecview.c xwin.c parse_input.c parse_output.c draw.c draw_opaque.c freqplot.c postscript.c OBJS = xnecview.o xwin.o parse_input.o parse_output.o draw.o draw_opaque.o freqplot.o postscript.o ComplexProgramTarget(xnecview) xnecview-1.35.orig/Imakefile_nopng0000644000175000017500000000047111005110762016010 0ustar afrb2afrb2SYS_LIBRARIES = `pkg-config gtk+-2.0 --libs` -lm INCLUDES = `pkg-config gtk+-2.0 --cflags` SRCS = xnecview.h config.h xnecview.c xwin.c parse_input.c parse_output.c draw.c freqplot.c postscript.c OBJS = xnecview.o xwin.o parse_input.o parse_output.o draw.o freqplot.o postscript.o ComplexProgramTarget(xnecview) xnecview-1.35.orig/Makefile0000644000175000017500000000127311005110762014437 0ustar afrb2afrb2# comment this out if you don't have libpng installed PNG = yes CC = gcc CFLAGS = -O2 -g -Wall `pkg-config gtk+-2.0 --cflags` LD = $(CC) LDFLAGS = `pkg-config gtk+-2.0 --libs` -lm ifeq ($(PNG),yes) CFLAGS += -DHAVE_LIBPNG LDFLAGS += -lpng endif SRCS = xnecview.c xwin.c parse_input.c parse_output.c draw.c draw_opaque.c freqplot.c postscript.c icon.xbm OBJS = xnecview.o xwin.o parse_input.o parse_output.o draw.o draw_opaque.o freqplot.o postscript.o all: xnecview xnecview: $(OBJS) $(CC) $(CFLAGS) $(LDFLAGS) $(OBJS) -o xnecview xnecview.tgz: $(SRCS) tar czvf xnecview.tgz COPYING README HISTORY Imakefile Imakefile_nopng Makefile $(SRCS) xnecview.h config.h xnecview.1x xnecview.man xnecview-1.35.orig/README0000644000175000017500000001272311005110762013661 0ustar afrb2afrb2X N E C V I E W --------------- A program for visualizing NEC2 input and output data (Unix/X-windows). Copyright (C) 1998-2006, Pieter-Tjerk de Boer ----------------------------------------------------------------------- This program is free software; you can redistribute it and/or modify it under the terms of version 2 of the GNU General Public License as published by the Free Software Foundation. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ----------------------------------------------------------------------- COMPILING: ---------- This should be trivial on Linux systems: just type 'make' in the directory where the source has been unpacked. I also supplied an Imakefile, which might make compilation on other systems easier: typing 'xmkmf' will generate a Makefile, which can then be used by 'make'. Furthermore, you might want to change a few of the #define lines in config.h, to suit your tastes; see the comments. If the PNG library is not installed on your system, you should either - use the Makefile after deleting the 'PNG=yes' line in it or - use xmkmf after doing mv Imakefile_nopng Imakefile Obviously, a thus-compiled version of xnecview cannot export to PNG files. If you get an error about missing getopt.h while compiling, put a line #define NO_GETOPT at the beginning of xnecview.c . You'll lose the support for command line options, though. This apparently happens if you have a non-GNU (compatible) version of getopt. And if you get an arror about drem(), uncomment the last line in xnecview.h and recompile. Last but not least, note that xnecview needs version 1.2 or later of the GTK+ libraries. USAGE: ------ Just invoke xnecview with, as its command-line arguments, one or more names of files containing NEC2 input (structure of an antenna model) and/or output data (impedance, radiation pattern). Then xnecview will try to graphically display whatever data is in those files, in one or two windows. Window 1 shows a 3D plot of the structure (wires etc.) of the antenna, and/or the spatial distribution of the radiation, and/or distribution of the current and on the antenna elements and near electric and magnetic fields. Window 2 shows a set of graphs of several quantities (SWR, gain, etc.) as a function of frequency. Keyboard and mouse can be used to manipulate these views and export them as postscript files. For more details, see the manpage (i.e., the file xnecview.1x). WHAT'S NEW: ----------- This is version 1.35; changes compared to version 1.34 are: - now supports for rectangular, triangular and quadrilateral surface patches in NEC input. - now understands the "other" output format that some versions of NEC apparently produce. - now compiles against GTK+-2 and later, rather than 1.2. - graphs in window 2 now also have units printed at/near the axes. - a few minor bugfixes (see the file HISTORY). HELP NEEDED: ------------ I'd like some help with the following issues: - GH-cards (helix/spiral) with helix-length = 0. I don't understand these, and neither does my copy of NEC. According to doc on the WWW, this is a non-official extension, but I'd still like to support it. - What should be the 0 dB gain reference in the gain plots if gain data is available at more than one frequency? At present, the maximum gain observed over all frequencies is used for this; this can be handy for comparing gain at different frequencies, but not necessarily for judging the radiation pattern at one particular frequency, due to the non-linearity of some of the gain scales. - In a similar vain: should the currents distribution use the same scaling at all frequencies if currents are available at several frequencies? At present, the scaling is performed separately for every frequency. - As should be clear from the man page, the interpretation of the currents display is somewhat non-trivial. Hints on how to make this clearer (either in the documentation, or by changes to the program) would be welcome. - The hidden-line removal code is somewhat fragile; it needs to make some assumptions about the structure of the radiation pattern data. The code tries to check that the data satisfies all conditions, and reverts to wire-grid plotting if needed. I'd be very interested in any reports about observations of unexpected or incorrect behaviour; e.g., lines being visible that shouldn't be, or the other way around, or the code reverting to the wire mesh drawing where it would be desirable to still have hidden-line removal (so the code should be extended to handle that case). - The number of variants of gain-vs-frequency plots is getting large, now that the polarization can also be selected; and still some useful quantities like the axial ratio are not available. Suggestions on making this more manageable are welcome. (One idea: rather than distinguishing between maxgain and vgain, there could be just one gain plot, and a separate selector to choose between max-gain, viewer-gain, and possibly others.) Contributions, comments and bug reports are welcome, please send them to any of these addresses: Internet e-mail: pa3fwm@amsat.org Amateur packet-radio: PA3FWM @ PI8DAZ.#TWE.NLD.EU xnecview-1.35.orig/config.h0000644000175000017500000000676411004452102014423 0ustar afrb2afrb2/* XNECVIEW - a program for visualizing NEC2 input and output data * * Copyright (C) 1998-2002, Pieter-Tjerk de Boer -- pa3fwm@amsat.org * * Distributed on the conditions of version 2 of the GPL: see the files * README and COPYING, which accompany this source file. */ /* ------------------------------------------------------------------------ */ /* some defines: */ #define ini_phi 30 /* initial rotation around Z-axis */ #define ini_theta 70 /* initial tilting of Z-axis */ #define ini_zoom 1.0 /* initial zoom-factor */ #define ini_trx 0 /* initial horizontal displacement of origin w.r.t. center of window */ #define ini_try 0 /* initial vertical displacement of origin w.r.t. center of window */ #define ini_winsize 500 /* initial size of window (pixels) */ #define MAXWIRES 2000 /* maximum number of wires */ #define MAXSURFACES 1000 /* maximum number of surfaces */ #define MAXEXCIS 100 /* maximum number of excitations */ #define MAXLOADS 1000 /* maximum number of loads */ #define MAXNETWS 30 /* maximum number of "networks" (including transmission lines) */ #define MAXFREQS 100 /* maximum number of frequencies in the NEC output file */ #define MAXSEGMENTS 600 /* maximum number of segments in the currents data in the NEC output file */ #define MAXPATCHES 600 /* maximum number of surface patches in the NEC output file */ #define Axislen 1.1 /* length of axes, compared to largest dimension of the antenna or gain grid */ #define GAINSIZE 3.0 /* ratio between size of gain grid and largest dimension of the antenna */ #define C_BG "white" /* color of the background */ #define C_AXIS "black" /* color of the axes */ #define C_WIRE "blue" /* color of the wires */ #define C_SURF "green3" /* color of the front of surfaces */ #define C_BACK "cyan" /* color of the back of surfaces */ #define C_GAIN "red" /* color of the gain pattern */ #define C_SCALE "gray70" /* color of the gain scale */ #define C_EXCI "orange" /* color of wire segment containing excitation */ #define C_LOAD "brown" /* color of loaded wire segment */ #define C_NETW "grey" /* color of networks and transmission lines */ #define C_INACTIVE "gray86" /* color in currents display of segments in which negligibly little current flows */ #define C_EFIELD "red" /* color of electric field vectors */ #define C_HFIELD "green2" /* color of magnetic field vectors */ #define C_POYNTING "yellow2" /* color of Poynting vectors */ #define C_QPOS "cyan3" /* color of positive charge */ #define C_QNEG "magenta" /* color of negative charge */ #define C_GAIN_LIN "red" /* color of gain when mostly linearly polarized (i.e., axial ratio < Polthr ) */ #define C_GAIN_LHCP "violet red" /* color of gain when mostly left-hand circularly polarized */ #define C_GAIN_RHCP "orange" /* color of gain when mostly right-hand circularly polarized */ #define NC_phase 64 /* number of different hues for the phase scale; must be a multiple of 4 */ #define Default_anim_update_interval 100 /* time (in milliseconds) between updates of the animation display */ #define XFONT "6x10" /* font for text in the on-screen drawing */ #define PSFONT "helvetica" /* font for postscript output (size is derived by scaling the X font) */ #define R0 50.0 /* default reference impedance for SWR calculation */ #define Polthr (M_SQRT2-1) /* threshold of axial ratio used in polarization-colouring */ xnecview-1.35.orig/draw.c0000644000175000017500000006523411005110762014107 0ustar afrb2afrb2/* XNECVIEW - a program for visualizing NEC2 input and output data * * Copyright (C) 1998-2006, Pieter-Tjerk de Boer -- pa3fwm@amsat.org * * Distributed on the conditions of version 2 of the GPL: see the files * README and COPYING, which accompany this source file. * * This module contains almost all 3D drawing related stuff, like coordinate * transformation and drawing routines based on generic low-level graphics * routines, as provided by the Outdev-struct pointed to by *out. * The only 3D drawing code not included here, is the code for drawing the * the gain pattern as an opaque surface, i.e., with hidden-line removal; * that code is in draw_opaque.c, because of its size and complexity. * * Note: the code for drawing the 2D plots of several quantities versus * frequency is in 'freqplot.c'. * */ #include #include #include #include #include #include "xnecview.h" /* ------------------------- coordinate conversion and related arithmetic -------------------------------------------------- */ /* these define the projection: */ double phi=ini_phi,theta=ini_theta; /* angles defining direction of eye */ double zoom=ini_zoom; /* zoom factor */ double trx=ini_trx,try=ini_try; /* 2D-translation, as a fraction of winsize */ int winsizex,winsizey; /* size of window in pixels */ /* and these are calculated from them: */ double Xx,Xy,Xz,Yx,Yy,Yz; /* projection matrix */ double Xtr,Ytr; /* 2D-translation */ int winsize; /* min(winsizex,winsizey) */ Point eyevec; /* tip of arrow pointing toward observer */ double Zx,Zy,Zz; /* weighting factors for calculating distance to observer (possibly in "locked" direction) */ void calcproj(void) /* calculate the projection matrix etc. */ { double ph,th; double scale; if (winsizeyx + Xy*p->y + Xz*p->z; c[1] = Ytr + Yx*p->x + Yy*p->y + Yz*p->z; } void interpolate(double *p,double f,double *a,double *b) /* interpolate linearly between two points in the window (f=0..1 -> *p=*a..*b) */ { p[0]=(1-f)*a[0]+f*b[0]; p[1]=(1-f)*a[1]+f*b[1]; } /* test whether a line through two points is "ascending" ( / ) as opposed to "descending" ( \ ) */ #define ascending(c1,c2) (((c2)[0]-(c1)[0])*((c2)[1]-(c1)[1])<0) /* ------------------------- antenna structure drawing ------------------------------------------------------------------- */ void draw_wires(void) /* draw the wires of the antenna */ { Wire *wi; double c0[2],c1[2]; char s[20]; if (numwires==0) return; SetForeground(&c_wire); SetLineAttributes(2, GDK_LINE_SOLID, GDK_CAP_ROUND, GDK_JOIN_ROUND); for (wi=wires;wip0,c0); proj(&wi->p1,c1); DrawLine(c0[0], c0[1], c1[0], c1[1]); if (structplot==SPtags && !quick && wi->itg>0 && wi->dispnr) { sprintf(s,"%i",wi->itg); if (ascending(c0,c1)) DrawStringUL((c0[0]+c1[0])/2+2,(c0[1]+c1[1])/2,s); else DrawStringLL((c0[0]+c1[0])/2+2,(c0[1]+c1[1])/2,s); } } } void draw_loads(void) /* (re)draw loaded wires */ { Load *lo; double c0[2],c1[2],c2[2],c3[2]; if (numloads==0) return; SetForeground(&c_load); SetLineAttributes(3, GDK_LINE_SOLID, GDK_CAP_ROUND, GDK_JOIN_ROUND); for (lo=loads;lowire->p0,c0); proj(&lo->wire->p1,c1); if (lo->wire->ns>0) { interpolate(c2,(double)(lo->firstseg-1)/(lo->wire->ns),c0,c1); interpolate(c3,(double)lo->lastseg/(lo->wire->ns),c0,c1); DrawLine(c2[0], c2[1], c3[0], c3[1]); } else { DrawLine(c0[0], c0[1], c1[0], c1[1]); } } } void proj_segment(Wire *wire,int seg,double *c0,double *c1) { if (wire->ns>0) { double d0[2],d1[2]; proj(&wire->p0,d0); proj(&wire->p1,d1); interpolate(c0,(double)(seg-1)/(wire->ns),d0,d1); interpolate(c1,(double)seg/(wire->ns),d0,d1); } else { proj(&wire->p0,c0); proj(&wire->p1,c1); } } void draw_excis(void) /* (re)draw wires with excitations */ { Exci *ex; double c0[2],c1[2]; if (numexcis==0) return; SetForeground(&c_exci); SetLineAttributes(4, GDK_LINE_SOLID, GDK_CAP_ROUND, GDK_JOIN_ROUND); for (ex=excis;exwire, ex->seg, c0, c1); DrawLine(c0[0], c0[1], c1[0], c1[1]); } } void draw_netws(void) /* (re)draw networks and transmission lines */ { Netw *ne; double c10[2],c11[2],c20[2],c21[2]; /* endpoints of relevant wires */ if (numnetws==0) return; SetForeground(&c_netw); for (ne=netws;newire1, ne->seg1, c10, c11); proj_segment(ne->wire2, ne->seg2, c20, c21); if (ne->type==1) { SetLineAttributes(1, GDK_LINE_SOLID, GDK_CAP_ROUND, GDK_JOIN_ROUND); DrawLine(c10[0], c10[1], c20[0], c20[1]); DrawLine(c11[0], c11[1], c21[0], c21[1]); } else if (ne->type==-1) { SetLineAttributes(1, GDK_LINE_SOLID, GDK_CAP_ROUND, GDK_JOIN_ROUND); DrawLine(c10[0], c10[1], c21[0], c21[1]); DrawLine(c11[0], c11[1], c20[0], c20[1]); } else { double d1[2],d2[2]; interpolate(d1,0.5,c10,c11); interpolate(d2,0.5,c20,c21); SetLineAttributes(2, GDK_LINE_SOLID, GDK_CAP_ROUND, GDK_JOIN_ROUND); DrawLine(d1[0], d1[1], d2[0], d2[1]); } } } void draw_surfaces(void) { Surface *su; double c0[2],c1[2],c2[2],c3[2]; double cc[2]; if (numsurfaces==0) return; SetLineAttributes(0, GDK_LINE_SOLID, GDK_CAP_ROUND, GDK_JOIN_ROUND); for (su=surfaces;supa.x-su->pc.x) + eyevec.y * (su->pa.y-su->pc.y) + eyevec.z * (su->pa.z-su->pc.z) >= 0 ) SetForeground(&c_surf); else SetForeground(&c_back); proj(&su->pc,cc); proj(&su->p0,c0); proj(&su->p1,c1); switch (su->type) { case SU_rect: case SU_arb: c2[0]=2*cc[0]-c0[0]; c2[1]=2*cc[1]-c0[1]; c3[0]=2*cc[0]-c1[0]; c3[1]=2*cc[1]-c1[1]; break; case SU_quad: proj(&su->p3,c3); /* fallthrough */ case SU_tri: proj(&su->p2,c2); break; } if (su->type==SU_tri) { /* triangle; draw border: */ DrawLine(c0[0], c0[1], c1[0], c1[1]); DrawLine(c1[0], c1[1], c2[0], c2[1]); DrawLine(c2[0], c2[1], c0[0], c0[1]); } else { /* joint drawing code for all other shapes, which are drawn as various quadrilaterals */ /* draw diagonals: */ DrawLine(c0[0], c0[1], c2[0], c2[1]); DrawLine(c1[0], c1[1], c3[0], c3[1]); if (su->type!=SU_arb) { /* draw border: */ DrawLine(c0[0], c0[1], c1[0], c1[1]); DrawLine(c1[0], c1[1], c2[0], c2[1]); DrawLine(c2[0], c2[1], c3[0], c3[1]); DrawLine(c3[0], c3[1], c0[0], c0[1]); } else { /* draw additional diagonals */ DrawLine(0.7071*(c0[0]+c1[0])-0.4142*cc[0], 0.7071*(c0[1]+c1[1])-0.4142*cc[1], 0.7071*(c2[0]+c3[0])-0.4142*cc[0], 0.7071*(c2[1]+c3[1])-0.4142*cc[1]); DrawLine(0.7071*(c0[0]+c3[0])-0.4142*cc[0], 0.7071*(c0[1]+c3[1])-0.4142*cc[1], 0.7071*(c2[0]+c1[0])-0.4142*cc[0], 0.7071*(c2[1]+c1[1])-0.4142*cc[1]); } } } } void draw_antenna(int ie) /* draw the entire antenna */ { draw_wires(); draw_loads(); draw_excis(); draw_netws(); if (Pending() && ie) return; draw_surfaces(); } /* ------------------------- currents/charges drawing -------------------------------------------------------------------- */ int cmpsegdist(const Segcur **s1, const Segcur **s2) { double d; d= (*s1)->dist - (*s2)->dist; if (d<0) return -1; if (d>0) return 1; return 0; } void addvector(Point *pe,float a[3],double q) { pe->x+=q*a[0]; pe->y+=q*a[1]; pe->z+=q*a[2]; } void draw_currents_anim(int ie,Currents *cu) /* draw the currents and charge distribution as vectors and circles with animation */ { Segcur *se; double si,co; double sc; double l; if (cu==NULL || cu->numseg==0) return; l=0; l+=(cu->s[0].p1.x-cu->s[0].p0.x)*(cu->s[0].p1.x-cu->s[0].p0.x); l+=(cu->s[0].p1.y-cu->s[0].p0.y)*(cu->s[0].p1.y-cu->s[0].p0.y); l+=(cu->s[0].p1.z-cu->s[0].p0.z)*(cu->s[0].p1.z-cu->s[0].p0.z); l=sqrt(l); si = sin(-animphase); co = cos(-animphase); sc = iscale * 0.8 * l / cu->maxI; for (se=cu->s;ses+cu->numanimseg;se++) { double c0[2],c1[2]; double q; Point pe; double ev[3]; int i; for (i=0;i<3;i++) ev[i] = se->re[i]*co + se->im[i]*si; q = qscale * l * 0.5 * (se->qre*co + se->qim*si) / cu->maxQ; pe.x = se->c.x + sc*ev[0]; pe.y = se->c.y + sc*ev[1]; pe.z = se->c.z + sc*ev[2]; proj(&se->c,c0); proj(&pe,c1); SetLineAttributes(2, GDK_LINE_SOLID, GDK_CAP_ROUND, GDK_JOIN_ROUND); SetForeground(&c_wire); DrawLine(c0[0], c0[1], c1[0], c1[1]); SetLineAttributes(1, GDK_LINE_SOLID, GDK_CAP_ROUND, GDK_JOIN_ROUND); if (q<0) SetForeground(&c_qneg); else SetForeground(&c_qpos); q=fabs(q); pe=se->c; addvector(&pe,se->q0,q); proj(&pe,c0); addvector(&pe,se->q0,-q); addvector(&pe,se->q1,q); proj(&pe,c1); DrawLine(c0[0], c0[1], c1[0], c1[1]); addvector(&pe,se->q0,-q); addvector(&pe,se->q1,-q); proj(&pe,c0); DrawLine(c0[0], c0[1], c1[0], c1[1]); addvector(&pe,se->q0,q); addvector(&pe,se->q1,-q); proj(&pe,c1); DrawLine(c0[0], c0[1], c1[0], c1[1]); addvector(&pe,se->q0,q); addvector(&pe,se->q1,q); proj(&pe,c0); DrawLine(c0[0], c0[1], c1[0], c1[1]); } } void draw_currents_noanim(int ie,Currents *cu) /* draw the currents distribution as a static phase/magnitude display */ { Segcur *se; Segcur **sse; int i; double c0[2],c1[2]; double a=0,phase; double dx,dy,l; double w; #ifdef GAINCALC double totalr=0,totali=0; #endif if (cu==NULL || cu->numseg==0) return; sse=malloc(cu->numseg*sizeof(Segcur*)); if (sse==NULL) return; /* first, calculate the distance of each segment (defined in xnecview.h), and at the same time fill the array **sse that will be sorted next */ se=cu->s; for (i=0;inumseg;i++) { se->dist=Zx*se->c.x+Zy*se->c.y+Zz*se->c.z; sse[i]=se; se++; } /* next, sort the array elements according to this distance, so the element closest to the observer will be drawn last */ qsort(sse, cu->numseg, sizeof(Segcur*), (int (*)(const void *, const void *))cmpsegdist); w = 360/(299.8/neco[rp_index].f); for (i=0;inumseg;i++) { se=sse[i]; proj(&se->p0,c0); proj(&se->p1,c1); dx=c1[0]-c0[0]; dy=c1[1]-c0[1]; if (!quick) { l=sqrt(dx*dx+dy*dy); if (l==0) continue; phase=se->phase; switch (polarization) { case POLnone: case POLcolour: a=se->a; break; case POLhor: a=se->a*dx/l; break; case POLvert: a=se->a*dy/l; break; case POLlhcp: a=se->a; phase+=180/M_PI*atan2(dy,dx); break; case POLrhcp: a=se->a; phase-=180/M_PI*atan2(dy,dx); break; } if (a<0) { phase+=180; a=-a; } if (distcor) phase+=w*se->dist; phase+=phaseoffset; while (phase<0) phase+=360; while (phase>=360) phase-=360; } if (!quick && maxthickness*a>=0.5) { SetLineAttributes(maxthickness*a+0.5, GDK_LINE_SOLID, GDK_CAP_ROUND, GDK_JOIN_ROUND); SetForeground(c_currents+(int)(phase*NC_phase/360)); } else { SetLineAttributes(1, GDK_LINE_SOLID, GDK_CAP_ROUND, GDK_JOIN_ROUND); SetForeground(&c_inactive); } DrawLine(c0[0], c0[1], c1[0], c1[1]); #ifdef GAINCALC totalr+=a*l*cos(phase*M_PI/180); totali+=a*l*sin(phase*M_PI/180); #endif } #ifdef GAINCALC totalr = sqrt(totalr*totalr+totali*totali); printf("--> %12g %12g\n",totalr,20*log10(totalr)); #endif free(sse); } void draw_phasecircle(void) { int xc,yc; /* coordinates of center */ int r; /* radius */ int i,j,x,y; double phase,step; double rc,rs; double sintab[NC_phase/4]; double costab[NC_phase/4]; r=winsize/10; xc=yc=r+r/2+1; SetLineAttributes(0, GDK_LINE_SOLID, GDK_CAP_ROUND, GDK_JOIN_ROUND); step = 1/(2*M_PI*r) * 360; for (i=0;i0); } /* ------------------------- near field drawing -------------------------------------------------------------------------- */ void draw_nearfield(int ie,NECoutput *ne) /* draw the E and H field and Poynting vectors */ { double f; Nearfield *nf; double sce,sch,scp; double si,co; nf = ne->nf; if (nf==NULL) return; if (nf->next==NULL) { f = 1; } else { f = (nf->next->p.x-nf->p.x)*(nf->next->p.x-nf->p.x); f += (nf->next->p.y-nf->p.y)*(nf->next->p.y-nf->p.y); f += (nf->next->p.z-nf->p.z)*(nf->next->p.z-nf->p.z); f = sqrt(f); } sce=f*escale/ne->maxe; sch=f*hscale/ne->maxh; scp=2*f*escale*hscale/(ne->maxe*ne->maxh); SetLineAttributes(0, GDK_LINE_SOLID, GDK_CAP_ROUND, GDK_JOIN_ROUND); si = sin(-animphase); co = cos(-animphase); while (nf) { Point pe; double ev[3], hv[3], pv[3]; double c0[2],c1[2]; int valid; proj(&nf->p,c0); valid=1; if (nf->evalid) { int i; for (i=0;i<3;i++) ev[i] = nf->ere[i]*co + nf->eim[i]*si; pe.x = nf->p.x + sce*ev[0]; pe.y = nf->p.y + sce*ev[1]; pe.z = nf->p.z + sce*ev[2]; proj(&pe,c1); SetForeground(&c_efield); DrawLine(c0[0], c0[1], c1[0], c1[1]); } else valid=0; if (nf->hvalid) { int i; for (i=0;i<3;i++) hv[i] = nf->hre[i]*co + nf->him[i]*si; pe.x = nf->p.x + sch*hv[0]; pe.y = nf->p.y + sch*hv[1]; pe.z = nf->p.z + sch*hv[2]; proj(&pe,c1); SetForeground(&c_hfield); DrawLine(c0[0], c0[1], c1[0], c1[1]); } else valid=0; if (valid && show_p) { pv[0] = ev[1]*hv[2] - ev[2]*hv[1]; pv[1] = ev[2]*hv[0] - ev[0]*hv[2]; pv[2] = ev[0]*hv[1] - ev[1]*hv[0]; pe.x = nf->p.x + scp*pv[0]; pe.y = nf->p.y + scp*pv[1]; pe.z = nf->p.z + scp*pv[2]; proj(&pe,c1); SetForeground(&c_poynting); DrawLine(c0[0], c0[1], c1[0], c1[1]); } nf=nf->next; } } /* ------------------------- gain pattern drawing (non-opaque) ----------------------------------------------------------- */ void draw_polref(void) { SetForeground(&c_gain_lin); DrawString(winsizex-5,5+0*fontheight,"linear",1,1); SetForeground(&c_gain_lhcp); DrawString(winsizex-5,5+1*fontheight,"left-hand circular",1,1); SetForeground(&c_gain_rhcp); DrawString(winsizex-5,5+2*fontheight,"right-hand circular",1,1); } void setpolcolour(double axial) { if (axial>Polthr) SetForeground(&c_gain_lhcp); else if (axial<-Polthr) SetForeground(&c_gain_rhcp); else SetForeground(&c_gain_lin); } void draw_gain_theta(int i,Radpattern *rp) { int j; double c0[2],c1[2]; double a0,a1; proj(&rp->gpo[0][i],c1); a1=rp->gain[0][i].axial; for (j=1;jnumphi;j++) { c0[0]=c1[0]; c0[1]=c1[1]; proj(&rp->gpo[j][i],c1); if (polarization==POLcolour) { a0=a1; a1=rp->gain[j][i].axial; setpolcolour((a0+a1)/2); } DrawLine(c0[0], c0[1], c1[0], c1[1]); } c0[0]=c1[0]; c0[1]=c1[1]; proj(&rp->gpo[0][i],c1); if (polarization==POLcolour) { a0=a1; a1=rp->gain[0][i].axial; setpolcolour((a0+a1)/2); } DrawLine(c0[0], c0[1], c1[0], c1[1]); } void draw_gain_phi(int j,Radpattern *rp) { int i; double c0[2],c1[2]; double a0,a1; proj(&rp->gpo[j][0],c1); a1=rp->gain[j][0].axial; for (i=1;inumtheta;i++) { c0[0]=c1[0]; c0[1]=c1[1]; proj(&rp->gpo[j][i],c1); if (polarization==POLcolour) { a0=a1; a1=rp->gain[j][i].axial; setpolcolour((a0+a1)/2); } DrawLine(c0[0], c0[1], c1[0], c1[1]); } } #define R90 (M_PI/2) #define R180 M_PI #define R270 (1.5*M_PI) #define R360 (2*M_PI) #include void draw_gain(int ie,Radpattern *rp) { int i,j; int gainplot2=gainplot; SetForeground(&c_gain); SetLineAttributes(0, GDK_LINE_SOLID, GDK_CAP_ROUND, GDK_JOIN_ROUND); if (gainplot==GPopaque && !quick) { Radpattern *p; int ok=0; p=rp; while (p) { if (!opaque_impossible(p)) { draw_opaque(p); ok=1; } p=p->next; } if (ok) return; fprintf(stderr,"Opaque gain plot not possible; reason: %s.\n",opaque_impossible(rp)); gainplot2=GPframe; } while (rp) { if (gainplot2==GPframe && !quick) { for (i=0;inumtheta;i++) draw_gain_theta(i,rp); if (Pending() && ie) return; for (j=0;jnumphi;j++) draw_gain_phi(j,rp); } else { if (!scaleplot || theta!=90) /* suppress if scaleplotting and XY-plane is perpendicular to screen */ for (i=0;inumtheta;i++) if (fabs(rp->gtheta[i]-R90)<0.001) /* points in XY-plane? */ draw_gain_theta(i,rp); for (j=0;jnumphi;j++) if (( ( (fabs((rp->gphi[j]+R90)-R90)<0.001 || fabs((rp->gphi[j])-R180)<0.001) /* points in XZ-plane? */ && (!scaleplot || (phi!=0 && theta!=0)) ) /* suppress if that plane is perpendicular to screen and we're plotting the scale */ ) || ( ( fabs((rp->gphi[j])-R90)<0.001 || fabs((rp->gphi[j])-R270)<0.001) /* points in YZ-plane? */ && (!scaleplot || (phi!=90 && theta!=0) ) /* suppress if that plane is perpendicular to screen and we're plotting the scale */ )) draw_gain_phi(j,rp); } rp=rp->next; } } void draw_gainscale(int ie) { double a; Point po; double c0[2],c1[2],orig[2]; #define Npt 90 #define da R360/Npt static double si[Npt],co[Npt]; static int firsttime=1; static double radii[numGS][6]= /* this array determines the radius of all gain scale circles; note: if you change this, also update the labels in GSnumbers[][] and in labels, here below */ { { 1, 0.794328, 0.501187, 0.251189, 0.1, 0 }, /* lin.power: 0, -1, -3, -6, -10 dB */ { 1, 0.891251, 0.707946, 0.501187, 0.316228, 0 }, /* lin.voltage: 0, -1, -3, -6, -10 dB */ { 1, 0.839624, 0.558406, 0.311817, 0.174121, 0 }, /* arrl: 0, -3, -10, -20, -30 dB */ { 1, 0.75, 0.5, 0.25, 0 } /* log: 0, -10, -20, -30 dB */ } ; static char labels[numGS][6][4]= { { "0dB", "-1", "-3", "-6", "-10" }, { "0dB", "-1", "-3", "-6", "-10" }, { "0dB", "-3", "-10", "-20", "-30" }, { "0dB", "-10", "-20", "-30" } } ; int i,j; double r; double *rp; if (!scaleplot) return; SetForeground(&c_scale); SetLineAttributes(0, GDK_LINE_SOLID, GDK_CAP_ROUND, GDK_JOIN_ROUND); if (firsttime) { for (i=Npt-1, a=0; i>=0; i--, a+=da) { si[i]=sin(a); co[i]=cos(a); } firsttime=0; } if (phi!=90 && theta!=0) { /* draw radial lines (yz plane, 10 degrees spacing) */ po.x=0; po.y=0; po.z=0; proj(&po,orig); for (i=0;i<360;i=i+10) { po.x=0; po.y=extr*cos(i*M_PI/180); po.z=extr*sin(i*M_PI/180); proj(&po,c1); DrawLine(orig[0],orig[1],c1[0],c1[1]); } /* draw circles */ rp=radii[gainscale]; j=0; while ((r=*rp++*extr)) { po.x=0; po.y=r; po.z=0; proj(&po,c0); for (i=0;i=c0[0] || !ascending(c0,c1)) DrawStringLL(c1[0]+2,c1[1],"X"); else DrawStringUL(c1[0]+2,c1[1],"X"); if (c2[0]>=c0[0] || !ascending(c0,c2)) DrawStringLL(c2[0]+2,c2[1],"Y"); else DrawStringUL(c2[0]+2,c2[1],"Y"); if (c3[0]>=c0[0] || !ascending(c0,c3)) DrawStringLL(c3[0]+2,c3[1],"Z"); else DrawStringUL(c3[0]+2,c3[1],"Z"); } SetLineAttributes(0, GDK_LINE_ON_OFF_DASH, GDK_CAP_ROUND, GDK_JOIN_ROUND); pp.x=0; pp.y=0; pp.z=0; proj(&pp,c0); pp.x=-Axislen*extr; pp.y=0; pp.z=0; proj(&pp,c1); DrawLine(c0[0], c0[1], c1[0], c1[1]); pp.x=0; pp.y=-Axislen*extr; pp.z=0; proj(&pp,c1); DrawLine(c0[0], c0[1], c1[0], c1[1]); pp.x=0; pp.y=0; pp.z=-Axislen*extr; proj(&pp,c1); DrawLine(c0[0], c0[1], c1[0], c1[1]); } void draw_all(int ie) { char s[100]; do { if (Pending() && ie) return; ClearWindow(); if (gainplot==GPslice || gainplot==GPframe || gainplot==GPopaque) draw_gainscale(ie); draw_axes(); if (Pending() && ie) break; if (structplot) { if (structplot==SPcurrents) { if (rp_index>=0) { draw_currents_noanim(ie,neco[rp_index].cu); if (!quick) draw_phasecircle(); } } else if (structplot==SPanim) { if (rp_index>=0) { draw_currents_anim(ie,neco[rp_index].cu); } } else draw_antenna(ie); } if (Pending() && ie) break; if (rp_index>=0) { if (gainplot==GPslice || gainplot==GPframe || gainplot==GPopaque) { draw_gain(ie,neco[rp_index].rp); if (polarization==POLcolour) draw_polref(); } if (gainplot==GPnearfield) draw_nearfield(ie,neco+rp_index); if (neco[rp_index].rp) { double maxgain,vgain; if (polarization==POLnone || polarization==POLcolour) { maxgain=neco[rp_index].d[neco_maxgain], vgain=neco[rp_index].d[neco_vgain]; } else { maxgain=neco[rp_index].d[Neco_polgain+Neco_gsize*polarization]; vgain=neco[rp_index].d[neco_vgain2]; } sprintf(s,"f = %g MHz maxgain = %g dBi vgain = %g dBi", neco[rp_index].f, maxgain, vgain); } else { sprintf(s,"f = %g MHz", neco[rp_index].f); } SetForeground(&c_axis); DrawString(winsizex/2,winsizey,s,0.5,0); } } while(0); out->Complete(); } xnecview-1.35.orig/draw_opaque.c0000644000175000017500000011732411005110762015457 0ustar afrb2afrb2/* XNECVIEW - a program for visualizing NEC2 input and output data * * Copyright (C) 2002-2005, Pieter-Tjerk de Boer -- pa3fwm@amsat.org * * Distributed on the conditions of version 2 of the GPL: see the files * README and COPYING, which accompany this source file. * * This module exclusively contains code for drawing the gain pattern as * an opaque surface, i.e., with hidden-line removal. * */ /******************************************************************************* The principle of the algorithm used here can be summarized as follows: a) draw parts of the gain surface in such an order, that for any bit of screen area, from all parts of the gain surface that appear on that bit of screen, the part closest to the observer is drawn first. b) keep track of which parts of the screen have already been covered by the parts of the gain surface drawn so far, and of any new part of the gain surface to be drawn, only draw those parts that are outside the parts of the screen already covered. This "covered area" is henceforth called "mask". Clearly, these two rules guarantee that only the visible parts of the gain surface are drawn. The above is deliberately vague about "parts of the gain surface". What are these? Well, when drawing a wire frame, we're simply drawing line segments. However, (infinitely thin) lines by themselves do not cover any area on the screen, so doing rule (b) would be hard on the basis of line segments. Actually, areas on the screen are covered by the quadrangles formed by those lines. So: we're drawing line segments, but we need to keep track of the screen area covered by the quadrangles formed by those line segments. How do we implement an ordering as described by rule (a) ? One way would be to sort the line segments or quadrangles according to their distance from the viewer, i.e., their position on an axis perpendicular to the screen. However, this may easily lead to rule (b) needing to keep track of multiple disjoint areas on the screen. Since the gain surface we're drawing is a single-valued function of the angular coordinates (phi and theta), a different way to satisfy rule (a) is sorting the line segments or quadrangles by the angle a line from the origin to them makes with a line from the origin to the viewer. Basically, start with the quadrangle that is between the viewer and the origin (i.e., the antenna gain in the direction toward the viewer), then continue "outward" from this initial quadrangle. One can easily verify that indeed rule (a) is satisfied. Furthermore, since we start with the quadrangle around the origin and work outward from there, it is also clear that the "covered area" that rule (b) is about, will be one single area. It would be even nicer (and efficient for the calculations), if the boundary of this single "covered area" (mask) would be a single-valued function of an angular coordinate on the screen. In other words: starting from the projection of the origin on the screen and moving outward on the screen along a straight line in any direction, one should cross the boundary of the mask exactly once. This can be achieved without violating rule (a) by working one's way through the quadrangles in the right order; some more details are in the comments in the function draw_opaque(), below. Note that the above algorithm bears quite a few similarities to the "obvious" way of plotting a function of two variables in a 3D Cartesian coordinate system: start with the front-most line, and keep track of the screen area already covered. See e.g. Section 15.1 in Foley/van Dam/Feiner/Hughes, 1996. In fact, the gain pattern is also a function of two variables, only the coordinate system is different. A completely different approach would be to draw the quadrangles starting with the ones farthest away, and drawing them as actually *filled* polygons. Thus, the front-most polygon will simply erase the ones farther away if they occupy the same screen position. I decided not to use this algorithm because of the large number of unnecessary drawing operations involved; I hope my algorithm is faster, although I didn't compare them. A note about coordinates used in this file: x,y are generally the on-screen coordinates, except for a translation: the projection of the origin is at x=0, y=0. The offset with the true screen coordinates (in which the origin may be anywhere inside or outside the window) is done at the final call to DrawLine(). These coordinates are used for actually drawing line segments, and for calculating intersections of new quadrangle boundaries with the mask. a is defined as ATAN(y,x), with ATAN(y,x) as #define'd below. Basically, it is the angle (in radians) of a line (on screen) from the origin to the point x,y, with some reference line. With the present definition of ATAN: a=0 is y>0, x=0 (i.e. "south" on the screen) a=pi/2 is x<0, y=0 (i.e. "east" on the screen) etc. This coordinate is used for sorting the points that describe the polygon which is the mask. s is defined as x*x+y*y . It is simply the square of the distance of x,y from the (on-screen) origin. It is only used to speed up decisions about (in)visibility; e.g., if both endpoints of a new line segment have s values less than the smallest s value presently in the mask, the new line segment surely is completely hidden by the mask. Finally: many of the complications in the implementation are caused by the possibility that points coincide exactly, or have exactly the same a value; particularly, this may happens with points on the Z axis. Of course, floating point arithmetic is not exact, so checking for equality is not a good idea. On the other hand, checking for "approximate equality" may take more time, and may also cause problems by itself. An additional problem is the fact that the Maskpoints are sorted by their a value (after all, the mask is a function of a), but there may be purely radial segments in the mask, so more sorting is needed than can be derived from the a values. The present implementation seems to work correctly in all examples I tried, but there might very well still be a case where it doesn't work correctly; if you run into it, please let me know. Also, perhaps on other architectures than i386 or with a different compiler, it might not work correctly, if the tests for equality of floats are handled differently. Again, please let me know if you encounter problems. *******************************************************************************/ #include #include #include #include #include #include #include #include #include "xnecview.h" /* #defining DEBUG prints lots of information about the drawing process, and regularly pauses the drawing waiting for a keypress: '>' does the next step, '<' first draws the present version of the mask. */ #undef DEBUG /* #defining MEASURE enables code that draw the picture 100 times and measures the time */ #undef MEASURE #ifdef DEBUG #define PRINTF(a) printf a /**/ #define DEBUGPAUSE \ { \ double aa=animphase; \ printf("i=%i j=%i theta=%f phi=%f \n",i,j,rp->gtheta[i]*180/M_PI,rp->gphi[j]*180/M_PI); \ out->Complete(); \ do { gtk_main_iteration_do(1); } while (aa==animphase); \ if (animphaseDrawLine((a),(b),(c),(d)); } #endif /* ------------------------- separate versions of calcproj() and proj() -------------------------------------------------- */ double ATAN(double y,double x) { double a; a=-atan2(x,y); if (a==M_PI) return -M_PI; if (fabs(a)<1e-7) return 0; return a; } /* this data type is used to store all relevant information about a point in the gain pattern as projected onto the 2D-screen: */ typedef struct { double x,y; /* x,y,a,s: see above description of the coordinates */ double a; double s; double c; /* this variable is the axial ratio of the radiation in this direction, and is used (in the coloured polarization mode) for determining the colour of the line segments */ void *maskpoint; /* pointer to the corresponding entry in the mask polygon, if this point presently is a corner point of the mask */ } Hidpoint; /* we have a separate projection matrix, since we sometimes need to change the viewing direction very slightly */ double Xx2,Xy2,Yx2,Yy2,Yz2; /* projection matrix */ double Xtr2,Ytr2; /* 2D-translation */ double phi2,theta2; /* in radians! */ static void inline calcproj2(void) /* similar to calcproj(), but uses phi2 and theta2 */ { double scale; scale=winsize*zoom/(3.0*extr); Yz2 = -scale*sin(theta2); Xx2 = -scale*sin(phi2); Xy2 = scale*cos(phi2); Yx2 = scale*cos(phi2)*cos(theta2); Yy2 = scale*sin(phi2)*cos(theta2); Xtr2 = 0.5 + trx*zoom*winsize + 0.5*winsizex; Ytr2 = 0.5 + try*zoom*winsize + 0.5*winsizey; } static void inline proj2(Point *p,Hidpoint *hp) { hp->x = Xx2*p->x + Xy2*p->y; hp->y = Yx2*p->x + Yy2*p->y + Yz2*p->z; hp->a=ATAN(hp->y,hp->x); hp->s=hp->x*hp->x+hp->y*hp->y; } /* ------------------------- handling of the mask ----------------------------------------------------- */ /* data structure for one corner of the polygon that is the mask: */ typedef struct _Maskpoint { double x,y; /* x,y,a,s: see coordinate description above */ double a; double s; struct _Maskpoint *next; /* pointer to the next point in the mask (sorted on a) */ short int del; /* flag: if !=0, this maskpoint is to be deleted at the next commit_mask() call */ short int after; /* flag, only used in the *new list: if set, means this Maskpoint should be inserted after (rather than before) any already existing point(s) with the samen a value; -1 means unknown, let after-flag of other point decide */ Hidpoint *hp; /* pointer to the corresponding Hidpoint, if any */ } Maskpoint; Maskpoint *Q; /* points to begin of linked list of Maskpoints, ordered by increasing a */ Maskpoint *Q2; /* pointer into that list, pointing at last Maskpoint with a<0; used to speed up searching the list */ Maskpoint *new; /* pointer to linked list of Maskpoints that need to be included into the list at the next commit_mask() call */ Maskpoint *spare; /* pointer to linked list of Maskpoint buffers that are available for use; this prevents us from needing to call malloc() and free() too often */ double min_s=0; /* roughly: lowest value of s currently in the mask More precisely: min_s is never higher than the lowest s value corresponding to any point on the mask; note that this lowest s value may well occur not on a Maskpoint, but somewhere in between; in recalc_min_s(), this lowest s value is not caculated exactly, but a lower bound is calculated. min_s is used to quickly detect lines that are well within the mask, so they can be skipped without further analysis. */ Maskpoint *min_s_mp=NULL; /* Maskpoint that had the lowest s value contributing to min_s; if this Maskpoint is deleted from the list, min_s should be recalculated since it probably can increase */ int left_half=0, right_half=0; /* two flags, indicating whether we're at the moment drawing on the left half (x<0), right half (x>0) or possibly both. This min_s value above is actually calculated only for the active half(es) */ /* a handy macro for traversing the linked list as if head and tail were joined (after all, a is an angular coordinate): */ #define NEXT(p) ( (p)->next ? (p)->next : Q ) static void alloc_sparemaskpoints(void) { int i; spare=mymalloc(100*sizeof(Maskpoint)); for (i=0;i<99;i++) spare[i].next=spare+i+1; spare[99].next=NULL; } static void inline mpfree(Maskpoint *p) { p->next=spare; spare=p; } static void cleanup_mask(void) { Maskpoint *p; while (Q) { p=Q; Q=Q->next; mpfree(p); } } static Maskpoint *newmaskpoint(double x,double y,double a,double s,Hidpoint *hp) { Maskpoint *mp; if (!spare) alloc_sparemaskpoints(); mp=spare; spare=mp->next; PRINTF(("newmaskpoint %f %f %f %p\n",x,y,s,hp)); mp->x=x; mp->y=y; mp->a=a; mp->s=s; mp->next=NULL; mp->del=0; mp->after=0; mp->hp=hp; return mp; } static void init_mask(void) { if (!spare) alloc_sparemaskpoints(); new=NULL; min_s=0; left_half=right_half=1; } static void add_to_mask(Maskpoint *q) { Maskpoint *p; p=Q; PRINTF(("============ add_to_mask: %g %g\n",q->a,p->a)); if (q->a>=0) p=Q2; else if (q->a <= p->a) { if (fabs(p->x-q->x)<1e-8 && fabs(p->y-q->y)<1e-8) { mpfree(q); return; } if (q->a==p->a && q->after==-1) q->after=!p->after; if (q->a==p->a && q->after) { while (p->next->a==q->a) p=p->next; q->next=p->next; p->next=q; if (q->hp) q->hp->maskpoint=q; return; } q->next=p; Q=q; if (q->hp) q->hp->maskpoint=q; return; } while (p->next && p->next->aa) p=p->next; if (!p->next) { p->next=q; q->next=NULL; if (q->hp) q->hp->maskpoint=q; return; } if (fabs(p->x-q->x)<1e-8 && fabs(p->y-q->y)<1e-8 && !p->del) { mpfree(q); return; } if (p->next && fabs(p->next->x-q->x)<1e-8 && fabs(p->next->y-q->y)<1e-8 && !p->next->del) { mpfree(q); return; } if (q->a==p->next->a && q->after==-1) q->after=!p->next->after; if (q->a==p->next->a && q->after) { while (p->next && p->next->a==q->a) p=p->next; q->next=p->next; p->next=q; if (q->hp) q->hp->maskpoint=q; return; } q->next=p->next; p->next=q; if (q->hp) q->hp->maskpoint=q; if (q->a<0 && q->a>Q2->a) Q2=q; } static void add_to_newlist(Maskpoint *q) { if (new && new->hp && q->hp==new->hp) { PRINTF(("============ add_to_newlist: dropped (%p,%p)\n",q->hp,new->hp)); mpfree(q); return; } q->next=new; new=q; } int find_in_mask(Hidpoint *h, Maskpoint **pp, Hidpoint *h1) /* For a given point *h, search the corresponding entry in the mask. Return values: 0 = *h lies inside mask; *pp points to maskpoint just before *h 1 = *h is one of the mask's corner points, *pp points to this maskpoint, and *h1 is such that initial segment of *h--*h1 is invisible 2 = as 1, except that initial segment is visible 3 = *h lies outside mask *pp is as follows: - if a Maskpoint coincides with *h, *pp is it - if the last two Maskpoint "before" (i.e., just counter-clockwise from) *h have the same a value, *pp is the first of these - otherwise, *pp is the last Maskpoint "before" *h */ { Maskpoint *p,*q; double a; double dx,dy; double x,y; x=h->x; y=h->y; a=h->a; if (h->maskpoint) { p=h->maskpoint; q=NEXT(p); goto found; } p=Q; if (a>=0) p=Q2; if (p->a>a) { while (p->next) p=p->next; } else { while (p->next && p->next->anext; if (p->next && p->next->a==a && !p->a==a) p=p->next; } q=NEXT(p); /* if (p->a==a && fabs(p->s-h->s)<1e-4) { */ if (p->a==a && p->s==h->s) { found: *pp=p; if (!h1) return 1; if (p->a==q->a) { if (q->ss) return 2; /* this assumes that *h1 is clockwise from *h */ else return 1; } dx=q->x-p->x; dy=q->y-p->y; a = (-dy*h1->x + dx*h1->y) / (-dy*p->x + dx*p->y); if (a<=1) return 1; return 2; } /* if (q->a==a && fabs(q->s-h->s)<1e-4) { */ if (q->a==a && q->s==h->s) { p=q; q=NEXT(p); goto found; } *pp=p; if (a==p->a && a==q->a) { if (h->s >= p->s && h->s >= q->s) return 3; if (h->s >= q->s && h1) return 3; /* this assumes that *h1 is clockwise from *h */ return 0; } dx=q->x-p->x; dy=q->y-p->y; a = (-dy*x + dx*y) / (-dy*p->x + dx*p->y); if (a<=1) return 0; return 3; } static void recalc_min_s(void) { Maskpoint *p,*q; double mm; Maskpoint *mmp; if (right_half) { p=Q; q=p; min_s=p->s; mmp=p; if (left_half) { while ((p=p->next)) { mm=(mmp=p)->s; if (mm>q->s) mm=(mmp=q)->s; mm*=cos((p->a-q->a)/2); if (mms; if (mm>q->s) mm=(mmp=q)->s; mm*=cos(drem(p->a-q->a,2*M_PI)/2); if (mmnext)) { mm=(mmp=p)->s; if (mm>q->s) mm=(mmp=q)->s; mm*=cos((p->a-q->a)/2); if (mma >= 1e-4) break; q=p; } } } else { p=Q2->next; min_s=p->s; mmp=p; q=p; while ((p=p->next)) { mm=(mmp=p)->s; if (mm>q->s) mm=(mmp=q)->s; mm*=cos((p->a-q->a)/2); if (mma < -M_PI+1e-4) { p=Q; mm=(mmp=p)->s; if (mm>q->s) mm=(mmp=q)->s; mm*=cos(drem(p->a-q->a,2*M_PI)/2); if (mmnext; add_to_mask(q); } pp=&Q; prev=NULL; while (*pp) { p=*pp; if (p->del) { need_recalc_min_s |= (p==min_s_mp); if (p==Q2) { Q2=prev; if (!Q2) Q2=Q; } if (p->hp) p->hp->maskpoint=NULL; p=*pp; *pp=p->next; mpfree(p); } else { pp=&(p->next); prev=p; } } if (need_recalc_min_s) recalc_min_s(); } #ifdef DEBUG static void draw_mask(void) /* for debugging purposes only: draw the mask (slightly enlarged, so it doesn't completely overwrite the actual lines) */ { Maskpoint *p=Q; double x0,y0; SetForeground(&c_hfield); x0=Q->x; y0=Q->y; PRINTF(("%8g %8g %8g\n",Q->x,Q->y,Q->a)); p=p->next; while (p) { DrawLine(1.01*x0+Xtr2, 1.01*y0+Ytr2, 1.01*p->x+Xtr2, 1.01*p->y+Ytr2); PRINTF(("%8g %8g %8g\n",p->x,p->y,p->a)); x0=p->x; y0=p->y; p=p->next; } DrawLine(1.01*x0+Xtr2, 1.01*y0+Ytr2, 1.01*Q->x+Xtr2, 1.01*Q->y+Ytr2); PRINTF(("left=%i right=%i min_s=%f Q2->a=%f\n",left_half, right_half,min_s,Q2->a)); } #endif /* ------------------------- code for actually drawing the non-masked parts of a line ----------------------------------------------------- */ static int intersect(Maskpoint *p,Maskpoint *q,double x0,double y0,double x1,double y1,double *xx,double *yy) /* calculate intersection of line between *p and *q and line between x0y0 and x1y1; coordinates of intersection go into *xx,*yy; return values: 0: no intersection 1: intersection, x0y0 visible 2: intersection, x0y0 not visible 3: endpoint identical */ { double t,tnum,tden; double nx,ny; double px,py; int type; PRINTF(("intersect: (%5f,%5f)-(%5f,%5f) , (%5f,%5f)-(%5f,%5f)", p->x,p->y, q->x, q->y, x0, y0, x1, y1)); if ((p->x==x0 && p->y==y0) || (q->x==x0 && q->y==y0)) { *xx=x0; *yy=y0; type=3; goto done; } if ((p->x==x1 && p->y==y1) || (q->x==x1 && q->y==y1)) { *xx=x1; *yy=y1; type=3; goto done; } /* part of the Cyrus-Beck algorithm for calculating intersections; see Foley/van Dam/Feiner/Hughes, 1996 */ nx = q->y - p->y; ny = p->x - q->x; tnum = nx*(x0-p->x) + ny*(y0-p->y); tden = -nx*(x1-x0) -ny*(y1-y0); if (tden==0) { /* parallel lines */ type=0; } else { t = tnum/tden; if (t<=0 || t>=1) { type=0; } else { /* here we depart from Cyrus-Beck, since we're only trying to intersect with one line segment */ double a; *xx = x0+t*(x1-x0); *yy = y0+t*(y1-y0); px = q->x - p->x; py = q->y - p->y; a = px* *xx + py* *yy; if (a <= px*p->x+py*p->y || a >= px*q->x+py*q->y) { type=0; } else { if (tden>0) type=1; else type=2; } } } done: PRINTF((" -> %i (%5f,%5f)\n", type, *xx, *yy)); return type; } int cnt0=0,cnt1=0; static void draw_masked1(Hidpoint *c0,Hidpoint *c1) /* actually draw a (masked) line between two points; the mask is not yet updated, but preparations are made so the next commit_mask() call will update the mask. */ { Maskpoint *p0,*p1,*p; double x,y; int i0,i1; int visible; int skip=0; double a0,a1; double x0,x1,y0,y1; double s0,s1; double s; if (c0->s < min_s && c1->s < min_s) return; a0=c0->a; a1=c1->a; if ((a0>a1) ^ (fabs(a0-a1)>M_PI)) { /* if needed, exchange endpoints to make sure that we're moving in a clockwise direction */ double h; Hidpoint *hh; h=a0; a0=a1; a1=h; hh=c0; c0=c1; c1=hh; } x0=c0->x; y0=c0->y; x1=c1->x; y1=c1->y; i0=find_in_mask(c0,&p0,c1); i1=find_in_mask(c1,&p1,NULL); PRINTF(("draw_masked1(%f,%f,%f,%f) %i %i\n",x0,y0,x1,y1,i0,i1)); if (a0==a1) { /* exactly radial line; this needs to be handled as a special case, because letting the normal code handle it causes problems because the resulting Maskpoints can no longer be sorted sensibly based on their (identical!) a values. (Note: exactly radial lines are pretty unlikely, except on the z axis.) */ if (i0!=3 && i1!=3) return; if (i0+i1<6) { double xx,yy; int type; Maskpoint *mp; if (p0->a!=a0) { type=intersect(p0,NEXT(p0),x0,y0,x1,y1,&xx,&yy); if (type==1 || type==2) { mp=newmaskpoint(xx,yy,ATAN(yy,xx),xx*xx+yy*yy,NULL); mp->after=-1; /* note: we only add the intersection to the mask; the line's visible end point is also new, but here we wouldn't know the order of the two; that's also the reason we set after to -1 */ add_to_newlist(mp); if (i0!=3) { x0=xx; y0=yy; } else { x1=xx; y1=yy; } } } else { if (NEXT(p0)->a==a0) { if (p0->s>NEXT(p0)->s) { xx=p0->x; yy=p0->y; } else { xx=NEXT(p0)->x; yy=NEXT(p0)->y; } } else { xx=p0->x; yy=p0->y; } if (i0!=3) { x0=xx; y0=yy; } else { x1=xx; y1=yy; } } } DrawLine(x0+Xtr2, y0+Ytr2, x1+Xtr2, y1+Ytr2); return; } if (i0==3) { /* if the initial point is outside the mask, it must become a new corner point of the mask */ Maskpoint *mp; mp=newmaskpoint(x0,y0,a0,c0->s,c0); mp->after=1; add_to_newlist(mp); } if (p0->a==a0 && NEXT(p0)->a==a0) { /* if the initial point is at the same angle as a radial piece of the mask, there are several cases: - initial point == first mask corner point (out of the two that form that radial piece of mask), and initial segment visible: remove second mask point - initial point == first mask corner point, and initial segment not visible: no changes to mask - initial point is inside the mask: no changes - initial point is outside the mask: mask is extended, so drop second mask point (because our line segment is clock wise) - initial point == second mask corner point: it that case, p0==second mask corner point, and this code is not reached because the if-condition is not satisfied */ if (i0>=2) NEXT(p0)->del=1; /* In all cases except the last (but in that case we don't get here), we should make sure that p0 points to the second mask point, so the while()-loop starts with a non-radial segment. However, the next block also conditionally skips one point, so we need an extra condition here to prevent duplication. */ if (i0!=1 && i0!=2) p0=NEXT(p0); } if (i0==1 || i0==2) { /* if the initial point is a mask point, the following cases are possible: */ if (p0==p1) { /* final point is in next mask segment: only if final point is outside the mask, does anything need to be drawn; and we don't need the while loop for that */ if (i1!=3) return; skip=1; } else { /* final point is in later segment: this is just a normal case, except that we don't need to intersect with first mask segment, since that intersection would be the initial point */ p0=NEXT(p0); } } s0=c0->s; s1=c1->s; x=x0; y=y0; s=s0; if (s1>s) s=s1; visible = (i0>=2); if (polarization==POLcolour) setpolcolour((c0->c+c1->c)/2); p=p0; if (!skip) while (1) { /* this is a while loop over all mask segments that potentially intersect the line we're drawing; we keep track of whether our line is visible in the variable 'visible', calculating intersections as we go */ Maskpoint *q; double xx,yy; int type; q=NEXT(p); cnt0++; type=intersect(p,q,x,y,x1,y1,&xx,&yy); switch (type) { case 0: /* no intersection */ case 3: /* endpoint identical */ if (visible) { if (p!=p0 || i0==1 || i0==2) p->del=1; if ((i1==0 || i1==3) && p!=p1) q->del=1; if ((i1==1 || i1==2) && q!=p1) q->del=1; PRINTF(("visible, no intersection: %c%f %f %f %c%f\n",p->del?'*':' ',p->a,p0->a,p1->a,q->del?'*':' ',q->a)); } break; case 1: /* intersection, x0y0 visible */ DrawLine(x+Xtr2, y+Ytr2, xx+Xtr2, yy+Ytr2); x=xx; y=yy; s=x*x+y*y; if (s1>s) s=s1; visible=0; if (p==p0 && i0==2) p->del=1; add_to_newlist(newmaskpoint(xx,yy,ATAN(yy,xx),xx*xx+yy*yy,NULL)); break; case 2: /* intersection, x0y0 not visible */ x=xx; y=yy; s=x*x+y*y; if (s1>s) s=s1; visible=1; add_to_newlist(newmaskpoint(xx,yy,ATAN(yy,xx),xx*xx+yy*yy,NULL)); break; } if (p==p1) break; p=q; if ((i1==1 || i1==2 || a1==p1->a) && p==p1) break; } if (visible) { /* draw the last segment of the line, if it is visible */ DrawLine(x+Xtr2, y+Ytr2, x1+Xtr2, y1+Ytr2); add_to_newlist(newmaskpoint(x1,y1,a1,c1->s,c1)); PRINTF(("visible, testing for del: %i %g %g %g\n",i1, a1, p1->a, NEXT(p1)->a)); if ((i1==0 || i1==3) && p1->a==a1 && NEXT(p1)->a==a1) p1->del=1; } } /* ------------------------- finally: the code for drawing the gain pattern ----------------------------------------------------- */ static void first_quadrangle(Radpattern *rp,Hidpoint **hp,int *i0,int *i1,int *j0,int *j1) /* Draw the first quadrangle: this is the polygon which is intersected by the line from the origin to the viewer's eye, i.e., the line orthogonal to the screen or paper. Furthermore, this function fills the Hidpoint array; this is not really a logical place to do this, but: - we can't do it earlier since this function may modify phi2 a little to prevent the viewing direction from passing through an edge; - we already in this function need four elements of the Hidpoint array to actually draw the quadrangle; then it makes sense to calculate them all. */ { int i,j,k,l; Hidpoint h0,h1; Hidpoint *c0,*c1; int d; double y,yprev; /* find the rp->gphi value closest to the phi2 of the viewing direction: */ j=0; for (l=1;lnumphi;l++) if (fabs(drem(rp->gphi[l]-phi2,2*M_PI)) < fabs(drem(rp->gphi[j]-phi2,2*M_PI))) j=l; /* if almost identical to phi2, change phi2 a little */ if (fabs(drem(rp->gphi[j]-phi2,2*M_PI)) < 1e-6) { if (drem(rp->gphi[j]-phi2,2*M_PI) < 0) phi2-=1e-6; else phi2+=1e-6; phi2=drem(phi2,2*M_PI); calcproj2(); } /* choose j and l such that they are consecutive phi indices around the viewing direction */ if (drem(rp->gphi[j]-phi2,2*M_PI)<0) { l=(j+1)%rp->numphi; } else { l=j; j=(j-1+rp->numphi)%rp->numphi; } /* find the rp->gtheta value closest to the theta2 of the viewing direction: */ i=0; for (k=1;knumtheta;k++) if (fabs(rp->gtheta[k]-theta2) < fabs(rp->gtheta[i]-theta2)) i=k; /* now calculate the y (screen coordinates) of the intersection of the corresponding "horizontal" segment with the (on screen) vertical through the origin: */ proj2(&rp->gpo[j][i], &h0); proj2(&rp->gpo[l][i], &h1); y = ((h1.y-h0.y)/(h1.x-h0.x)) * (-h0.x) + h0.y; /* We need to choose subsequent theta indices such that the quadrangle surrounds the origin (in the projection on the screen). We test this by calculating the intersections of the "horizontal" segments with the (on-screen) vertical through the origin. */ if (y>0) d=-1; else d=+1; do { k=i; yprev=y; i+=d; proj2(&rp->gpo[j][i], &h0); proj2(&rp->gpo[l][i], &h1); y = ((h1.y-h0.y)/(h1.x-h0.x)) * (-h0.x) + h0.y; } while (y*d<0); if (i>k) { int h; double hh; h=i; i=k; k=h; hh=y; y=yprev; yprev=hh; } { /* fill the Hidpoint array */ int i,j; for (i=0;inumtheta;i++) for (j=0;jnumphi;j++) { proj2(&rp->gpo[j][i], &hp[j][i]); hp[j][i].c=rp->gain[j][i].axial; hp[j][i].maskpoint=NULL; } } /* draw the quadrangle, and set up the mask */ #ifdef DEBUG SetForeground(&c_wire); #endif c0=&hp[l][i]; Q2=Q=newmaskpoint(c0->x,c0->y,c0->a,c0->s,c0); c1=&hp[j][i]; add_to_mask(newmaskpoint(c1->x,c1->y,c1->a,c1->s,c1)); if (polarization==POLcolour) setpolcolour((c0->c+c1->c)/2); DrawLine(c0->x+Xtr2, c0->y+Ytr2, c1->x+Xtr2, c1->y+Ytr2); c0=&hp[j][k]; add_to_mask(newmaskpoint(c0->x,c0->y,c0->a,c0->s,c0)); if (polarization==POLcolour) setpolcolour((c0->c+c1->c)/2); DrawLine(c0->x+Xtr2, c0->y+Ytr2, c1->x+Xtr2, c1->y+Ytr2); c1=&hp[l][k]; add_to_mask(newmaskpoint(c1->x,c1->y,c1->a,c1->s,c1)); if (polarization==POLcolour) setpolcolour((c0->c+c1->c)/2); DrawLine(c0->x+Xtr2, c0->y+Ytr2, c1->x+Xtr2, c1->y+Ytr2); c0=&hp[l][i]; if (polarization==POLcolour) setpolcolour((c0->c+c1->c)/2); DrawLine(c0->x+Xtr2, c0->y+Ytr2, c1->x+Xtr2, c1->y+Ytr2); #ifdef DEBUG SetForeground(&c_gain); #endif while (Q2->next && Q2->next->a<0) Q2=Q2->next; /* if a "horizontal" edge of the quadrangle passes through the origin (on screen), add a maskpoint to make sure the mask really surrounds the origin: */ if (fabs(yprev)<1e-5) add_to_mask(newmaskpoint(0,-0.01,ATAN(-0.01,0),0.0001,NULL)); if (fabs(y)<1e-5) add_to_mask(newmaskpoint(0,0.01,0,0.0001,NULL)); /* caller wants to know where in the *rp array the initial quadrangle is: */ *i0=i; *i1=k; *j0=j; *j1=l; } static void first_polygon(Radpattern *rp, Hidpoint **hp,int *i0, int *i1, int *j0, int *j1) /* Draw the first polygon, in cases where data is only available for half of the sphere (namely theta <= 90 degrees), and we're looking from a direction outside that range (i.e., from "down under"). In this case, the first polygon is simply the set of points at theta=90 degrees. */ { int i,j,l; Hidpoint *c0,*c1; i=rp->numtheta-1; *i0=i; *i1=i; /* find the rp->gphi value closest to the phi2 of the viewing direction: */ j=0; for (l=1;lnumphi;l++) if (fabs(drem(rp->gphi[l]-phi2,2*M_PI)) < fabs(drem(rp->gphi[j]-phi2,2*M_PI))) j=l; /* if almost identical to phi2, change phi2 a little */ if (fabs(drem(rp->gphi[j]-phi2,2*M_PI)) < 1e-6) { if (drem(rp->gphi[j]-phi2,2*M_PI) < 0) phi2-=1e-6; else phi2+=1e-6; phi2=drem(phi2,2*M_PI); calcproj2(); } /* choose *j0 and *j1 such that they are subsequent phi indices around the viewing direction */ if (drem(rp->gphi[j]-phi2,2*M_PI)<0) { *j0=j; *j1=(j+1)%rp->numphi; } else { *j1=j; *j0=(j-1+rp->numphi)%rp->numphi; } { /* fill the Hidpoint array */ int i,j; for (i=0;inumtheta;i++) for (j=0;jnumphi;j++) { proj2(&rp->gpo[j][i], &hp[j][i]); hp[j][i].c=rp->gain[j][i].axial; hp[j][i].maskpoint=NULL; } } c1=&hp[0][i]; Q=Q2=newmaskpoint(c1->x,c1->y,c1->a,c1->s,c1); for (j=1;jnumphi;j++) { Q2=Q; /* during initial setup of mask, using Q2 is not reliable otherwise (and we don't really need the speedup here) */ c0=c1; c1=&hp[j][i]; add_to_mask(newmaskpoint(c1->x,c1->y,c1->a,c1->s,c1)); if (polarization==POLcolour) setpolcolour((c0->c+c1->c)/2); DrawLine(c0->x+Xtr2, c0->y+Ytr2, c1->x+Xtr2, c1->y+Ytr2); } Q2=Q; c0=c1; c1=&hp[0][i]; add_to_mask(newmaskpoint(c1->x,c1->y,c1->a,c1->s,c1)); if (polarization==POLcolour) setpolcolour((c0->c+c1->c)/2); DrawLine(c0->x+Xtr2, c0->y+Ytr2, c1->x+Xtr2, c1->y+Ytr2); while (Q2->next && Q2->next->a<0) Q2=Q2->next; } void draw_opaque(Radpattern *rp) { int i,j; Hidpoint *c0,*c1,*c2,*c3; int i0,i1; int j0,j1; Hidpoint **hp; int halfsphere=0; int lookfrombottom=0; Hidpoint hpnul1={0,0,-M_PI,0,1,NULL}; Hidpoint hpnul2={0,0,0,0,1,NULL}; #ifdef MEASURE struct rusage ru1,ru2; getrusage(RUSAGE_SELF,&ru1); for (count=99;count>=0;count--) { #endif /* Prepare the viewing direction: in principle just phi,theta, but the algorithm doesn't work well when looking exactly from the "top" or "bottom", i.e., when theta is 0 or 180 degrees. Note that phi2 may be changed a little bit later on in first_polygon() or first_quadrangle(). */ phi2=phi*(M_PI/180.); theta2=theta*(M_PI/180.); if (theta2<1e-7) theta2=1e-7; else if (theta2>M_PI-1e-7) theta2=M_PI-1e-7; if (fabs(theta2-M_PI/2)<1e-7) theta2=M_PI/2-1e-7; calcproj2(); if (rp->gtheta[rp->numtheta-1]numphi*sizeof(Hidpoint *)); for (j=0;jnumphi;j++) hp[j]=mymalloc(rp->numtheta*sizeof(Hidpoint)); /* initialize the mask, drawing the first polygon */ init_mask(); if (!halfsphere || theta<=90) { first_quadrangle(rp,hp,&i0,&i1,&j0,&j1); } else { first_polygon(rp,hp,&i0,&i1,&j0,&j1); lookfrombottom=1; } recalc_min_s(); PRINTF(("%i %i\n",i,j)); /* next, complete the quadrangles "above" and "below" the initial quadrangle, up to the "poles" */ c0=&hp[j0][i1]; c1=&hp[j1][i1]; for (i=i1+1;inumtheta;i++) { c2=&hp[j0][i]; c3=&hp[j1][i]; draw_masked1(c0,c2); draw_masked1(c1,c3); draw_masked1(c2,c3); commit_mask(); c0=c2; c1=c3; } c0=&hp[j0][i0]; c1=&hp[j1][i0]; for (i=i0-1;i>=0;i--) { c2=&hp[j0][i]; c3=&hp[j1][i]; draw_masked1(c0,c2); draw_masked1(c1,c3); draw_masked1(c2,c3); commit_mask(); c0=c2; c1=c3; } j=j1; /* Next, we do all quadrangles "right" of the vertical line through the origin, "column" by column, starting with the column just to the right of the initial quadrangle. Within a column, we need to be a bit careful: depending on the precise location of the points, we either need to go downward (starting at the "north pole") or upward (from the "south pole"). In fact, we usually need to change direction one or more times. This is needed to ensure that the mask is a single-valued function of the angle a . */ left_half=0; recalc_min_s(); while (1) { int l; int ii; int jn=(j+1)%rp->numphi; if (drem(rp->gphi[jn]-phi2,2*M_PI)<0) break; ii=0; for (i=1;inumtheta-1;i++) { double a1,a2; DEBUGPAUSE c1=&hp[j][i]; a1 = c1->a; c2=&hp[jn][i]; a2 = c2->a; if ( drem(a2-a1,2*M_PI)<0 ) { draw_masked1(c1,c2); c0=&hp[jn][i-1]; draw_masked1(c0,c2); l=i-1; while (l>ii) { c2=c0; c1=&hp[j][l]; draw_masked1(c1,c2); commit_mask(); c0=&hp[jn][l-1]; draw_masked1(c0,c2); l--; } if (l==0) draw_masked1(c0,&hpnul1); commit_mask(); ii=i; } } c2=&hp[jn][i]; if (halfsphere && !lookfrombottom) { c1=&hp[j][i]; draw_masked1(c1,c2); } if (!halfsphere) { draw_masked1(c2,&hpnul2); } c0=&hp[jn][i-1]; draw_masked1(c0,c2); l=i-1; while (l>ii) { c2=c0; c1=&hp[j][l]; draw_masked1(c1,c2); commit_mask(); c0=&hp[jn][l-1]; draw_masked1(c0,c2); l--; } commit_mask(); j=jn; } j1=j; /* now exactly the same procedure, but for columns to the left of the origin */ j=j0; left_half=1; right_half=0; recalc_min_s(); while (1) { int l; int ii; int jn=(j-1+rp->numphi)%rp->numphi; if (drem(rp->gphi[jn]-phi2,2*M_PI)>0) break; ii=0; for (i=1;inumtheta-1;i++) { double a1,a2; DEBUGPAUSE c1=&hp[j][i]; a1 = c1->a; c2=&hp[jn][i]; a2 = c2->a; if ( drem(a2-a1,2*M_PI)>0 ) { draw_masked1(c1,c2); c0=&hp[jn][i-1]; draw_masked1(c0,c2); l=i-1; while (l>ii) { c2=c0; c1=&hp[j][l]; draw_masked1(c1,c2); commit_mask(); c0=&hp[jn][l-1]; draw_masked1(c0,c2); l--; } commit_mask(); ii=i; } } c2=&hp[jn][i]; if (halfsphere && !lookfrombottom) { c1=&hp[j][i]; draw_masked1(c1,c2); } c0=&hp[jn][i-1]; draw_masked1(c0,c2); l=i-1; while (l>ii) { c2=c0; c1=&hp[j][l]; draw_masked1(c1,c2); commit_mask(); c0=&hp[jn][l-1]; draw_masked1(c0,c2); l--; } commit_mask(); j=jn; } j0=j; right_half=1; recalc_min_s(); /* finally, draw the set of quadrangles that are "behind" the (3D) Z axis */ DEBUGPAUSE if (halfsphere) { if (!lookfrombottom) { i0=i1=rp->numtheta; } else { i1=1; while (rp->gtheta[i1]numtheta-1-i0; i1=rp->numtheta-i1; } for (i=1;inumtheta-2;i>=i0;i--) { c2=&hp[j0][i]; c3=&hp[j1][i]; draw_masked1(c2,c3); commit_mask(); } DEBUGPAUSE PRINTF(("\n")); cleanup_mask(); for (j=0;jnumphi;j++) free(hp[j]); free(hp); #ifdef MEASURE } getrusage(RUSAGE_SELF,&ru2); printf("duration = %i\n",ru2.ru_utime.tv_usec-ru1.ru_utime.tv_usec + 1000000*(ru2.ru_utime.tv_sec-ru1.ru_utime.tv_sec)); printf("cnt1/cnt0 = %g\n",(double)cnt1/cnt0); #endif } char *opaque_impossible(Radpattern *rp) /* returns NULL if opaque drawing of this Radpattern is possible; otherwise, returns a pointer to a string explaining why opaque drawing is not possible. */ { double a; if (rp->gtheta[0]*180/M_PI>0.1) return "theta range does not start at 0 degrees"; a=rp->gtheta[rp->numtheta-1]*180/M_PI; if (a>180.1) return "theta range continues beyond 180 degrees"; if ((a<89.9 || a>90.1) && a<179.9) return "theta range does not go on until either 90 or 180 degrees"; if (rp->gphi[0]*180/M_PI>0.1) return "phi range does not start at 0 degrees"; if (rp->gphi[rp->numphi-1]*180/M_PI<271) return "phi range does not go on until almost 360 degrees"; if (rp->gphi[rp->numphi-1]*180/M_PI>359.9) return "phi range goes on until 360 degrees or beyond"; if (rp->numtheta<4) return "not enough theta values"; if (rp->numphi<4) return "not enough phi values"; return NULL; } xnecview-1.35.orig/freqplot.c0000644000175000017500000003431011005110762014775 0ustar afrb2afrb2/* XNECVIEW - a program for visualizing NEC2 input and output data * * Copyright (C) 2000-2006, Pieter-Tjerk de Boer -- pa3fwm@amsat.org * * Distributed on the conditions of version 2 of the GPL: see the files * README and COPYING, which accompany this source file. * * This module contains code for drawing plots of several quantities * like impedance, SWR and gain as a function of frequency. * */ #include #include #include #include #include #include "xnecview.h" int win2sizex,win2sizey; /* size of window in pixels */ int plot2_swr=1; /* show the SWR graph? */ int plot2_maxgain=1; /* show the maxgain and front/back graph? */ int plot2_vgain=0; /* show the vgain graph? */ int plot2_z=0; /* show the impedance graph? */ int plot2_z2=0; /* show the phi(z)/abs(z) graph? */ int plot2_dir=0; /* show the direction-of-maximum-gain graph? */ double r0=R0; /* reference impedance for SWR calculation */ void fixrange1(double *mi, double *ma, int *np) /* mi and ma point to minimum and maximum value of a range of values to be plotted, and np to the maximum acceptable number of subdivision. This function tries to modify the minimum and maximum and the number of subdivision such that the resulting grid lines are at "round" numbers. */ { double d,e; double a; double newmin,newmax; int i; int n=*np; /* static double acceptable[]={10, 5.0, 2.5, 2.0, 1.0, -1}; */ static double acceptable[]={10, 5.0, 2.0, 1.0, -1}; if (*ma==*mi) { if (*mi>0) {*mi=0; *ma=2* *ma;} else if (*mi<0) {*mi=2* *mi; *ma=0;} else {*mi=-10; *ma=10;} } d=(*ma-*mi)/n; e=1.0; while (ed) e/=10; a=d/e; i=0; while (acceptable[i]>a) i++; if (acceptable[i]==-1) i--; i++; do { i--; if (i<0) { e*=10; i=0; while (acceptable[i+1]>0) i++; } a=acceptable[i]; d=a*e; newmin = d*floor(*mi/d); newmax = d*ceil(*ma/d); n = (int)((newmax-newmin)/d+0.5); } while (n>*np); *np=n; *mi=newmin; *ma=newmax; } void fixrange2(double *mi1, double *ma1, double *mi2, double *ma2, int *np) /* like fixrange2(), but for two (vertical) axes simultaneously */ { static double acceptable[]={100.0, 50.0, 25.0, 20.0, 10.0, 5.0, 2.5, 2.0, 1.0, 0.5, 0.25, 0.2, 0.1, 0.05, 0.025, 0.02, 0.01, -1}; double a,d,e1,e2,s; int n=*np; int i1,i2; int i,j; int ibest,jbest; int n1[5],n2[5]; double x1[4],x2[4]; double best; if (*ma1==*mi1) { if (*mi1>0) {*mi1=0; *ma1=2* *ma1;} else if (*mi1<0) {*mi1=2* *mi1; *ma1=0;} else {*mi1=-10; *ma1=10;} } d=(*ma1-*mi1)/n; /* d is the ideal, but usually not acceptable, stepsize, for axis 1 */ *ma1-=0.00001*d; /* prevent rounding errors from causing a boundary of say 1000 to be seen as slightly larger than say 10 steps of 100 each */ *mi1+=0.00001*d; /* idem */ d-=0.00001*d; e1=1.0; while (e1d) e1/=10; /* e1 is the appropriate power of 10 to scale the steps, for axis 1 */ a=d/e1; i1=0; while (acceptable[i1+1]>=a) i1++; /* i1 is the index in the acceptable[] array of the highest acceptable stepsize, for axis 1 */ for (i=0;i<4;i++) { /* consider this and the next 3 lower stepsizes: */ s = e1*acceptable[i1-i]; n1[i] = ceil(*ma1/s) - floor(*mi1/s) ; /* minimum number of acceptable steps */ x1[i] = (*ma1-*mi1) / s; /* "usage factor": how many of these steps does the data cover? */ } /* same calculations for axis 2 */ if (*ma2==*mi2) { if (*mi2>0) {*mi2=0; *ma2=2* *ma2;} else if (*mi2<0) {*mi2=2* *mi2; *ma2=0;} else {*mi2=-10; *ma2=10;} } d=(*ma2-*mi2)/n; *ma2-=0.00001*d; *mi2+=0.00001*d; d-=0.00001*d; e2=1.0; while (e2d) e2/=10; a=d/e2; i2=0; while (acceptable[i2+1]>=a) i2++; for (i=0;i<4;i++) { s = e2*acceptable[i2-i]; n2[i] = ceil(*ma2/s) - floor(*mi2/s) ; x2[i] = (*ma2-*mi2) / s; } /* search for best combination: the combination for which the data covers as large a fraction of both axes as possible */ best=0; ibest=jbest=0; for (i=0;i<4;i++) for (j=0;j<4;j++) { double x; int n; n = n1[i]; if (n2[j]>n) n=n2[j]; x = (x1[i]/n) * (x2[j]/n); if (x>best*1.1 || (x>best && n>=*np)) { best=x; ibest=i; jbest=j; *np=n; } } n = *np; i1-=ibest; i2-=jbest; s = e1*acceptable[i1]; *mi1 = s*floor(*mi1/s); *ma1 = *mi1+n*s; s = e2*acceptable[i2]; *mi2 = s*floor(*mi2/s); *ma2 = *mi2+n*s; } double minf,maxf; int xleft, xright; #define idxOK(idx,ne) (idx>=0 && ( !ONLY_IF_RP(idx) || ne->rp ) && ne->d[idx]>-DBL_MAX) void freqplot( int idx1, /* index in neco[].d[] of quantity for left axis */ int idx2, /* index in neco[].d[] of quantity for right axis */ int idx1a, /* index in neco[].d[] of second quantity for left axis (dotted line) */ int idx2a, /* index in neco[].d[] of second quantity for right axis (dotted line) */ char *title1, char *title2, /* titles for left and right */ char *title, /* center title */ GdkColor *color1, GdkColor *color2, /* colours for both curves */ double ybotf, double ytopf /* vertical position; 0...1 = top...bottom of window */ ) { int ybot, ytop; int i; double min1,max1, min2, max2; NECoutput *ne; int ntx,nty; int xx1,xx2,yy1,yy2; int xx1a,xx2a,yy1a,yy2a; /* choose the corner points of the graph area */ ybot = ybotf*win2sizey - fontheight; ytop = ytopf*win2sizey + fontheight; xleft = 5*fontheight; xright = win2sizex - 5*fontheight; /* find the ranges */ minf=maxf=neco[0].f; min1=min2=DBL_MAX; max1=max2=-DBL_MAX; for (i=0, ne=neco; if < minf) minf=ne->f; if (ne->f > maxf) maxf=ne->f; if (idxOK(idx1,ne)) { if (ne->d[idx1] < min1) min1=ne->d[idx1]; if (ne->d[idx1] > max1) max1=ne->d[idx1]; } if (idxOK(idx1a,ne)) { if (ne->d[idx1a] < min1) min1=ne->d[idx1a]; if (ne->d[idx1a] > max1) max1=ne->d[idx1a]; } if (idxOK(idx2,ne)) { if (ne->d[idx2] < min2) min2=ne->d[idx2]; if (ne->d[idx2] > max2) max2=ne->d[idx2]; } if (idxOK(idx2a,ne)) { if (ne->d[idx2a] < min2) min2=ne->d[idx2a]; if (ne->d[idx2a] > max2) max2=ne->d[idx2a]; } } if (min1>max1) { idx1=-1; idx1a=-1; } if (min2>max2) { idx2=-1; idx2a=-1; } /* extend the ranges to have 'round' numbers at each division */ ntx=(xright-xleft)/fontheight/3; fixrange1(&minf,&maxf,&ntx); nty = (ybot-ytop)/fontheight; if (idx1>=0) { if (idx1==neco_zr) { if (max1 > 20*r0) max1 = 20*r0; } } if (idx2>=0) { if (idx2==neco_zi || idx2==neco_zabs) { if (max2 > 20*r0 && min2 < 20*r0) max2 = 20*r0; if (min2 < -20*r0 && max2 > -20*r0) min2 = -20*r0; } } if (idx1==neco_swr) { min1=0; if (max1>10) max1=9; else max1-=1; } if (idx2==neco_swr) { min2=0; if (max2>10) max2=9; else max2-=1; } if (idx1<0 && idx1a<0) { if (idx2<0 && idx2a<0) return; fixrange1(&min2,&max2,&nty); } else { if (idx2<0 && idx2a<0) fixrange1(&min1,&max1,&nty); else fixrange2(&min1,&max1,&min2,&max2,&nty); } if (idx1==neco_swr) { min1+=1; max1+=1; } if (idx2==neco_swr) { min2+=1; max2+=1; } /* macros for converting from "real" values to screen coordinates */ #define sx(f) (((f)-minf)/(maxf-minf)*(xright-xleft)+xleft) #define sy1(f) (((f)-min1)/(max1-min1)*(ytop-ybot)+ybot) #define sy2(f) (((f)-min2)/(max2-min2)*(ytop-ybot)+ybot) SetLineAttributes(0, GDK_LINE_SOLID, GDK_CAP_ROUND, GDK_JOIN_ROUND); /* vertical grid lines and associated labels */ for (i=0; i<=ntx; i++) { double f; int x; char s[20]; f=minf+(maxf-minf)*((double)i)/ntx; x=sx(f); if (i>0 && i0 && i=0) { sprintf(s,"%g ",f); SetForeground(color1); DrawString(xleft,y,s,1,0.5); } if (idx2>=0) { f=min2+(max2-min2)*((double)i)/nty; if (fabs(f/(max2-min2))<0.1/nty) f=0; y=sy2(f); sprintf(s," %g",f); SetForeground(color2); DrawString(xright,y,s,0,0.5); } } SetForeground(&c_axis); /* border around the graph */ DrawLine(xleft,ybot,xright,ybot); DrawLine(xleft,ytop,xright,ytop); DrawLine(xleft,ybot,xleft,ytop); DrawLine(xright,ybot,xright,ytop); /* title(s) */ if (title) { SetForeground(&c_axis); DrawString((xleft+xright)/2, ytop-1, title, 0.5,0); } if (title1) { SetForeground(color1); DrawString(xleft-fontheight, ytop-1, title1, 0,0); } if (title2) { SetForeground(color2); DrawString(xright+fontheight, ytop-1, title2, 1,0); } /* the actual data points and connecting lines */ SetClipRectangle(xleft-2,ytop,xright+2,ybot); xx1=xx2=yy1=yy2=-1; xx1a=xx2a=yy1a=yy2a=-1; for (i=0, ne=neco; if); if (idx1a>=0 || idx2a>=0) SetLineAttributes(0, GDK_LINE_ON_OFF_DASH, GDK_CAP_ROUND, GDK_JOIN_ROUND); if (idxOK(idx1a,ne)) { y = sy1(ne->d[idx1a]); SetForeground(color1); if (numneco < win2sizex / 4) { DrawLine(x-2,y-2,x+2,y-2); DrawLine(x-2,y+2,x+2,y+2); DrawLine(x-2,y-2,x-2,y+2); DrawLine(x+2,y-2,x+2,y+2); } if (xx1a!=-1) DrawLine(xx1a,yy1a,x,y); xx1a=x; yy1a=y; } if (idxOK(idx2a,ne)) { y = sy2(ne->d[idx2a]); SetForeground(color2); if (numneco < win2sizex / 4) { DrawLine(x-3,y,x,y-3); DrawLine(x-3,y,x,y+3); DrawLine(x+3,y,x,y-3); DrawLine(x+3,y,x,y+3); } if (xx2a!=-1) DrawLine(xx2a,yy2a,x,y); xx2a=x; yy2a=y; } if (idx1a>=0 || idx2a>=0) SetLineAttributes(0, GDK_LINE_SOLID, GDK_CAP_ROUND, GDK_JOIN_ROUND); if (idxOK(idx1,ne)) { y = sy1(ne->d[idx1]); SetForeground(color1); if (numneco < win2sizex / 4) { DrawLine(x-2,y-2,x+2,y-2); DrawLine(x-2,y+2,x+2,y+2); DrawLine(x-2,y-2,x-2,y+2); DrawLine(x+2,y-2,x+2,y+2); } if (xx1!=-1) DrawLine(xx1,yy1,x,y); xx1=x; yy1=y; } if (idxOK(idx2,ne)) { y = sy2(ne->d[idx2]); SetForeground(color2); if (numneco < win2sizex / 4) { DrawLine(x-3,y,x,y-3); DrawLine(x-3,y,x,y+3); DrawLine(x+3,y,x,y-3); DrawLine(x+3,y,x,y+3); } if (xx2!=-1) DrawLine(xx2,yy2,x,y); xx2=x; yy2=y; } } SetClipRectangle(0,0,win2sizex,win2sizey); } double xfreq(int x) { return ((double)x-xleft)/(xright-xleft)*(maxf-minf)+minf; } int freqx(double f) { return sx(f); } int freqindex(double f) { double d,dbest; int i,ibest; ibest=-1; dbest=DBL_MAX; for (i=0;iComplete(); } xnecview-1.35.orig/icon.xbm0000644000175000017500000000155311004452102014434 0ustar afrb2afrb2#define icon_width 32 #define icon_height 32 static unsigned char icon_bits[] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0xc0, 0x0f, 0x40, 0x10, 0x30, 0x70, 0x41, 0x10, 0x08, 0x80, 0x41, 0x10, 0x04, 0x00, 0x43, 0x10, 0x04, 0x00, 0x45, 0x13, 0x02, 0x00, 0xc9, 0x14, 0x02, 0x00, 0x71, 0x12, 0x02, 0x00, 0x41, 0x11, 0x02, 0x00, 0x41, 0x11, 0x02, 0x00, 0x71, 0x12, 0x02, 0x00, 0xc9, 0x14, 0x04, 0x00, 0x45, 0x13, 0x04, 0x00, 0x43, 0x10, 0x08, 0x80, 0x41, 0x10, 0x30, 0x70, 0x41, 0x10, 0xc0, 0x0f, 0x40, 0x10, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, }; xnecview-1.35.orig/parse_input.c0000644000175000017500000005536211005110762015504 0ustar afrb2afrb2/* XNECVIEW - a program for visualizing NEC2 input and output data * * Copyright (C) 1998-2006, Pieter-Tjerk de Boer -- pa3fwm@amsat.org * * Distributed on the conditions of version 2 of the GPL: see the files * README and COPYING, which accompany this source file. * * This module parses the NEC input file (i.e., the file describing the * antenna's structure). */ #include #include #include #include #include #include #include "xnecview.h" /* some error codes for internal use: */ #define Err_misc 1 #define Err_scan 2 #define Err_nwires 3 #define Err_nsurfaces 4 #define Err_nexcis 5 #define Err_nloads 6 #define Err_nnetws 7 int nextabs; /* next absolute segment number to be assigned */ char lastCard[2]; /* type of previous card */ double len3d(Point p1) /* calculates length of 3D vector p1 */ { return sqrt(p1.x*p1.x+p1.y*p1.y+p1.z*p1.z); } Point int3d(Point p1, Point p2, double f) /* calculate intermediate point (3D); f=0..1 -> p1..p2 */ { Point p; p.x=(1-f)*p1.x+f*p2.x; p.y=(1-f)*p1.y+f*p2.y; p.z=(1-f)*p1.z+f*p2.z; return p; } void out3d(Surface *su,double f) /* calculates su->pa, with f determining the length of the arrow */ { Point d0,d1,no; d0.x=su->p0.x-su->pc.x; d0.y=su->p0.y-su->pc.y; d0.z=su->p0.z-su->pc.z; d1.x=su->p1.x-su->pc.x; d1.y=su->p1.y-su->pc.y; d1.z=su->p1.z-su->pc.z; no.x=d0.y*d1.z-d0.z*d1.y; no.y=-d0.x*d1.z+d0.z*d1.x; no.z=d0.x*d1.y-d0.y*d1.x; f *= (len3d(d0)+len3d(d1))/len3d(no); no.x*=f; no.y*=f; no.z*=f; su->pa.x=su->pc.x+no.x; su->pa.y=su->pc.y+no.y; su->pa.z=su->pc.z+no.z; } void updateextremes(Point *p) { if (p->x>extr_str) extr_str=p->x; if (p->y>extr_str) extr_str=p->y; if (p->z>extr_str) extr_str=p->z; if (-p->x>extr_str) extr_str=-p->x; if (-p->y>extr_str) extr_str=-p->y; if (-p->z>extr_str) extr_str=-p->z; } int reflect(int plane,int inc) /* perform a reflection of all antenna elements */ { Wire *w,*w0; Surface *s,*s0; int i; EXPAND_IF_NECESSARY(numwires*2,maxwires,wires) for ( i=numwires, w0=wires, w=wires+numwires ; i>0 ; i--, w0++, w++, numwires++ ) { *w=*w0; switch (plane) { case 0: w->p0.x=-w->p0.x; w->p1.x=-w->p1.x; break; case 1: w->p0.y=-w->p0.y; w->p1.y=-w->p1.y; break; case 2: w->p0.z=-w->p0.z; w->p1.z=-w->p1.z; break; } if (w->itg>0) w->itg+=inc; w->abs=nextabs; nextabs+=w->ns; } EXPAND_IF_NECESSARY(numsurfaces*2,maxsurfaces,surfaces) for ( i=numsurfaces, s0=surfaces, s=surfaces+numsurfaces ; i>0 ; i--, s0++, s++, numsurfaces++ ) { *s=*s0; switch (plane) { case 0: s->p0.x=-s->p0.x; s->p1.x=-s->p1.x; s->p2.x=-s->p2.x; s->p3.x=-s->p3.x; s->pc.x=-s->pc.x; s->pa.x=-s->pa.x; break; case 1: s->p0.y=-s->p0.y; s->p1.y=-s->p1.y; s->p2.y=-s->p2.y; s->p3.y=-s->p3.y; s->pc.y=-s->pc.y; s->pa.y=-s->pa.y; break; case 2: s->p0.z=-s->p0.z; s->p1.z=-s->p1.z; s->p2.z=-s->p2.z; s->p3.z=-s->p3.z; s->pc.z=-s->pc.z; s->pa.z=-s->pa.z; break; } } return 0; } void removecommas(char *p) /* change commas in string into spaces */ { while ((p=strchr(p,','))) *p++=' '; } int find_segment(int tag, int seg, char *card, Wire **wirep, int *segp) { int i; if (tag>0) { /* location specified as tag,seg pair */ for (i=0;i0) { if (seg<=0 || seg>wires[i].ns) { fprintf(stderr,"Incorrect segment number on %s card: %i\n",card,seg); return Err_misc; } } else { while (wires[i].ns!=-seg && wires[i].itg==tag) i++; if (wires[i].itg!=tag) { fprintf(stderr,"Incorrect segment number on %s card: %i\n",card,seg); return Err_misc; } } *segp = seg; } else { /* location specified as absolute segment number */ for (i=0;i=wires[i].abs && seg comment card; check whether it contains xnecview options */ { s+=2; while (*s==' ') s++; if (memcmp(s,"xnecview:",9)==0) process_optionstring(s+9); return 0; } int read_nec_EX(char *s) /* EX -> excitation */ { int type,tag,seg; int err; removecommas(s); if (sscanf(s,"EX%d%d%d",&type,&tag,&seg) != 3) return Err_scan; if (type!=0 && type!=5) { fprintf(stderr,"Only voltage sources can be displayed: %s\n",s); return Err_misc; } EXPAND_IF_NECESSARY(numexcis,maxexcis,excis) err=find_segment(tag, seg, "EX", &excis[numexcis].wire, &excis[numexcis].seg); if (err) return err; numexcis++; return 0; } int read_nec_TL(char *s) /* TL -> transmission line */ { int tag1,seg1,tag2,seg2; int err; double imp; removecommas(s); if (sscanf(s,"TL%d%d%d%d%lg",&tag1,&seg1,&tag2,&seg2,&imp) != 5) return Err_scan; EXPAND_IF_NECESSARY(numnetws,maxnetws,netws) err=find_segment(tag1, seg1, "TL", &netws[numnetws].wire1, &netws[numnetws].seg1); if (err) return err; err=find_segment(tag2, seg2, "TL", &netws[numnetws].wire2, &netws[numnetws].seg2); if (err) return err; if (imp<0) netws[numnetws].type = -1; else netws[numnetws].type = 1; numnetws++; return 0; } int read_nec_NT(char *s) /* NT -> network */ { int tag1,seg1,tag2,seg2; int err; removecommas(s); if (sscanf(s,"NT%d%d%d%d",&tag1,&seg1,&tag2,&seg2) != 4) return Err_scan; EXPAND_IF_NECESSARY(numnetws,maxnetws,netws) err=find_segment(tag1, seg1, "NT", &netws[numnetws].wire1, &netws[numnetws].seg1); if (err) return err; err=find_segment(tag2, seg2, "NT", &netws[numnetws].wire2, &netws[numnetws].seg2); if (err) return err; netws[numnetws].type = 0; numnetws++; return 0; } int addload(Wire *wi,int first,int last) /* add one load */ { EXPAND_IF_NECESSARY(numloads,maxloads,loads) loads[numloads].wire=wi; loads[numloads].firstseg=first; loads[numloads].lastseg=last; numloads++; return 0; } int read_nec_LD(char *s) /* LD -> loading */ { int type,tag,segf,segl; int i; Wire *wi; removecommas(s); tag=-2; segf=segl=0; if (sscanf(s,"LD%d%d%d%d",&type,&tag,&segf,&segl) < 1) return Err_scan; if (tag==-2) return Err_scan; if (type==-1) { numloads=0; return 0; } if (tag>0) { /* location specified as tag,seg pair */ for (i=0;i0) { if (segf==0) segf=1; if (segl==0) segl=wires[i].ns; if (segf<=0 || segl>wires[i].ns || segf>segl) { fprintf(stderr,"Incorrect segment numbers on LD card: %i, %i\n",segf,segl); return Err_misc; } addload(wires+i,segf,segl); return 0; } else { if (segf==0) segf=-1; else segf=-segf; if (segl==0) segl=-INT_MAX; else segl=-segl; while (wires[i].ns!=segf && wires[i].itg==tag && i=numwires) { fprintf(stderr,"Incorrect first segment number on LD card: %i\n",segf); return Err_misc; } while (wires[i].ns>=segl && wires[i].itg==tag && i=numwires) && segl!=-INT_MAX) { fprintf(stderr,"Incorrect last segment number on LD card: %i\n",-segl); return Err_misc; } } } else { /* location specified as absolute segment number */ if (segf==0 && segl==0) { for (i=0;i0) addload(wires+i,1,wires[i].ns); else addload(wires+i,1,1); return 0; } if (segf==0 || segl==0 || segf>segl) { fprintf(stderr,"Incorrect absolute segment numbers on LD card: %i %i\n",segf,segl); return Err_misc; } for (wi=wires;wins>0) { int f,l; f=segf-wi->abs+1; if (f>wi->ns) continue; l=segl-wi->abs+1; if (l<1) continue; if (f<1) f=1; if (l>wi->ns) l=wi->ns; addload(wi,f,l); } else { if (wi->abs>=segf && wi->abs<=segl) addload(wi,1,1); } } } return 0; } int read_nec_GA(char *s) /* GA -> wire arc */ { int ns,itg,segnr; double rada,ang1,ang2,rad; double phi,incphi; Point po; Wire *w; removecommas(s); if (sscanf(s,"GA%d%d%lg%lg%lg%lg",&itg,&ns,&rada,&ang1,&ang2,&rad) != 6) return Err_scan; EXPAND_IF_NECESSARY(numwires+ns,maxwires,wires) phi=ang1*M_PI/180; incphi=(ang2-ang1)*M_PI/180/ns; po.x=cos(phi)*rada; po.y=0; po.z=sin(phi)*rada; updateextremes(&po); w=wires+numwires; numwires+=ns; for (segnr=1; segnr<=ns; segnr++) { w->p0=po; phi+=incphi; po.x=cos(phi)*rada; po.y=0; po.z=sin(phi)*rada; updateextremes(&po); w->p1=po; w->rad=rad; w->itg=itg; w->ns=-segnr; w->abs=nextabs; nextabs++; w->dispnr=(segnr-1==ns/2); w++; } return 0; } int read_nec_GH(char *s) /* GH -> helix */ { int ns,itg,segnr; double sp,hl,a1,b1,a2,b2,rad; double z,incz; double phi,incphi; double rx,incrx; double ry,incry; Point po; Wire *w; removecommas(s); if (sscanf(s,"GH%d%d%lg%lg%lg%lg%lg%lg%lg",&itg,&ns,&sp,&hl,&a1,&b1,&a2,&b2,&rad) != 9) return Err_scan; EXPAND_IF_NECESSARY(numwires+ns,maxwires,wires) if (hl==0) { fprintf(stderr,"Helix with length=0 not yet supported.\n"); return Err_misc; } z=0; incz=hl/ns; rx=a1; incrx=(a2-a1)/ns; ry=b1; incry=(b2-b1)/ns; phi=0; incphi=2*M_PI*incz/sp; if (hl<0) { incz=-incz; phi=M_PI/2; } po.x=cos(phi)*rx; po.y=sin(phi)*ry; po.z=z; updateextremes(&po); w=wires+numwires; numwires+=ns; for (segnr=1; segnr<=ns; segnr++) { w->p0=po; z+=incz; rx+=incrx; ry+=incry; phi+=incphi; po.x=cos(phi)*rx; po.y=sin(phi)*ry; po.z=z; updateextremes(&po); w->p1=po; w->rad=rad; w->itg=itg; w->ns=-segnr; w->abs=nextabs; nextabs++; w->dispnr=(segnr-1==ns/2); w++; } return 0; } void pointtransf(Point *p0,Point *p1,double *mat,double xs,double ys,double zs) { Point po; po.x= xs + mat[0]*p0->x + mat[1]*p0->y + mat[2]*p0->z; po.y= ys + mat[3]*p0->x + mat[4]*p0->y + mat[5]*p0->z; po.z= zs + mat[6]*p0->x + mat[7]*p0->y + mat[8]*p0->z; *p1=po; updateextremes(&po); } int read_nec_GM(char *s) /* GM -> coordinate transformation */ { int nrpt,its,i,itsi; double rox,roy,roz,xs,ys,zs; double mat[9],mmat[9]; double co,si; Wire *w00,*w0,*w1,*wlast; Surface *s0,*s1; removecommas(s); rox=roy=roz=xs=ys=zs=its=0; i=sscanf(s,"GM%d%d%lg%lg%lg%lg%lg%lg%d",&itsi,&nrpt,&rox,&roy,&roz,&xs,&ys,&zs,&its); if (i<2) return Err_scan; rox*=M_PI/180.0; roy*=M_PI/180.0; roz*=M_PI/180.0; mat[0]=mat[4]=mat[8]=1; mat[1]=mat[2]=mat[3]=0; mat[5]=mat[6]=mat[7]=0; co=cos(rox); si=sin(rox); memcpy(mmat,mat,sizeof(mat)); mat[3] = co*mmat[3] - si*mmat[6]; mat[4] = co*mmat[4] - si*mmat[7]; mat[5] = co*mmat[5] - si*mmat[8]; mat[6] = si*mmat[3] + co*mmat[6]; mat[7] = si*mmat[4] + co*mmat[7]; mat[8] = si*mmat[5] + co*mmat[8]; co=cos(roy); si=-sin(roy); memcpy(mmat,mat,sizeof(mat)); mat[0] = co*mmat[0] - si*mmat[6]; mat[1] = co*mmat[1] - si*mmat[7]; mat[2] = co*mmat[2] - si*mmat[8]; mat[6] = si*mmat[0] + co*mmat[6]; mat[7] = si*mmat[1] + co*mmat[7]; mat[8] = si*mmat[2] + co*mmat[8]; co=cos(roz); si=sin(roz); memcpy(mmat,mat,sizeof(mat)); mat[0] = co*mmat[0] - si*mmat[3]; mat[1] = co*mmat[1] - si*mmat[4]; mat[2] = co*mmat[2] - si*mmat[5]; mat[3] = si*mmat[0] + co*mmat[3]; mat[4] = si*mmat[1] + co*mmat[4]; mat[5] = si*mmat[2] + co*mmat[5]; EXPAND_IF_NECESSARY(numwires*(nrpt+1),maxwires,wires) if (nrpt==0) { w00=w1=wires; } else { w00=wires; w1=wires+numwires; } wlast=wires+numwires; while (w00itg>=its) { w0=w00; i=0; do { if (nrpt>0) { numwires++; } else { w1=w0; } pointtransf(&w0->p0,&w1->p0,mat,xs,ys,zs); pointtransf(&w0->p1,&w1->p1,mat,xs,ys,zs); w1->rad=w0->rad; w1->ns=w0->ns; if (w0->itg>0) w1->itg=w0->itg+itsi; else w1->itg=w0->itg; w1->abs=nextabs; nextabs+=w1->ns; w1->dispnr=w0->dispnr; w0=w1; w1++; } while (++i < nrpt); } w00++; } if (nrpt==0) { s0=s1=surfaces; i=numsurfaces; } else { int oldnumsurfaces=numsurfaces; i=numsurfaces*nrpt; numsurfaces*=(1+nrpt); EXPAND_IF_NECESSARY(numsurfaces,maxsurfaces,surfaces) s0=surfaces; s1=surfaces+oldnumsurfaces; } while (i--) { s1->type=s0->type; pointtransf(&s0->p0,&s1->p0,mat,xs,ys,zs); pointtransf(&s0->p1,&s1->p1,mat,xs,ys,zs); pointtransf(&s0->p2,&s1->p2,mat,xs,ys,zs); pointtransf(&s0->p3,&s1->p3,mat,xs,ys,zs); pointtransf(&s0->pc,&s1->pc,mat,xs,ys,zs); pointtransf(&s0->pa,&s1->pa,mat,xs,ys,zs); s0++; s1++; } return 0; } int read_nec_GR(char *s) /* GR -> generate cylindrical structure */ { char gm[60]; int n,inc; removecommas(s); if (sscanf(s,"GR%d%d",&inc,&n) != 2) return Err_scan; sprintf(gm,"GM %i %i 0 0 %g 0 0 0",inc,n-1,360./n); read_nec_GM(gm); /* GR card is translated into equivalent GM card */ return 0; } int read_nec_GS(char *s) /* GS -> geometry scale */ { double a; Wire *w; Surface *su; int i; removecommas(s); if (sscanf(s,"GS%*d%*d%lg",&a) != 1) return Err_scan; for (i=0,w=wires;ip0.x*=a; w->p0.y*=a; w->p0.z*=a; w->p1.x*=a; w->p1.y*=a; w->p1.z*=a; w->rad*=a; } for (i=0,su=surfaces;ip0.x*=a; su->p0.y*=a; su->p0.z*=a; su->p1.x*=a; su->p1.y*=a; su->p1.z*=a; su->p2.x*=a; su->p2.y*=a; su->p2.z*=a; su->p3.x*=a; su->p3.y*=a; su->p3.z*=a; su->pc.x*=a; su->pc.y*=a; su->pc.z*=a; su->pa.x*=a; su->pa.y*=a; su->pa.z*=a; } extr_str*=a; return 0; } int read_nec_GW(char *s) /* GW -> straight wire */ { Wire *w; int i; EXPAND_IF_NECESSARY(numwires,maxwires,wires) w=wires+numwires++; removecommas(s); w->rad=0; i=sscanf(s,"GW%d%d%g%g%g%g%g%g%lg",&w->itg,&w->ns,&w->p0.x,&w->p0.y,&w->p0.z,&w->p1.x,&w->p1.y,&w->p1.z,&w->rad); if (i!=8 && i!=9) return Err_scan; /* note: omitting the radius seems to be allowed, for tapered wires */ w->abs=nextabs; nextabs+=w->ns; w->dispnr=1; updateextremes(&w->p0); updateextremes(&w->p1); return 0; } int read_nec_GX(char *s) /* GX -> reflection in coordinate planes */ { int i,inc, err; removecommas(s); if (sscanf(s,"GX%d%d",&inc,&i) != 2) return Err_scan; if (i%10==1) { err=reflect(2,inc); if (err) return err; inc*=2; } if ((i/10)%10==1) { err=reflect(1,inc); if (err) return err; inc*=2; } if ((i/100)==1) return reflect(0,inc); return 0; } int read_nec_SC(char *s) /* SC -> continuation of SM, SP, or SC */ { Surface *su; int ns; Point p3; int n; if(lastCard[0]!='S'){ fprintf(stderr,"SC card not preceded by SM, SP, or SC.\n"); return Err_misc; } EXPAND_IF_NECESSARY(numsurfaces,maxsurfaces,surfaces) su=surfaces+numsurfaces++; removecommas(s); switch (lastCard[1]) { case 'M': if (sscanf(s,"SC%*i%*i%g%g%g",&su->p2.x,&su->p2.y,&su->p2.z) != 3) return Err_scan; updateextremes(&su->p2); su->pc=int3d(su->p0,su->p2,0.5); p3=int3d(su->p1,su->pc,2); updateextremes(&p3); out3d(su,0.05); su->type=SU_rect; break; case 'P': n=sscanf(s,"SC%*i%*i%g%g%g%g%g%g",&su->p2.x,&su->p2.y,&su->p2.z,&su->p3.x,&su->p3.y,&su->p3.z); if (n!=6 && n!=3) return Err_scan; updateextremes(&su->p2); switch (su->type) { case SU_rect: su->pc=int3d(su->p0,su->p2,0.5); su->p3=int3d(su->p1,su->pc,2); // we don't really need su->p3 now, but do need it in case of continuation using more SC cards updateextremes(&su->p3); out3d(su,0.05); break; case SU_tri: su->pc=int3d(int3d(su->p0,su->p1,0.5),su->p2,1./3); out3d(su,0.05); break; case SU_quad: if (n!=6) return Err_scan; updateextremes(&su->p3); su->pc=int3d(int3d(su->p0,su->p1,0.5),int3d(su->p2,su->p3,0.5),0.5); out3d(su,0.05); break; default: fprintf(stderr,"SC card for surface type which is not rectangle/triangle/quadrilateral.\n"); return Err_misc; } break; case 'C': n=sscanf(s,"SC%*i%d%g%g%g%g%g%g",&ns,&su->p2.x,&su->p2.y,&su->p2.z,&su->p3.x,&su->p3.y,&su->p3.z); if (n!=7 && n!=4) return Err_scan; Surface *suLast=surfaces+(numsurfaces-2); su->p0=suLast->p3; su->p1=suLast->p2; updateextremes(&su->p2); switch (ns) { case 1: su->type=SU_rect; su->pc=int3d(su->p0,su->p2,0.5); su->p3=int3d(su->p1,su->pc,2); updateextremes(&su->p3); out3d(su,0.05); break; case 3: if (n!=7) return Err_scan; su->type=SU_quad; updateextremes(&su->p3); su->pc=int3d(int3d(su->p0,su->p1,0.5),int3d(su->p2,su->p3,0.5),0.5); out3d(su,0.05); break; default: fprintf(stderr,"SC card specifying unsupported shape NS=%d.\n",ns); return Err_misc; } break; default: fprintf(stderr,"SC card following card %c%c.\n",lastCard[0],lastCard[1]); return Err_misc; } return 0; } int read_nec_SM(char *s) /* SM -> multiple surface patch (shown as one large rectangular piece of surface) */ { Surface *su; EXPAND_IF_NECESSARY(numsurfaces,maxsurfaces,surfaces) su=surfaces+numsurfaces; removecommas(s); if (sscanf(s,"SM%*i%*i%g%g%g%g%g%g",&su->p0.x,&su->p0.y,&su->p0.z,&su->p1.x,&su->p1.y,&su->p1.z) !=6) return Err_scan; updateextremes(&su->p0); updateextremes(&su->p1); return 0; } int read_nec_SP(char *s) /* SP -> surface patch */ { Surface *su; int ns; double x1,y1,z1,x2,y2,z2; double r; EXPAND_IF_NECESSARY(numsurfaces,maxsurfaces,surfaces) su=surfaces+numsurfaces; su->type=SU_arb; removecommas(s); if (sscanf(s,"SP%*d%d%lg%lg%lg%lg%lg%lg",&ns,&x1,&y1,&z1,&x2,&y2,&z2) !=7) return Err_scan; if (ns==0) { su->type=SU_arb; su->pc.x=x1; su->pc.y=y1; su->pc.z=z1; updateextremes(&su->pc); r=sqrt(z2/M_PI); x2*=M_PI/180; y2*=M_PI/180; su->p0.x = x1 - r*sin(y2); su->p0.y = y1 + r*cos(y2); su->p0.z = z1; su->p1.x = x1 - r*sin(x2)*cos(y2); su->p1.y = y1 - r*sin(x2)*sin(y2); su->p1.z = z1 + r*cos(x2); su->p2=int3d(su->p0,su->pc,2); out3d(su,0.1); ++numsurfaces; return 0; } switch (ns) { case 1: su->type=SU_rect; break; case 2: su->type=SU_tri; break; case 3: su->type=SU_quad; break; default: fprintf(stderr,"Unknown patch shape: NS=%i.\n",ns); return Err_misc; } su->p0.x=x1; su->p0.y=y1; su->p0.z=z1; su->p1.x=x2; su->p1.y=y2; su->p1.z=z2; updateextremes(&su->p0); updateextremes(&su->p1); return 0; } void read_nec_input(FILE *f) /* read the input file, and update the arrays of wires and surfaces accordingly */ { char s[100]; int err; int last_was_NT_or_TL=0; nextabs=1; fgets(s,100,f); while (!feof(f) && (s[0]!='E' || s[1]!='N')) { s[0]=toupper(s[0]); s[1]=toupper(s[1]); err=0; switch (s[0]) { case 'C': if (s[1]=='M') err=read_nec_CM(s); break; case 'E': if (s[1]=='X') err=read_nec_EX(s); break; case 'G': switch (s[1]) { case 'A': err=read_nec_GA(s); break; case 'C': break; /* just ignore GC cards: they only modify the radius of GW cards, which xnecview doesn't use anyway */ case 'H': err=read_nec_GH(s); break; case 'M': err=read_nec_GM(s); break; case 'R': err=read_nec_GR(s); break; case 'W': err=read_nec_GW(s); break; case 'X': err=read_nec_GX(s); break; case 'S': err=read_nec_GS(s); break; case 'F': fprintf(stderr,"Warning: unhandled cardtype in %s",s); break; } break; case 'L': if (s[1]=='D') err=read_nec_LD(s); break; case 'M': switch (s[1]) { case 'M': case 'E': printf("%s",s); break; } break; case 'N': if (s[1]=='T') { if (!last_was_NT_or_TL) numnetws=0; err=read_nec_NT(s); last_was_NT_or_TL=1; } break; case 'S': switch (s[1]) { case 'C': err=read_nec_SC(s); break; case 'M': err=read_nec_SM(s); break; case 'P': err=read_nec_SP(s); break; } break; case 'T': if (s[1]=='L') { if (!last_was_NT_or_TL) numnetws=0; err=read_nec_TL(s); last_was_NT_or_TL=1; } break; } switch (err) { case Err_scan: fprintf(stderr,"Error in parameters: %s\n",s); break; case Err_nwires: fprintf(stderr,"Too many wires.\n"); break; case Err_nsurfaces: fprintf(stderr,"Too many surfaces.\n"); break; case Err_nexcis: fprintf(stderr,"Too many excitations.\n"); break; case Err_nloads: fprintf(stderr,"Too many loads.\n"); break; case Err_nnetws: fprintf(stderr,"Too many networks and/or transmission lines.\n"); break; } lastCard[0]=s[0]; lastCard[1]=s[1]; fgets(s,100,f); } extr = extr_str; } xnecview-1.35.orig/parse_output.c0000644000175000017500000007451511005110762015706 0ustar afrb2afrb2/* XNECVIEW - a program for visualizing NEC2 input and output data * * Copyright (C) 1998-2006, Pieter-Tjerk de Boer -- pa3fwm@amsat.org * * Distributed on the conditions of version 2 of the GPL: see the files * README and COPYING, which accompany this source file. * * This module parses NEC2's output. * Furthermore, it contains some functions for later processing of this * data. */ #include #include #include #include #include #include #include "xnecview.h" double globalmaxdb=-1e30; void calcswr(NECoutput *ne) { double zr,zi,gamma; zr=ne->d[neco_zr]; zi=ne->d[neco_zi]; gamma = sqrt( ( (zr-r0)*(zr-r0) + zi*zi )/( (zr+r0)*(zr+r0) + zi*zi ) ); ne->d[neco_swr] = (1+gamma)/(1-gamma); } void calcphiabs(NECoutput *ne) { double zr,zi; zr=ne->d[neco_zr]; zi=ne->d[neco_zi]; ne->d[neco_zabs] = sqrt(zr*zr + zi*zi); ne->d[neco_zphi] = 180 * M_1_PI * atan2(zi, zr); } void *mymalloc(size_t n) { void *p; p=malloc(n); if (p==NULL) { fprintf(stderr,"Out of memory\n"); exit(1); } return p; } void *myrealloc(void *p,size_t n) { p=realloc(p,n); if (p==NULL) { fprintf(stderr,"Out of memory\n"); exit(1); } return p; } double calc_polfactor(int pol,Gain *g) { /* note: for efficiency reasons, these formulas are also in update_maxgains() */ double a,b,f; switch (pol) { case POLhor: a=g->axial*g->axial; b=sin(g->tilt); f=(a+(1-a)*b*b)/(1+a); break; case POLvert: a=g->axial*g->axial; b=cos(g->tilt); f=(a+(1-a)*b*b)/(1+a); break; case POLlhcp: a=g->axial*g->axial; f=(1+2*g->axial+a)/2/(1+a); break; case POLrhcp: a=g->axial*g->axial; f=(1-2*g->axial+a)/2/(1+a); break; default: f=1; /* POLnone, POLcolour */ } return f; } void update_maxgains(Gain *g,NECoutput *ne,double phi,double theta) { double f; double a,b; if (g->total > ne->d[neco_maxgain]) { ne->d[neco_maxgain]=g->total; ne->d[neco_phi]=phi; ne->d[neco_theta]=theta; } a=g->axial*g->axial; f=(1+2*g->axial+a)/2/(1+a); f=g->total+10*log10(f); if (f > ne->d[neco_maxgain_lhcp]) { ne->d[neco_maxgain_lhcp]=f; ne->d[neco_phi_lhcp]=phi; ne->d[neco_theta_lhcp]=theta; } f=(1-2*g->axial+a)/2/(1+a); f=g->total+10*log10(f); if (f > ne->d[neco_maxgain_rhcp]) { ne->d[neco_maxgain_rhcp]=f; ne->d[neco_phi_rhcp]=phi; ne->d[neco_theta_rhcp]=theta; } b=sin(g->tilt); f=(a+(1-a)*b*b)/(1+a); f=g->total+10*log10(f); if (f > ne->d[neco_maxgain_hor]) { ne->d[neco_maxgain_hor]=f; ne->d[neco_phi_hor]=phi; ne->d[neco_theta_hor]=theta; } b=cos(g->tilt); f=(a+(1-a)*b*b)/(1+a); f=g->total+10*log10(f); if (f > ne->d[neco_maxgain_vert]) { ne->d[neco_maxgain_vert]=f; ne->d[neco_phi_vert]=phi; ne->d[neco_theta_vert]=theta; } } void find_fb(NECoutput *ne,Radpattern *r) { int i,j,k; double theta,phi; theta=ne->d[neco_theta]*M_PI/180; phi=ne->d[neco_phi]*M_PI/180; for (k=POLnone;k<=POLrhcp;k++) { for (i=0;inumtheta;i++) { double d; d = fmod(fabs(r->gtheta[i]+theta),2*M_PI); if (fabs(d-M_PI) < 0.00001) break; } for (j=0;jnumphi;j++) { double d; d = fmod(fabs(r->gphi[j]-phi),2*M_PI); if (fabs(d-M_PI) < 0.00001) break; } if (k==POLnone) { if (inumtheta && jnumphi) ne->d[neco_fb] = ne->d[neco_maxgain] - r->gain[j][i].total; else ne->d[neco_fb] = -DBL_MAX; } else { if (inumtheta && jnumphi) { double f; Gain *g=&r->gain[j][i]; ne->d[Neco_gsize*k+Neco_polfb2] = ne->d[Neco_gsize*k+Neco_polgain] - g->total; f=calc_polfactor(k,g); ne->d[Neco_gsize*k+Neco_polfb1] = ne->d[Neco_gsize*k+Neco_polgain] - r->gain[j][i].total + 10*log10(f); } else { ne->d[Neco_gsize*k+Neco_polfb1] = -DBL_MAX; ne->d[Neco_gsize*k+Neco_polfb2] = -DBL_MAX; } } theta=ne->d[Neco_gsize*(k+1)+Neco_poltheta]*M_PI/180; phi=ne->d[Neco_gsize*(k+1)+Neco_polphi]*M_PI/180; } } void read_nec_output_rad(FILE *f,NECoutput *ne) /* tries to read (far field) radiation pattern data from f */ { char s[200]; double phi,theta; float db,axial,tilt; char polsense; int i; int end=0; int maxthetas, maxphis; Radpattern *r; /* Some versions of NEC2 output extraneous data after "RADIATION PATTERNS" before the actual data * and column labels (e.g. RANGE = and EXP (-JKR) values ). Discard until * the true bottom of the column labels (look for DB) */ do fgets(s,200,f); while (!feof(f) && !strstr(s,"DB")); if (feof(f)) return; /* allocate some memory; several of these arrays may need to be reallocated later on, if it turns out that their initial size is too small */ r=mymalloc(sizeof(Radpattern)); maxphis=128; r->gain=mymalloc(maxphis*sizeof(Gain*)); r->gphi=mymalloc(maxphis*sizeof(double)); /* read the first block of radiation data, i.e., for one value of phi and the full range of theta */ /* after this, we know how many thetas to expect per phi, which makes the memory allocation simpler */ fgets(s,200,f); removecommas(s); if (sscanf(s,"%lg%lg%*g%*g%g%g%g %c",&theta,&phi,&db,&axial,&tilt,&polsense)!=6) return; r->gphi[0]=phi; r->numphi=1; r->numtheta=0; maxthetas=16; r->gtheta=mymalloc(maxthetas*sizeof(double)); r->gain[0]=mymalloc(maxthetas*sizeof(Gain)); do { Gain *g; if (r->numtheta>=maxthetas) { maxthetas*=2; r->gtheta=myrealloc(r->gtheta,maxthetas*sizeof(double)); r->gain[0]=myrealloc(r->gain[0],maxthetas*sizeof(Gain)); } g=&r->gain[r->numphi-1][r->numtheta]; g->total=db; g->tilt=tilt*M_PI/180; if (polsense=='R') axial=-axial; g->axial=axial; update_maxgains(g,ne,phi,theta); r->gtheta[r->numtheta++]=theta*M_PI/180; fgets(s,200,f); removecommas(s); if (sscanf(s,"%lg%lg%*g%*g%g%g%g %c",&theta,&phi,&db,&axial,&tilt,&polsense)!=6) { end=1; break; } } while (phi==r->gphi[0]); r->gtheta=myrealloc(r->gtheta,r->numtheta*sizeof(double)); r->gain[0]=myrealloc(r->gain[0],r->numtheta*sizeof(Gain)); r->gphi[0]*=M_PI/180; /* next, read the rest of the data, i.e., for the same values of theta and the entire range of phi */ if (!end) { i=0; while (1) { Gain *g; if (i==0) { if (r->numphi>=maxphis) { maxphis*=2; r->gain=myrealloc(r->gain,maxphis*sizeof(Gain)); r->gphi=myrealloc(r->gphi,maxphis*sizeof(double)); } r->gain[r->numphi]=mymalloc(r->numtheta*sizeof(Gain)); r->gphi[r->numphi++]=phi*M_PI/180; } g=&r->gain[r->numphi-1][i]; g->total=db; g->tilt=tilt*M_PI/180; if (polsense=='R') axial=-axial; g->axial=axial; update_maxgains(g,ne,phi,theta); if (++i==r->numtheta) i=0; fgets(s,200,f); removecommas(s); if (sscanf(s,"%lg%lg%*g%*g%g%g%g %c",&theta,&phi,&db,&axial,&tilt,&polsense)!=6) break; } } r->gain=myrealloc(r->gain,r->numphi*sizeof(Gain)); r->gphi=myrealloc(r->gphi,r->numphi*sizeof(double)); /* further processing of maximum gain info */ if (ne->d[neco_maxgain] > globalmaxdb) globalmaxdb=ne->d[neco_maxgain]; find_fb(ne,r); /* allocate arrays which will later be filled with the grid coordinates in 3D space */ r->gpo=mymalloc(r->numphi*sizeof(Point*)); for (i=0;inumphi;i++) r->gpo[i]=mymalloc(r->numtheta*sizeof(Point)); /* allocate and fill the arrays of sines and cosines of theta and phi */ r->sin_gphi=mymalloc(r->numphi*sizeof(double)); r->cos_gphi=mymalloc(r->numphi*sizeof(double)); for (i=0;inumphi;i++) { r->sin_gphi[i]=sin(r->gphi[i]); r->cos_gphi[i]=cos(r->gphi[i]); } r->sin_gtheta=mymalloc(r->numtheta*sizeof(double)); r->cos_gtheta=mymalloc(r->numtheta*sizeof(double)); for (i=0;inumtheta;i++) { r->sin_gtheta[i]=sin(r->gtheta[i]); r->cos_gtheta[i]=cos(r->gtheta[i]); } /* finally, attach the set of radiation data just read to the *ne structure */ r->next=NULL; if (!ne->rp) ne->rp=r; else { Radpattern *p; p=ne->rp; while (p->next) p=p->next; p->next=r; } } Currents *read_nec_output_seg(FILE *f) /* tries to read segmentation data from f; returns NULL if not succesful */ { char s[200]; char *p; Currents *cu; /* skip some column labels etc., until we find a line that starts with a digit */ do { fgets(s,200,f); p=s; while (*p==' ') p++; } while (!isdigit(*p) && !feof(f)); if (feof(f)) return NULL; /* allocate memory */ cu=mymalloc(sizeof(Currents)); cu->numseg=0; cu->maxseg=1; cu->s=mymalloc(cu->maxseg*sizeof(*(cu->s))); cu->maxI=0; cu->maxQ=0; /* read the segmentation geometry */ do { double x,y,z,l,alpha,beta; double dx,dy,dz; Segcur *se; if (sscanf(s,"%*d%lg%lg%lg%lg%lg%lg",&x,&y,&z,&l,&alpha,&beta)!=6) break; EXPAND_IF_NECESSARY(cu->numseg,cu->maxseg,cu->s) se=cu->s+cu->numseg; alpha*=M_PI/180; beta*=M_PI/180; l/=2; dx=l*cos(alpha)*cos(beta); dy=l*cos(alpha)*sin(beta); dz=l*sin(alpha); se->c.x=x; se->c.y=y; se->c.z=z; se->p0.x=x-dx; se->p0.y=y-dy; se->p0.z=z-dz; se->p1.x=x+dx; se->p1.y=y+dy; se->p1.z=z+dz; updateextremes(&se->c); se->re[0]=dx/l; se->re[1]=dy/l; se->re[2]=dz/l; if (cos(alpha)==0) { se->q0[0]=1; se->q0[1]=0; se->q0[2]=0; se->q1[0]=0; se->q1[1]=1; se->q1[2]=0; } else { se->q0[0]=sin(alpha)*cos(beta); se->q0[1]=sin(alpha)*sin(beta); se->q0[2]=-cos(alpha); se->q1[0]=sin(beta); se->q1[1]=-cos(beta); se->q1[2]=0; } se->qre=0; se->qim=0; cu->numseg++; fgets(s,200,f); removecommas(s); } while (!feof(f)); cu->numrealseg=cu->numseg; cu->numanimseg=cu->numseg; return cu; } void read_nec_output_patches(FILE *f,Currents *cu) /* tries to read surface patch data from f */ { char s[200]; char *p; /* skip some column labels etc., until we find a line that starts with a digit */ do { fgets(s,200,f); p=s; while (*p==' ') p++; } while (!isdigit(*p) && !feof(f)); if (feof(f)) return; /* read the surface patch geometry */ do { double x,y,z,area; if (sscanf(s,"%*d%lg%lg%lg%*g%*g%*g%lg",&x,&y,&z,&area)!=4) break; EXPAND_IF_NECESSARY(cu->numseg,cu->maxseg,cu->s) cu->s[cu->numseg].c.x=x; cu->s[cu->numseg].c.y=y; cu->s[cu->numseg].c.z=z; updateextremes(&cu->s[cu->numseg].c); cu->s[cu->numseg].area=area; cu->numseg++; fgets(s,200,f); removecommas(s); } while (!feof(f)); cu->numanimseg=cu->numseg; } Currents *read_nec_output_currents(FILE *f, Currents *cug) /* tries to read segment currents from f */ { char s[200]; char *p; Currents *cu; int i; /* skip column labels etc., until we find a line that starts with a digit */ do { fgets(s,200,f); p=s; while (*p==' ') p++; } while (!isdigit(*p) && !feof(f)); if (feof(f)) return NULL; /* allocate memory and copy the geometry info that we collected earlier: */ cu=mymalloc(sizeof(Currents)); memcpy(cu,cug,sizeof(Currents)); cu->maxseg=cu->numseg; cu->s=mymalloc(cu->maxseg*sizeof(*(cu->s))); memcpy(cu->s,cug->s,cu->maxseg*sizeof(*(cu->s))); /* read the amplitude and phase for every segment */ i=0; do { int j; if (sscanf(s,"%d%*d%*g%*g%*g%*g%*g%*g%g%g",&j,&cu->s[i].a,&cu->s[i].phase)!=3) break; if (i!=j-1) break; for (j=0;j<3;j++) { cu->s[i].im[j] = cu->s[i].re[j] * cu->s[i].a * sin(cu->s[i].phase*M_PI/180); cu->s[i].re[j] = cu->s[i].re[j] * cu->s[i].a * cos(cu->s[i].phase*M_PI/180); } if (cu->s[i].a>cu->maxI) cu->maxI=cu->s[i].a; i++; fgets(s,200,f); } while (inumrealseg && !feof(f)); if (i!=cu->numrealseg) { fprintf(stderr,"Segment currents data in output file incorrect or incomplete\n"); free(cu->s); free(cu); return NULL; } return cu; } void read_nec_output_charges(FILE *f, Currents *cu) /* tries to read segment charges from f */ { char s[200]; char *p; int i; /* skip column labels etc., until we find a line that starts with a digit */ do { fgets(s,200,f); p=s; while (*p==' ') p++; } while (!isdigit(*p) && !feof(f)); if (feof(f)) return; /* read the amplitude and phase for every segment */ i=0; do { int j; double a; double l; if (sscanf(s,"%d%*d%*g%*g%*g%*g%g%g%lg",&j,&cu->s[i].qre,&cu->s[i].qim,&a)!=4) break; if (i!=j-1) break; /* note: we read charge densities, so we need to multiply them by the segment length to obtain the charges themselves */ l=0; l += (cu->s[i].p1.x-cu->s[i].p0.x)*(cu->s[i].p1.x-cu->s[i].p0.x); l += (cu->s[i].p1.y-cu->s[i].p0.y)*(cu->s[i].p1.y-cu->s[i].p0.y); l += (cu->s[i].p1.z-cu->s[i].p0.z)*(cu->s[i].p1.z-cu->s[i].p0.z); l=sqrt(l); cu->s[i].qre*=l; cu->s[i].qim*=l; a*=l; if (a>cu->maxQ) cu->maxQ=a; i++; fgets(s,200,f); } while (inumrealseg && !feof(f)); if (i!=cu->numrealseg) { fprintf(stderr,"Segment charges data in output file incorrect or incomplete\n"); return; } return; } Currents *read_nec_output_patchcurrents(FILE *f, Currents *cu) /* tries to read surface patch currents from f */ { char s[200]; char *p; int i; int oldnumseg; /* skip some column labels etc., until we find a line that starts with a digit */ do { fgets(s,200,f); p=s; while (*p==' ') p++; } while (!isdigit(*p) && !feof(f)); if (feof(f)) return NULL; /* skip the line containing only the patch number */ fgets(s,200,f); /* read the amplitude and phase for every segment */ i=cu->numrealseg; oldnumseg=cu->numseg; EXPAND_IF_NECESSARY(2*cu->numseg-cu->numrealseg,cu->maxseg,cu->s) do { double xr,xi,yr,yi,zr,zi; /* real and imaginary components of current density in X, Y and Z direction */ double xr2,xi2,yr2,yi2,zr2,zi2; /* and their squares */ double totl; /* length of the segment that we're going to use to represent the patch current */ double dxr,dyr,dzr; double dxi,dyi,dzi; double h,hh; double ta,si,co,phi; #if 0 double dx,dy,dz; double l; #endif if (sscanf(s,"%*g%*g%*g%*g%*g%*g%*g%lg%lg%lg%lg%lg%lg",&xr,&xi,&yr,&yi,&zr,&zi)!=6) break; totl=sqrt(cu->s[i].area)/2; /* in principle we can choose any length we wish, but this one gives a nice picture */ cu->s[i].re[0]=xr; cu->s[i].im[0]=xi; cu->s[i].re[1]=yr; cu->s[i].im[1]=yi; cu->s[i].re[2]=zr; cu->s[i].im[2]=zi; #if 0 /* This (disabled) piece of code just represents the currents on the surface patch by two segment currents corresponding to the real and imaginary vectors supplied by NEC. The code is here only for testing purposes. */ xr2=xr*xr; xi2=xi*xi; yr2=yr*yr; yi2=yi*yi; zr2=zr*zr; zi2=zi*zi; cu->s[cu->numseg].c = cu->s[i].c; hh=sqrt(xr2+xi2+yr2+yi2+zr2+zi2); cu->s[i].a= hh*cu->s[i].area/totl; cu->s[cu->numseg].a=hh*cu->s[i].area/totl; h=sqrt(xr2+yr2+zr2); l=totl*h/hh; dx=xr*totl/hh/2; dy=yr*totl/hh/2; dz=zr*totl/hh/2; cu->s[i].phase=0; cu->s[i].p0.x = cu->s[i].c.x-dx; cu->s[i].p0.y = cu->s[i].c.y-dy; cu->s[i].p0.z = cu->s[i].c.z-dz; cu->s[i].p1.x = cu->s[i].c.x+dx; cu->s[i].p1.y = cu->s[i].c.y+dy; cu->s[i].p1.z = cu->s[i].c.z+dz; h=sqrt(xi2+yi2+zi2); l=totl*h/hh; dx=xi*totl/hh/2; dy=yi*totl/hh/2; dz=zi*totl/hh/2; cu->s[cu->numseg].phase=90; cu->s[cu->numseg].p0.x = cu->s[cu->numseg].c.x-dx; cu->s[cu->numseg].p0.y = cu->s[cu->numseg].c.y-dy; cu->s[cu->numseg].p0.z = cu->s[cu->numseg].c.z-dz; cu->s[cu->numseg].p1.x = cu->s[cu->numseg].c.x+dx; cu->s[cu->numseg].p1.y = cu->s[cu->numseg].c.y+dy; cu->s[cu->numseg].p1.z = cu->s[cu->numseg].c.z+dz; cu->numseg++; #endif #if 1 /* In general, the current on a surface patch can be considered as "running around" an ellipse. In many linearly polarized antennas, this ellipse collapses to a straight line. The below code represents the "elliptic" surface current by two segment currents oriented along the major and minor axes of the ellipse. If the ellipse collapses to a straight line, the minor axis component becomes zero, leaving one segment aligned according to the real direction of the current in the surface patch. Particularly in the latter case, this is much more natural than taking the "raw" real and imaginary current vectors supplied by NEC, which in this case would have the same direction and thus be indistinguishable in the picture. */ xr2=xr*xr; xi2=xi*xi; yr2=yr*yr; yi2=yi*yi; zr2=zr*zr; zi2=zi*zi; cu->s[i].a=sqrt(xr2+yr2+zr2+xi2+yi2+zi2)*cu->s[i].area/totl; if (cu->s[i].a>cu->maxI) cu->maxI=cu->s[i].a; hh=xr2+yr2+zr2-xi2-yi2-zi2; if (hh==0) { co = sqrt(0.5); si = sqrt(0.5); phi = M_PI/4; } else { ta = 2*(xr*xi+yr*yi+zr*zi)/(xr2+yr2+zr2-xi2-yi2-zi2); co = sqrt(0.5+0.5*sqrt(1/(ta*ta+1))); si = sqrt(0.5-0.5*sqrt(1/(ta*ta+1))); if (ta<0) si=-si; phi = atan(ta)/2; } dxr = xr*co + xi*si; dyr = yr*co + yi*si; dzr = zr*co + zi*si; dxi = -xr*si + xi*co; dyi = -yr*si + yi*co; dzi = -zr*si + zi*co; h = totl/sqrt(xr2+yr2+zr2+xi2+yi2+zi2)/2; cu->s[i].phase = phi*180/M_PI; cu->s[i].p0.x = cu->s[i].c.x-dxr*h; cu->s[i].p0.y = cu->s[i].c.y-dyr*h; cu->s[i].p0.z = cu->s[i].c.z-dzr*h; cu->s[i].p1.x = cu->s[i].c.x+dxr*h; cu->s[i].p1.y = cu->s[i].c.y+dyr*h; cu->s[i].p1.z = cu->s[i].c.z+dzr*h; cu->s[cu->numseg].a = cu->s[i].a; cu->s[cu->numseg].c = cu->s[i].c; cu->s[cu->numseg].phase = phi*180/M_PI+90; cu->s[cu->numseg].p0.x = cu->s[cu->numseg].c.x-dxi*h; cu->s[cu->numseg].p0.y = cu->s[cu->numseg].c.y-dyi*h; cu->s[cu->numseg].p0.z = cu->s[cu->numseg].c.z-dzi*h; cu->s[cu->numseg].p1.x = cu->s[cu->numseg].c.x+dxi*h; cu->s[cu->numseg].p1.y = cu->s[cu->numseg].c.y+dyi*h; cu->s[cu->numseg].p1.z = cu->s[cu->numseg].c.z+dzi*h; cu->numseg++; #endif i++; fgets(s,200,f); /* again, skip the line containing only the patch number */ fgets(s,200,f); } while (is); free(cu); return NULL; } return cu; } void normalize_currents(Currents *cu) { int i; double max=0; for (i=0;inumseg;i++) if (maxs[i].a) max=cu->s[i].a; for (i=0;inumseg;i++) cu->s[i].a/=max; } void read_nec_output_nearfield(FILE *f, NECoutput *ne,int magnetic) /* tries to read near electric or magnetic field from f */ { char s[200]; char *p; Nearfield *nf,*np; /* skip some column labels etc., until we find a line that starts with a digit, possibly preceeded by a minus */ do { fgets(s,200,f); p=s; while (*p==' ' || *p=='-') p++; } while (!isdigit(*p) && !feof(f)); do { double mag[3], phase[3]; /* note: "mag" is an abbreviation for magnitude, not for magnetic... */ Point p; int i; double l; /* parse a line of data */ if (sscanf(s,"%g%g%g%lg%lg%lg%lg%lg%lg", &p.x, &p.y, &p.z, &mag[0], &phase[0], &mag[1], &phase[1], &mag[2], &phase[2])!=9) break; np=ne->nf; if (np==NULL) { /* no near field data yet? then this is the first point */ nf=mymalloc(sizeof(Nearfield)); if (magnetic) nf->evalid=0; else nf->hvalid=0; ne->nf=nf; nf->next=NULL; nf->p=p; } else { /* otherwise, search whether this point is already in the data set */ do { if (np->p.x==p.x && np->p.y==p.y && np->p.z==p.z) { /* if so, just copy the new data into the existing record */ nf=np; break; } if (!np->next) { /* otherwise, append a new record to the set */ nf=mymalloc(sizeof(Nearfield)); if (magnetic) nf->evalid=0; else nf->hvalid=0; np->next=nf; nf->next=NULL; nf->p=p; break; } np=np->next; } while(1); } if (magnetic) nf->hvalid=1; else nf->evalid=1; l=0; for (i=0;i<3;i++) { if (magnetic) { nf->hre[i] = mag[i]*cos(phase[i]*M_PI/180); l += nf->hre[i]*nf->hre[i]; nf->him[i] = mag[i]*sin(phase[i]*M_PI/180); l += nf->him[i]*nf->him[i]; } else { nf->ere[i] = mag[i]*cos(phase[i]*M_PI/180); l += nf->ere[i]*nf->ere[i]; nf->eim[i] = mag[i]*sin(phase[i]*M_PI/180); l += nf->eim[i]*nf->eim[i]; } } l=sqrt(l); if (magnetic) { if (l>ne->maxh) ne->maxh=l; } else { if (l>ne->maxe) ne->maxe=l; } /* read next line */ fgets(s,200,f); } while(1); } int read_nec_output(FILE *f) /* tries to read NEC output data from f; returns 1 if not succesful */ { char s[200]; char *p; double zr,zi; int new_rp_index=-1; int near_index=-1; int curr_index=-1; NECoutput *ne; double freq; int i; Currents *cu=NULL; int oldnumneco; oldnumneco=numneco; /* skip all lines until a line containing "STRUCTURE SPECIFICATION": this effectively skips all comments (from CM cards) and text resulting from reading Numerical Green's Function parts. Of course, if a user writes "STRUCTURE SPECIFICATION" in his comment lines, this still fails... */ do { fgets(s,200,f); } while (!feof(f) && !strstr(s,"STRUCTURE SPECIFICATION")); if (feof(f)) return 1; /* check whether segmentation and surface patch data is included, and if so, read it */ do { fgets(s,200,f); if (strstr(s,"SEGMENTATION DATA")) cu=read_nec_output_seg(f); if (cu && strstr(s,"SURFACE PATCH DATA")) read_nec_output_patches(f,cu); } while (!feof(f) && !strstr(s,"FREQUENCY")); printf("# freq. Zr Zi SWR gain f/b phi theta\n"); while (!feof(f)) { EXPAND_IF_NECESSARY(numneco,maxfreqs,neco) /* search for the frequency and read it */ do fgets(s,200,f); while (!feof(f) && !strstr(s,"FREQUENCY=") && !strstr(s,"FREQUENCY : ")); if (feof(f)) break; p = strchr(s,'='); if (!p) p = strchr(s,':'); freq = atof(p+1); /* find the right place in memory, and prepare it if needed */ if (numneco==0 || freq>neco[numneco-1].f) { ne=neco+numneco; numneco++; ne->f=0; } else { for (i=0, ne=neco; if>=freq) { if (ne->f==freq) break; memmove(ne+1,ne,(neco+numneco-ne)*sizeof(NECoutput)); numneco++; if (new_rp_index>=i) new_rp_index++; break; } } if (ne->f!=freq) { ne->f = freq; ne->rpgpovalid = 0; ne->rp = NULL; ne->cu = NULL; ne->nf = NULL; ne->maxe = ne->maxh = 0; ne->d[neco_maxgain]=-DBL_MAX; ne->d[neco_maxgain_hor]=-DBL_MAX; ne->d[neco_maxgain_vert]=-DBL_MAX; ne->d[neco_maxgain_lhcp]=-DBL_MAX; ne->d[neco_maxgain_rhcp]=-DBL_MAX; } /* find and read the input impedance, and calculate the SWR */ do fgets(s,200,f); while (!feof(f) && !strstr(s,"ANTENNA INPUT PARAMETERS")); if (feof(f)) break; do { /* skip lines until a line starting with a digit is found */ fgets(s,200,f); p=s; while (*p==' ') p++; } while (!feof(f) && !isdigit(*p)); if ((p=strchr(s,'*'))) *p=' '; if (sscanf(s,"%*i%*i%*g%*g%*g%*g%lg%lg",&zr,&zi)!=2) break; ne->d[neco_zr]=zr; ne->d[neco_zi]=zi; calcswr(ne); calcphiabs(ne); /* check whether radiation pattern data is included for this frequency, and if so, read it */ /* check also whether currents and/or charge data is included for this frequency, and if so, read it */ /* check also whether near field data is included for this frequency, and if so, read it */ do { fgets(s,200,f); if (cu && strstr(s,"CURRENTS AND LOCATION")) { if (ne->cu) { fprintf(stderr,"File contains more than one set of currents at a frequency; ignoring earlier data\n"); free(ne->cu->s); free(ne->cu); } ne->cu=read_nec_output_currents(f,cu); if (ne->cu && curr_index<0) curr_index=ne-neco; } if (ne->cu && strstr(s,"SURFACE PATCH CURRENTS")) ne->cu=read_nec_output_patchcurrents(f,ne->cu); if (cu && strstr(s,"CHARGE DENSITIES")) { if (!ne->cu) fprintf(stderr,"Charge density information should come after currents information; ignoring it\n"); else read_nec_output_charges(f,ne->cu); } if (strstr(s,"NEAR ELECTRIC FIELDS")) { read_nec_output_nearfield(f,ne,0); if (near_index<0) near_index=ne-neco; } if (strstr(s,"NEAR MAGNETIC FIELDS")) { read_nec_output_nearfield(f,ne,1); if (near_index<0) near_index=ne-neco; } if (strstr(s,"RADIATION PATTERNS")) { read_nec_output_rad(f,ne); if (!ne->rp) return 1; if (new_rp_index<0) new_rp_index=ne-neco; } } while (!feof(f) && !strstr(s,"FREQUENCY")); if (ne->cu) normalize_currents(ne->cu); /* print the data */ printf("%8g %8g %8g %8g ",ne->f,zr,zi,ne->d[neco_swr]); if (ne->rp) if (ne->d[neco_fb]>-DBL_MAX) printf("%8g %8g %8g %8g\n",ne->d[neco_maxgain],ne->d[neco_fb],ne->d[neco_phi],ne->d[neco_theta]); else printf("%8g - %8g %8g\n",ne->d[neco_maxgain],ne->d[neco_phi],ne->d[neco_theta]); else printf(" - - - -\n"); } if (cu) { free(cu->s); free(cu); } if (rp_index<0 || rp_index>=numneco) { if (new_rp_index>=0) rp_index=new_rp_index; else if (near_index>=0) rp_index=near_index; else if (curr_index>=0) rp_index=curr_index; } if (neco[rp_index].rp==NULL && new_rp_index>=0) rp_index=new_rp_index; return (numneco==oldnumneco); } #define pow10(x) exp(M_LN10*(x)) void process_nec_output(NECoutput *ne) /* transform gain distribution into an array of points in 3D space */ { int i,j; double r=0; Radpattern *rp; if (ne->rpgpovalid) return; rp=ne->rp; if (extr_str==0) extr=1; else extr=extr_str*GAINSIZE; while (rp) { for (i=0;inumtheta;i++) for (j=0;jnumphi;j++) { double db; double f; Gain *g=&rp->gain[j][i]; f=calc_polfactor(polarization,g); if (f<1e-200) r=0; else { db=g->total-globalmaxdb; switch (gainscale) { case GSlinpower: r=f*pow10(db/10); break; /* linear in power */ case GSlinvolt: r=sqrt(f)*pow10(db/20); break; /* linear in voltage */ case GSarrl: r=exp(0.116534*(db+10*log10(f))/2); break; /* arrl */ case GSlog: r=db+10*log10(f); if (r<-40) r=0; else r=r/40+1; break; /* log */ } r*=extr; } if (r<1e-6) r=1e-6; /* prevent points from being just about exactly at the origin, since this confuses the edge hiding calculations */ rp->gpo[j][i].x = rp->cos_gphi[j] * rp->sin_gtheta[i] * r; rp->gpo[j][i].y = rp->sin_gphi[j] * rp->sin_gtheta[i] * r; rp->gpo[j][i].z = rp->cos_gtheta[i] * r; } rp=rp->next; } ne->rpgpovalid=1; } void calc_vgain(void) /* update the vgain records in accordance with the viewing direction */ { NECoutput *ne; int i,j,k; int i0=0,j0=0; Radpattern *rp,*rp0; double dif,d,d0; double ph,th; ph=fmod(phi,360.)*(M_PI/180.); ph+=5*M_PI; th=theta*(M_PI/180.); for (k=0, ne=neco; krp; ne->d[neco_vgain]=-DBL_MAX; ne->d[neco_vgain2]=-DBL_MAX; dif=DBL_MAX; rp0=NULL; while (rp) { d0=DBL_MAX; i0=0; for (i=0;inumtheta;i++) { d = fabs(rp->gtheta[i]-th); if (dnumphi;j++) { d = d0 + fabs(fmod(ph-rp->gphi[j],2*M_PI)-M_PI); if (dgain[j0][i0]; ne->d[neco_vgain]=g->total; ne->d[neco_vgain2]=g->total+10*log10(calc_polfactor(polarization,g)); } } } rp=rp->next; } if (rp0) { double ph0,th0; th0=rp0->gtheta[i0]; ph0=rp0->gphi[j0]; for (i=0;inumtheta;i++) { double d; d = fmod(fabs(rp0->gtheta[i]+th0),2*M_PI); if (fabs(d-M_PI) < 0.00001) break; } for (j=0;jnumphi;j++) { double d; d = fmod(fabs(rp0->gphi[j]-ph0),2*M_PI); if (fabs(d-M_PI) < 0.00001) break; } if (inumtheta && jnumphi) { Gain *g; g=&rp0->gain[j][i]; ne->d[neco_vfb] = ne->d[neco_vgain2]-g->total+10*log10(calc_polfactor(polarization,g)); ne->d[neco_vfb2] = ne->d[neco_vgain2]-g->total; } else { ne->d[neco_vfb] = -DBL_MAX; ne->d[neco_vfb2] = -DBL_MAX; } } } } void mark_gpo_invalid(void) { NECoutput *ne; int k; for (k=0, ne=neco; krpgpovalid=0; } xnecview-1.35.orig/postscript.c0000644000175000017500000001275411005110762015363 0ustar afrb2afrb2/* XNECVIEW - a program for visualizing NEC2 input and output data * * Copyright (C) 1998-2006, Pieter-Tjerk de Boer -- pa3fwm@amsat.org * * Distributed on the conditions of version 2 of the GPL: see the files * README and COPYING, which accompany this source file. * * This module contains all low level postscript drawing routines. */ #include #include #include #include #include "xnecview.h" FILE *psfile=NULL; int needstroke; int needmoveto; GdkColor *xcprev=NULL; void ps_DrawLine(double x1,double y1,double x2,double y2) { static double lastx=-1,lasty=-1; if (x1!=lastx || y1!=lasty || needmoveto) { if (needstroke) fprintf(psfile,"stroke\n"); fprintf(psfile,"%g %g m\n",x1,y1); needmoveto=0; } fprintf(psfile,"%g %g l\n",x2,y2); lastx=x2; lasty=y2; needstroke=1; } void ps_SetLineAttributes(unsigned line_width,int line_style,int cap_style,int join_style) { if (needstroke) { fprintf(psfile,"stroke\n"); needstroke=0; } if (line_width==0) line_width++; /* Zero width lines for X11 speed of drawing but in postscript don't really want the thinnest lines which are possible on the device! */ fprintf(psfile,"%u setlinewidth ",line_width); switch (line_style) { case GDK_LINE_SOLID: fprintf(psfile,"[] 0 setdash\n"); break; case GDK_LINE_ON_OFF_DASH: fprintf(psfile,"[5 5] 0 setdash\n"); break; } if (cap_style!=GDK_CAP_ROUND || join_style!=GDK_JOIN_ROUND || line_style==GDK_LINE_DOUBLE_DASH) { puts("Error in ps_SetLineAttributes!"); } needmoveto=1; } void ps_SetForeground(GdkColor *xc) { if (xc==xcprev) return; xcprev=xc; if (needstroke) { fprintf(psfile,"stroke\n"); needstroke=0; } fprintf(psfile,"%g %g %g setrgbcolor\n",xc->red/65535.,xc->green/65535.,xc->blue/65535.); needmoveto=1; } void ps_ClearWindow(void) { } void ps_DrawString(double a,double b,char *s,double d,double e) { fprintf(psfile,"gsave (%s) dup stringwidth pop %g mul %g add\n",s,-d,a); fprintf(psfile,"%g descent sub ascent descent add %g mul add m 1 -1 scale show grestore\n", b, e); } void ps_Complete(void) { } void ps_SetClipRectangle(double x1,double y1,double x2,double y2) { if (needstroke) { fprintf(psfile,"stroke\n"); needstroke=0; } xcprev=NULL; fprintf(psfile,"grestore gsave\n"); fprintf(psfile,"newpath %g %g moveto %g %g lineto %g %g lineto %g %g lineto closepath clip newpath\n", x1,y1, x1,y2, x2,y2, x2,y1 ); } Outdev out_ps = { ps_SetLineAttributes, ps_DrawLine, ps_SetForeground, ps_ClearWindow, ps_DrawString, ps_Complete, ps_SetClipRectangle, NULL, } ; #define PSsize 400 #define PSllx 50 #define PSlly 150 int write_postscript(const char *filename,void (*drawfn)(int),int xsize,int ysize) { Outdev *oldout; time_t ti; psfile=fopen(filename,"w"); if (!psfile) return 1; oldout=out; out=&out_ps; time(&ti); fprintf(psfile,"%%!PS-Adobe-2.0 EPSF-2.0\n" "%%%%DocumentFonts: %s\n" "%%%%Title: Xnecview output\n" "%%%%Creator: Xnecview %s\n" "%%%%CreationDate: %s" "%%%%Pages: 1\n" "%%%%BoundingBox: %g %g %g %g\n" "%%%%EndComments\n", PSFONT, VERSION, ctime(&ti), (double)PSllx,(double)PSlly,(double)PSllx+xsize,(double)PSlly+ysize); fprintf(psfile,"/xnecview 10 dict def\n xnecview begin \n"); /* use a private dictionary as recommended in EPSF-3.0 standard */ fprintf(psfile,"/l /lineto load def\n"); /* define abbreviations l and m for lineto and moveto */ fprintf(psfile,"/m /moveto load def\n"); fprintf(psfile,"end\n"); /* end use (definition) of local dictionary */ fprintf(psfile,"%%%%EndProlog\n"); fprintf(psfile,"%%%%Page: 1 1\n"); fprintf(psfile,"xnecview begin\n"); /* use local dictionary again */ fprintf(psfile,"%g %g translate 1 -1 scale\n", /* set scaling and translation such that our Xwindow coordinates can be used */ (double)PSllx, (double)PSlly+ysize ); fprintf(psfile,"newpath 0 0 moveto 0 %g lineto %g %g lineto %g 0 lineto closepath clip newpath\n", (double)ysize, (double)xsize, (double)ysize, (double)xsize ); fprintf(psfile,"1 setlinejoin 1 setlinecap\n"); fprintf(psfile,"/%s findfont %i scalefont setfont\n",PSFONT,fontheight); /* prepare the font */ fprintf(psfile,"gsave 0 0 moveto (10ALIZgjy) true charpath pathbbox grestore /ascent exch def pop -1 mul /descent exch def pop\n"); /* estimate ascent and descent of font */ fprintf(psfile,"gsave\n"); /* this gsave will be grestore'd by the first SetClipRectangle call */ needstroke=0; needmoveto=0; xcprev=NULL; drawfn(0); #if 0 fprintf(psfile,"save 0 setgray 10 %i moveto 1 -1 scale (Antenna: %s) show restore\n", fontheight,inputfilename); if (gainplot!=GPnone) { fprintf(psfile,"save 0 setgray 10 %i moveto 1 -1 scale (Scale: %s%s%s) show restore\n", 2*fontheight, GSnames[gainscale], scaleplot ? ", " : "", scaleplot ? GSnumbers[gainscale] : ""); } #endif if (needstroke) fprintf(psfile,"stroke\n"); fprintf(psfile,"grestore\n"); fprintf(psfile,"end\n"); /* end use of local dictionary */ fprintf(psfile,"showpage\n"); out=oldout; return ferror(psfile) | fclose(psfile); } xnecview-1.35.orig/xnecview.1x0000644000175000017500000004305311005110762015103 0ustar afrb2afrb2.\" Copyright (c) 1999-2002, Pieter Tjerk de Boer (pa3fwm@amsat.org) .\" .\" This is free documentation; you can redistribute it and/or .\" modify it under the terms of the GNU General Public License as .\" published by the Free Software Foundation; either version 2 of .\" the License, or (at your option) any later version. .\" .\" The GNU General Public License's references to "object code" .\" and "executables" are to be interpreted as the output of any .\" document formatting or typesetting system, including .\" intermediate and printed output. .\" .\" This manual is distributed in the hope that it will be useful, .\" but WITHOUT ANY WARRANTY; without even the implied warranty of .\" MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the .\" GNU General Public License for more details. .\" .\" You should have received a copy of the GNU General Public .\" License along with this manual; if not, write to the Free .\" Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, .\" USA. .\" .TH XNECVIEW 1 .SH NAME xnecview \- A program for visualizing NEC2 input and output files .SH SYNOPSIS .B xnecview .I "[options]" .I "filename" .I "[filename....]" .SH DESCRIPTION .B Xnecview can visualize NEC2 input (structure of the antenna model) and output data files (gain as a function of direction, gain and impedance as a function of frequency). Structure and gain are shown as a three-dimensional picture which can be rotated by the mouse. The program will determine the type of data (input or output) from the files themselves. More information and some examples can be found on the web at http://www.cs.utwente.nl/~ptdeboer/ham/xnecview/ .SH USAGE On the commandline, the program expects to find one or more filenames, each containing either NEC2 structure (input) data, or output data (impedance and radiation pattern). The program will determine the type of data from the data itself. Depending on the data found in the file(s) specified, one or two windows are opened. Window 1 shows a 3D plot of the structure (wires etc.) of the antenna, and/or the spatial distribution of the radiation. Window 2 shows a set of graphs of several quantities (SWR, gain, etc.) as a function of frequency. .SS Window 1 This window shows the antenna's structure and/or the gain pattern. Initially, the Z axis points up, the X axis points to lower left, and the Y axis points to lower right (unless you changed those settings in the source code). The view can be manipulated using the mouse as follows: .TP .B rotate move mouse while holding left button depressed (additionally, keep CTRL depressed to drag only a partial picture for higher speed) .TP .B zoom move mouse up/down while holding middle button depressed (additionally, keep CTRL depressed to drag only a partial picture for higher speed); alternatively, click left mouse button for zooming in or click right mouse button for zooming out. .TP .B move picture move mouse while holding right button depressed (additionally, keep CTRL depressed to drag only a partial picture for higher speed) .TP .B go back to original view click middle mouse button .PP The view can also be rotated using the arrow keys. The keys PageUp and PageDown select a different frequency, if radiation data is available at more than one frequency. The top of the window contains a set of buttons and other indicators; from left to right these are: .TP .B quit to stop the program; keyboard shortcut: Q .TP .B reload to reload the files; keyboard shortcuts: R and . .PP .TP .B export for saving the picture as an (encapsulated) PostScript or a PNG file. .TP .B none/struct/+tags/currents/animation toggles displaying of antenna structure on and off; in '+tags' mode, segment tag numbers are displayed too (which can be helpful when trying to modify an antenna structure). For display of currents and animations, see below. .TP .B none/slice/frame/opaque/near toggles display of gain pattern: either none is shown, or only slices in the coordinate planes, or the entire 3D structure (of course subject to the availability of data in NEC's output file, and thus ultimately to the RP cards used in the input file). The 3D structure can either be shown as a wire mesh (i.e., transparent), or as an opaque surface (i.e., with hidden lines removed); the latter usually gives a clearer picture, but is often somewhat slower, and is not available if the NEC output data do not cover a theta range from 0 to 90 or 180 degrees, and a phi range from 0 to between 270 and 360 degrees; note that the gain surface is only opaque w.r.t. itself, not to other elements of the picture such as the antenna structure. For display of near fields, see under 'animation' below. .TP .B lin.P/lin.V/arrl/log toggles gain scaling (linear in power, linear in voltage, ARRL-style, or logarithmic with -40 dB at the center). .TP .B total/hor./vert./lhcp/rhcp/colour determines the handling of radiation's polarization: whether the gain shown is according to the total power regardless of polarization, or only the horizontal/vertical/left-hand circular/right-hand circular component. Choosing "colour" also shows the total power, but uses colour to show whether the radiation is mostly linearly polarized, or lhcp or rhcp. This setting also influences the gain-vs-frequency plots in window 2, and the currents display in window 1 (see below). .TP .B X, Y and Z rotate view to viewing along X, Y or Z axis respectively. A gain scale will appear, with lines at several gain levels. All of these gains are with respect to the maximum gain in the entire set of output data. .PP Though xnecview should be quite liberal in accepting output data from NEC, you might want to start out by using this line (card) in your input: .nf RP 0, 37, 72, 1000, 0, 0, 5, 5 .fi This will instruct NEC to calculate the gain at 5 degree intervals. .SS Window 2: This window contains plots of several quantities as a function of frequency, if the NEC output file contains data for several frequencies. The following quantities can be plotted: .TP .B SWR .TP .B real and imaginary part of the input impedance .TP .B phase and magnitude of the input impedance If the antenna has multiple sources, SWR and impedance are only plotted for whichever source's data appears first in the output file. .TP .B maximum gain and corresponding front/back ratio The gain as plotted is the maximum gain observed over the entire radiation pattern; this may not be the direction in which the antenna was supposed to radiate! The front/back ratio is just the ratio of the maximum observed gain to the gain in the exactly opposite direction; again, this may not be the front/back ratio you're interested in, e.g. if the main lobe is elevated so the 'back' direction points into the ground. If a specific polarization (rather than total power) has been chosen (by command-line option or by the button in the top row of window 1), this also influences the graph. Two gain lines then appear: a solid line showing the gain in the selected polarization, and a dashed line showing the total gain (for comparison). Also, two f/b lines appear: for both, the front power is only the selected polarization component, while the back power is also the selected polarization (solid line), or the total power (dashed line). .TP .B direction (phi and theta) of maximum gain .TP .B vgain and corresponding front/back ratio This is the gain in the direction towards viewer (as set by rotating the picture in window 1) and the corresponding front/back ratio. .PP The row of buttons at the top have the following functions: .TP .B quit to stop the program; keyboard shortcut: Q .TP .B reload to reload the files; keyboard shortcuts: R and . .TP .B export for saving the picture as an (encapsulated) PostScript or a PNG file. .TP .B Z0=... for setting the reference impedance for SWR calculations; furthermore, the impedance plots are limited to 20*Z0. .TP .B maxgain, vgain, SWR, Re/Im, phi/abs, and dir for toggling the display of the graphs. .PP Finally, if radiation pattern data is available, a vertical line over the entire height of the window shows the frequency at which the radiation pattern is being shown in the other window. With a mouse click or drag, or the keys PageUp, PageDown and arrow keys, another frequency can be chosen. .SS Display of current distribution: Window 1 can also be used to display the distribution of the current flowing in the antenna wires, if this information is available in the NEC output file(s); by default, it is, but it may be switched off by a 'PT' card in the NEC input. This display is enabled by selecting 'currents' in the none/struct/+tags/currents menu. Then the thickness of each wire segment indicates the magnitude of the current flowing there, while the colour indicates its phase. At the bottom of the window a few extra controls appear: two sliders for changing the colours and scaling the thicknesses, and some buttons which are discussed below. Contrary to what might be expected, the magnitude and phase of the current as plotted are not necessarily directly the values present in the NEC output file. Taking that data directly would typically not result in a meaningful display, since there is a 180 degree phase ambiguity: if the endpoints of a wire are exchanged, then the 'positive direction' in that wire is reversed, so the phase calculated by NEC changes by 180 degrees even though the antenna and its properties don't change. Therefore, it is preferable to project the current in each segment onto some reference direction, e.g., horizontal. The result of this is a measure for the contribution of that segment to the horizontally polarized radiation of the antenna. The polarization actually used, is the one selected by the polarization button in the top row; choosing "total" there (default), switches the projection operation off, so 'raw' phases and magnitudes are used. If left- or right-hand circular polarization is selected, the projection is also not performed, but every current gets an extra phase shift proportional to the angle its projection perpendicular to the viewing direction makes with horizontal. Actually, the phase displayed as discussed above is still not very interesting. Consider the following: if one segment is further away from the target to which the antenna is supposed to radiate than another segment, then the radiation from the former segment will incur a larger delay before reaching the target than the radiation from the latter segment. Effectively, this introduces another phase-shift, whose value depends on the position of the segments in space. Xnecview can compensate for this effect, by calculating this additional phase-shift in the direction toward the viewer (i.e., perpendicular to the screen); this option can be switched on and off by the first button on the bottom row. The second button locks the direction used in the phase-shift calculation; its use can best be explained by an example. Consider a yagi antenna which is aimed along the X axis. Then in order to get the correct phase-shift, one needs to rotate the picture such that the X axis points to the viewer. Unfortunately, in that orientation all elements are behind each other, so it is impossible to distinguish them in order to compare their colours. This problem is resolved by pressing the 'lock' button to lock the phase-shift calculation and then rotating the antenna to an orientation in which the elements are distinguishable. .SS Animated display of currents, charges and near fields: Antennas as modeled by NEC are driven by a source (or more than one) which applies a voltage or current to the antenna, varying sinusoidally in time. Consequently, the currents in the antenna wires, the charges on the wires, and also the electric and magnetic field in the surrounding space, vary sinusoidally in time too, at the same frequency as the driving force, but possibly with a different phase. The display of the currents as described in the previous section represents these time-varying currents by their amplitude (thickness in the picture) and phase w.r.t. the source (colour in the picture). For some purposes, this is not very intuitive. Therefore, xnecview can also show the currents (and charges and field strengths) exactly as they vary in time: an animation. Basically, the process which in reality happens at a frequency of thousands or more cycles per second is slowed down to a frequency of about 1 cycle per second, and at that speed the currents and charges are displayed. The animated display of currents and charges is enabled by selecting 'animation' from the none/struct/+tags/currents/animation menu. Then each segment of each wire is replaced by a short blue line, one end of which is at the center of the wire, while the other end indicates the direction and (relative) magnitude of the current. Furthermore, around each segment a square is drawn. This square represents the charge built up on that segment. The size of the square is proportional to the magnitude of the charge, while the colour shows the sign: cyan for positive charge, magenta for negative. The animated display of the electric and magnetic field near the antenna is chosen by selecting 'near' from the none/slice/frame/near menu. Then at every point for which near field data is found in the NEC output file, three coloured lines (vectors) are drawn. A red one indicates the direction and (relative) magnitude of the electric field, and a green one indicates the direction and (relative) magnitude of the magnetic field. From the electric and magnetic field vectors, the so-called Poynting vector is calculated, and displayed in yellow. This vector can be interpreted as the flow of energy; see a textbook on electromagnetic theory for details. When either or both of the animated displays is selected, an additional set of controls appears at the bottom of the window. The left four of these are sliders to control the scaling of (from left to right) currents, charges, electric and magnetic field strength. To the right of these, an on/off control labelled 'P' is shown, which controls whether or not the Poynting vectors are drawn. The rightmost slider controls the speed of the animation: if your computer is fast enough, the number at the slider is the number of animated cycles per second. By setting this slider to 0, or hitting the 'z' key, the animation can be frozen. Then the phase can be changed back and forth by typing '<' and '>' on the keyboard. Obviously, xnecview can only show currents, charges and near fields if such information is available in the NEC output file being visualized. As discussed earlier in this manual, the inclusion of currents is controlled by the PT card in the NEC input. The inclusion of charge information is controlled by the PQ card, and the calculation of near electric and magnetic fields is controlled by NE and NH cards, respectively. Examples are: .nf PQ 0, 0 NE 0, 1,20,20, 0,0.05,0.05, 0,0.05,0.05 NH 0, 1,20,20, 0,0.05,0.05, 0,0.05,0.05 .fi These instruct NEC to include the charge information, and to calculate the near fields at 20 x 20 points in a grid with stepsize 0.05, in the Y-Z-plane. For more information see NEC documentation. .SH COMMAND-LINE OPTIONS In normal usage of xnecview, command-line options (other than the names of the files to be displayed) are rarely needed. However, they can be useful to bring xnecview quickly in the desired state, or to use xnecview for non-interactive, automated generation of plots. Command-line options can not only be given on the command line with which xnecview is started, but they can also be embedded as a CM card (line) in the NEC input file to be read. In order for the content of a CM card to be recognized as xnecview options, the CM card should contain the word xnecview: (including the colon) before those options. The following options are available: .TP .B -h, --help show usage information .TP .B --struct set structure view to 'struct' .TP .B --tags set structure view to 'struct+tags' .TP .B --currents set structure view to 'currents' .TP .B --animation set structure view to 'animation' .TP .B --slice set radiation view to 'slice' .TP .B --frame set radiation view to 'frame' .TP .B --opaque set radiation view to 'opaque' .TP .B --near set radiation view to 'near field' .TP .B --linpower set radiation scale linear in power .TP .B --linvoltage set radiation scale linear in voltage .TP .B --arrl set radiation scale to ARRL style .TP .B --log set radiation scale to logarithmic .TP .B --pol=x choose polarization; x may be total, hor, vert, lhcp, rhcp or colour . .TP .B --qscale num set charges scale (animation) .TP .B --iscale num set currents scale (animation) .TP .B --escale num set electric field scale .TP .B --hscale num set magnetic field scale .TP .B --hidepoynting hide Poynting vector in near field display .TP .B --afreq num set animation frequency (Hz) .TP .B --aphase num set animation phase (degrees) .TP .B --aupdate num set animation update interval (milliseconds). Default is 100, but on a slow computer and/or with a large data set it may be useful to set the update interval higher. Conversely, on a fast computer and with a simple data set, a smaller setting provides smoother movement. .TP .B --freq num set frequency (MHz) .TP .B --z0 num set reference impedance (ohm) .TP .B --expeps filename no X11 display, just export picture to .eps-file .TP .B --exppng no X11 display, just export picture to .png-file (only available if linked against the libpng library) .TP .B --view phi,theta,zoom,trx,try set viewing direction and zoom .PP Note: typing 'v' in window 1 writes the current values for all of these settings to the standard output. .SH AUTHOR Pieter-Tjerk de Boer; Internet e-mail: pa3fwm@amsat.org, amateur packet-radio: PA3FWM @ PI8DAZ.#TWE.NLD.EU. xnecview-1.35.orig/xnecview.c0000644000175000017500000003262511005110762015000 0ustar afrb2afrb2/* XNECVIEW - a program for visualizing NEC2 input and output data * * Copyright (C) 1998-2006, Pieter-Tjerk de Boer -- pa3fwm@amsat.org * * Distributed on the conditions of version 2 of the GPL: see the files * README and COPYING, which accompany this source file. * * Main module, which mostly deals with initial setup, (re)reading of * files, and parsing command line options. */ #include #include #include #include #ifndef NO_GETOPT #include #endif #include "xnecview.h" double extr=0; double extr_str=0; int gainscale=0; int gainplot; int structplot; int scaleplot=0; int phasedirlock=0; double phasephi, phasetheta; int polarization=POLnone; int distcor=1; double phaseoffset=0; int maxthickness=10; double animphase=0; double animfreq=1.0; double anim_update_interval=Default_anim_update_interval; double escale=1; double hscale=1; int show_p=1; double qscale=1; double iscale=1; int g_argc; char **g_argv; char *inputfilename; int window1open=0; int window2open=0; char *expeps=NULL; char *exppng=NULL; /*---------- global variables, together containing the antenna structure -------------*/ int numwires=0; int maxwires=1; Wire *wires=NULL; int numsurfaces=0; int maxsurfaces=1; Surface *surfaces; int numexcis=0; int maxexcis=1; Exci *excis=NULL; int numloads=0; int maxloads=1; Load *loads=NULL; int numnetws=0; int maxnetws=1; Netw *netws=NULL; /*---------- global variables, together containing the gain distribution -------------*/ int numneco=0; int maxfreqs=1; NECoutput *neco=NULL; int rp_index=-1; /* index of the entry in neco[] whose radiation pattern is being shown in the 3D plot */ /*------------------------------------------------------------------------------------*/ void info() { puts("Copyright (C) 1998-2003 Pieter-Tjerk de Boer -- pa3fwm@amsat.org\n"); puts("Xnecview comes with ABSOLUTELY NO WARRANTY. This is free software, and you are\n" "welcome to redistribute it under the conditions of version 2 of the GNU GPL.\n" "For more information: see the files README and COPYING which should accompany\n" "this software, or ask the author.\n"); } void usage() { puts("Usage: xnecview [options] filenames [options]\n" "filenames : names of NEC2 input and/or output files to be displayed\n" "options:\n" " -h or --help : show this information\n" #ifndef NO_GETOPT " --struct : set structure view to 'struct'\n" " --tags : set structure view to 'struct+tags'\n" " --currents : set structure view to 'currents'\n" " --animation : set structure view to 'animation'\n" " --slice : set radiation view to 'slice'\n" " --frame : set radiation view to 'frame'\n" " --opaque : set radiation view to 'opaque'\n" " --near : set radiation view to 'near field'\n" " --linpower : set radiation scale linear in power\n" " --linvoltage : set radiation scale linear in voltage\n" " --arrl : set radiation scale to ARRL style\n" " --log : set radiation scale to logarithmic\n" " --qscale num : set charges scale (animation)\n" " --iscale num : set currents scale (animation)\n" " --escale num : set electric field scale\n" " --hscale num : set magnetic field scale\n" " --hidepoynting : hide Poynting vector in near field display\n" " --afreq num : set animation frequency (Hz)\n" " --aphase num : set animation phase (degrees)\n" " --aupdate num : set time between animation updates (milliseconds, default 100)\n" " --freq num : set frequency (MHz)\n" " --z0 num : set reference impedance (ohm)\n" " --expeps filename : only export picture to .eps-file\n" #ifdef HAVE_LIBPNG " --exppng filename : only export picture to .png-file\n" #endif " --view phi,theta,zoom,trx,try : set viewing direction and zoom\n" "Note: typing 'v' prints the current values for all of these settings,\n" "for easy copying into scripts.\n" #endif ); exit(1); } void readfile(char *s) { FILE *f; f=fopen(s,"r"); if (!f) { fprintf(stderr,"Can't open \"%s\" for reading\n",s); usage(); } if (read_nec_output(f)==1) { /* try interpreting as output data first */ rewind(f); read_nec_input(f); /* if data is not output data, try interpreting it as input data */ } } void reread(void) { int i; int old_rp_index; numwires=0; numsurfaces=0; numexcis=0; numloads=0; extr=0; globalmaxdb=-1e30; while (numneco>0) { /* delete all old output data and free the associated Radpattern arrays */ Radpattern *rp,*rp1; Nearfield *nf,*nf1; numneco--; rp=neco[numneco].rp; while (rp) { for (i=0;inumphi;i++) { free(rp->gain[i]); free(rp->gpo[i]); } free(rp->gain); free(rp->gpo); free(rp->gphi); free(rp->gtheta); free(rp->sin_gphi); free(rp->cos_gphi); free(rp->sin_gtheta); free(rp->cos_gtheta); rp1=rp; rp=rp->next; free(rp1); } if (neco[numneco].cu) { free(neco[numneco].cu->s); free(neco[numneco].cu); } nf=neco[numneco].nf; while (nf) { nf1=nf; nf=nf->next; free(nf1); } } old_rp_index=rp_index; rp_index=-1; for (i=0;i=0) { if (old_rp_index>=0 && old_rp_index