├── .gitignore ├── COPYING ├── COPYING-HEADER ├── README.md ├── add-license ├── examples ├── coax.pl ├── dipole-2m.pl ├── helix.pl ├── yagi-after-xnec2c-optimize.nec ├── yagi-after-xnec2c-optimize.png ├── yagi-before-xnec2c-optimize.nec ├── yagi-before-xnec2c-optimize.png ├── yagi-optimized.nec └── yagi.conf ├── lib ├── NEC2.pm └── NEC2 │ ├── Antenna │ └── Yagi.pm │ ├── Card.pm │ ├── Card │ ├── CM.pm │ ├── EN.pm │ ├── EX.pm │ ├── FR.pm │ ├── GA.pm │ ├── GC.pm │ ├── GE.pm │ ├── GH.pm │ ├── GM.pm │ ├── GN.pm │ ├── GR.pm │ ├── GS.pm │ ├── GW.pm │ ├── GX.pm │ ├── LD.pm │ ├── NE.pm │ ├── NT.pm │ ├── RP.pm │ ├── SC.pm │ ├── SM.pm │ ├── SP.pm │ ├── TL.pm │ └── ZO.pm │ ├── Coax.pm │ ├── Polyline.pm │ ├── Shape.pm │ ├── Word.pm │ └── xnec2c │ └── optimize.pm └── xnec2c-simplex.pl /.gitignore: -------------------------------------------------------------------------------- 1 | /*.nec 2 | /examples/yagi.nec 3 | /examples/*.save 4 | *.csv 5 | *.sw? 6 | -------------------------------------------------------------------------------- /COPYING: -------------------------------------------------------------------------------- 1 | GNU GENERAL PUBLIC LICENSE 2 | Version 3, 29 June 2007 3 | 4 | Copyright (C) 2007 Free Software Foundation, Inc. 5 | Everyone is permitted to copy and distribute verbatim copies 6 | of this license document, but changing it is not allowed. 7 | 8 | Preamble 9 | 10 | The GNU General Public License is a free, copyleft license for 11 | software and other kinds of works. 12 | 13 | The licenses for most software and other practical works are designed 14 | to take away your freedom to share and change the works. By contrast, 15 | the GNU General Public License is intended to guarantee your freedom to 16 | share and change all versions of a program--to make sure it remains free 17 | software for all its users. We, the Free Software Foundation, use the 18 | GNU General Public License for most of our software; it applies also to 19 | any other work released this way by its authors. You can apply it to 20 | your programs, too. 21 | 22 | When we speak of free software, we are referring to freedom, not 23 | price. Our General Public Licenses are designed to make sure that you 24 | have the freedom to distribute copies of free software (and charge for 25 | them if you wish), that you receive source code or can get it if you 26 | want it, that you can change the software or use pieces of it in new 27 | free programs, and that you know you can do these things. 28 | 29 | To protect your rights, we need to prevent others from denying you 30 | these rights or asking you to surrender the rights. Therefore, you have 31 | certain responsibilities if you distribute copies of the software, or if 32 | you modify it: responsibilities to respect the freedom of others. 33 | 34 | For example, if you distribute copies of such a program, whether 35 | gratis or for a fee, you must pass on to the recipients the same 36 | freedoms that you received. You must make sure that they, too, receive 37 | or can get the source code. And you must show them these terms so they 38 | know their rights. 39 | 40 | Developers that use the GNU GPL protect your rights with two steps: 41 | (1) assert copyright on the software, and (2) offer you this License 42 | giving you legal permission to copy, distribute and/or modify it. 43 | 44 | For the developers' and authors' protection, the GPL clearly explains 45 | that there is no warranty for this free software. For both users' and 46 | authors' sake, the GPL requires that modified versions be marked as 47 | changed, so that their problems will not be attributed erroneously to 48 | authors of previous versions. 49 | 50 | Some devices are designed to deny users access to install or run 51 | modified versions of the software inside them, although the manufacturer 52 | can do so. This is fundamentally incompatible with the aim of 53 | protecting users' freedom to change the software. The systematic 54 | pattern of such abuse occurs in the area of products for individuals to 55 | use, which is precisely where it is most unacceptable. Therefore, we 56 | have designed this version of the GPL to prohibit the practice for those 57 | products. If such problems arise substantially in other domains, we 58 | stand ready to extend this provision to those domains in future versions 59 | of the GPL, as needed to protect the freedom of users. 60 | 61 | Finally, every program is threatened constantly by software patents. 62 | States should not allow patents to restrict development and use of 63 | software on general-purpose computers, but in those that do, we wish to 64 | avoid the special danger that patents applied to a free program could 65 | make it effectively proprietary. To prevent this, the GPL assures that 66 | patents cannot be used to render the program non-free. 67 | 68 | The precise terms and conditions for copying, distribution and 69 | modification follow. 70 | 71 | TERMS AND CONDITIONS 72 | 73 | 0. Definitions. 74 | 75 | "This License" refers to version 3 of the GNU General Public License. 76 | 77 | "Copyright" also means copyright-like laws that apply to other kinds of 78 | works, such as semiconductor masks. 79 | 80 | "The Program" refers to any copyrightable work licensed under this 81 | License. Each licensee is addressed as "you". "Licensees" and 82 | "recipients" may be individuals or organizations. 83 | 84 | To "modify" a work means to copy from or adapt all or part of the work 85 | in a fashion requiring copyright permission, other than the making of an 86 | exact copy. The resulting work is called a "modified version" of the 87 | earlier work or a work "based on" the earlier work. 88 | 89 | A "covered work" means either the unmodified Program or a work based 90 | on the Program. 91 | 92 | To "propagate" a work means to do anything with it that, without 93 | permission, would make you directly or secondarily liable for 94 | infringement under applicable copyright law, except executing it on a 95 | computer or modifying a private copy. Propagation includes copying, 96 | distribution (with or without modification), making available to the 97 | public, and in some countries other activities as well. 98 | 99 | To "convey" a work means any kind of propagation that enables other 100 | parties to make or receive copies. Mere interaction with a user through 101 | a computer network, with no transfer of a copy, is not conveying. 102 | 103 | An interactive user interface displays "Appropriate Legal Notices" 104 | to the extent that it includes a convenient and prominently visible 105 | feature that (1) displays an appropriate copyright notice, and (2) 106 | tells the user that there is no warranty for the work (except to the 107 | extent that warranties are provided), that licensees may convey the 108 | work under this License, and how to view a copy of this License. If 109 | the interface presents a list of user commands or options, such as a 110 | menu, a prominent item in the list meets this criterion. 111 | 112 | 1. Source Code. 113 | 114 | The "source code" for a work means the preferred form of the work 115 | for making modifications to it. "Object code" means any non-source 116 | form of a work. 117 | 118 | A "Standard Interface" means an interface that either is an official 119 | standard defined by a recognized standards body, or, in the case of 120 | interfaces specified for a particular programming language, one that 121 | is widely used among developers working in that language. 122 | 123 | The "System Libraries" of an executable work include anything, other 124 | than the work as a whole, that (a) is included in the normal form of 125 | packaging a Major Component, but which is not part of that Major 126 | Component, and (b) serves only to enable use of the work with that 127 | Major Component, or to implement a Standard Interface for which an 128 | implementation is available to the public in source code form. A 129 | "Major Component", in this context, means a major essential component 130 | (kernel, window system, and so on) of the specific operating system 131 | (if any) on which the executable work runs, or a compiler used to 132 | produce the work, or an object code interpreter used to run it. 133 | 134 | The "Corresponding Source" for a work in object code form means all 135 | the source code needed to generate, install, and (for an executable 136 | work) run the object code and to modify the work, including scripts to 137 | control those activities. However, it does not include the work's 138 | System Libraries, or general-purpose tools or generally available free 139 | programs which are used unmodified in performing those activities but 140 | which are not part of the work. For example, Corresponding Source 141 | includes interface definition files associated with source files for 142 | the work, and the source code for shared libraries and dynamically 143 | linked subprograms that the work is specifically designed to require, 144 | such as by intimate data communication or control flow between those 145 | subprograms and other parts of the work. 146 | 147 | The Corresponding Source need not include anything that users 148 | can regenerate automatically from other parts of the Corresponding 149 | Source. 150 | 151 | The Corresponding Source for a work in source code form is that 152 | same work. 153 | 154 | 2. Basic Permissions. 155 | 156 | All rights granted under this License are granted for the term of 157 | copyright on the Program, and are irrevocable provided the stated 158 | conditions are met. This License explicitly affirms your unlimited 159 | permission to run the unmodified Program. The output from running a 160 | covered work is covered by this License only if the output, given its 161 | content, constitutes a covered work. This License acknowledges your 162 | rights of fair use or other equivalent, as provided by copyright law. 163 | 164 | You may make, run and propagate covered works that you do not 165 | convey, without conditions so long as your license otherwise remains 166 | in force. You may convey covered works to others for the sole purpose 167 | of having them make modifications exclusively for you, or provide you 168 | with facilities for running those works, provided that you comply with 169 | the terms of this License in conveying all material for which you do 170 | not control copyright. Those thus making or running the covered works 171 | for you must do so exclusively on your behalf, under your direction 172 | and control, on terms that prohibit them from making any copies of 173 | your copyrighted material outside their relationship with you. 174 | 175 | Conveying under any other circumstances is permitted solely under 176 | the conditions stated below. Sublicensing is not allowed; section 10 177 | makes it unnecessary. 178 | 179 | 3. Protecting Users' Legal Rights From Anti-Circumvention Law. 180 | 181 | No covered work shall be deemed part of an effective technological 182 | measure under any applicable law fulfilling obligations under article 183 | 11 of the WIPO copyright treaty adopted on 20 December 1996, or 184 | similar laws prohibiting or restricting circumvention of such 185 | measures. 186 | 187 | When you convey a covered work, you waive any legal power to forbid 188 | circumvention of technological measures to the extent such circumvention 189 | is effected by exercising rights under this License with respect to 190 | the covered work, and you disclaim any intention to limit operation or 191 | modification of the work as a means of enforcing, against the work's 192 | users, your or third parties' legal rights to forbid circumvention of 193 | technological measures. 194 | 195 | 4. Conveying Verbatim Copies. 196 | 197 | You may convey verbatim copies of the Program's source code as you 198 | receive it, in any medium, provided that you conspicuously and 199 | appropriately publish on each copy an appropriate copyright notice; 200 | keep intact all notices stating that this License and any 201 | non-permissive terms added in accord with section 7 apply to the code; 202 | keep intact all notices of the absence of any warranty; and give all 203 | recipients a copy of this License along with the Program. 204 | 205 | You may charge any price or no price for each copy that you convey, 206 | and you may offer support or warranty protection for a fee. 207 | 208 | 5. Conveying Modified Source Versions. 209 | 210 | You may convey a work based on the Program, or the modifications to 211 | produce it from the Program, in the form of source code under the 212 | terms of section 4, provided that you also meet all of these conditions: 213 | 214 | a) The work must carry prominent notices stating that you modified 215 | it, and giving a relevant date. 216 | 217 | b) The work must carry prominent notices stating that it is 218 | released under this License and any conditions added under section 219 | 7. This requirement modifies the requirement in section 4 to 220 | "keep intact all notices". 221 | 222 | c) You must license the entire work, as a whole, under this 223 | License to anyone who comes into possession of a copy. This 224 | License will therefore apply, along with any applicable section 7 225 | additional terms, to the whole of the work, and all its parts, 226 | regardless of how they are packaged. This License gives no 227 | permission to license the work in any other way, but it does not 228 | invalidate such permission if you have separately received it. 229 | 230 | d) If the work has interactive user interfaces, each must display 231 | Appropriate Legal Notices; however, if the Program has interactive 232 | interfaces that do not display Appropriate Legal Notices, your 233 | work need not make them do so. 234 | 235 | A compilation of a covered work with other separate and independent 236 | works, which are not by their nature extensions of the covered work, 237 | and which are not combined with it such as to form a larger program, 238 | in or on a volume of a storage or distribution medium, is called an 239 | "aggregate" if the compilation and its resulting copyright are not 240 | used to limit the access or legal rights of the compilation's users 241 | beyond what the individual works permit. Inclusion of a covered work 242 | in an aggregate does not cause this License to apply to the other 243 | parts of the aggregate. 244 | 245 | 6. Conveying Non-Source Forms. 246 | 247 | You may convey a covered work in object code form under the terms 248 | of sections 4 and 5, provided that you also convey the 249 | machine-readable Corresponding Source under the terms of this License, 250 | in one of these ways: 251 | 252 | a) Convey the object code in, or embodied in, a physical product 253 | (including a physical distribution medium), accompanied by the 254 | Corresponding Source fixed on a durable physical medium 255 | customarily used for software interchange. 256 | 257 | b) Convey the object code in, or embodied in, a physical product 258 | (including a physical distribution medium), accompanied by a 259 | written offer, valid for at least three years and valid for as 260 | long as you offer spare parts or customer support for that product 261 | model, to give anyone who possesses the object code either (1) a 262 | copy of the Corresponding Source for all the software in the 263 | product that is covered by this License, on a durable physical 264 | medium customarily used for software interchange, for a price no 265 | more than your reasonable cost of physically performing this 266 | conveying of source, or (2) access to copy the 267 | Corresponding Source from a network server at no charge. 268 | 269 | c) Convey individual copies of the object code with a copy of the 270 | written offer to provide the Corresponding Source. This 271 | alternative is allowed only occasionally and noncommercially, and 272 | only if you received the object code with such an offer, in accord 273 | with subsection 6b. 274 | 275 | d) Convey the object code by offering access from a designated 276 | place (gratis or for a charge), and offer equivalent access to the 277 | Corresponding Source in the same way through the same place at no 278 | further charge. You need not require recipients to copy the 279 | Corresponding Source along with the object code. If the place to 280 | copy the object code is a network server, the Corresponding Source 281 | may be on a different server (operated by you or a third party) 282 | that supports equivalent copying facilities, provided you maintain 283 | clear directions next to the object code saying where to find the 284 | Corresponding Source. Regardless of what server hosts the 285 | Corresponding Source, you remain obligated to ensure that it is 286 | available for as long as needed to satisfy these requirements. 287 | 288 | e) Convey the object code using peer-to-peer transmission, provided 289 | you inform other peers where the object code and Corresponding 290 | Source of the work are being offered to the general public at no 291 | charge under subsection 6d. 292 | 293 | A separable portion of the object code, whose source code is excluded 294 | from the Corresponding Source as a System Library, need not be 295 | included in conveying the object code work. 296 | 297 | A "User Product" is either (1) a "consumer product", which means any 298 | tangible personal property which is normally used for personal, family, 299 | or household purposes, or (2) anything designed or sold for incorporation 300 | into a dwelling. In determining whether a product is a consumer product, 301 | doubtful cases shall be resolved in favor of coverage. For a particular 302 | product received by a particular user, "normally used" refers to a 303 | typical or common use of that class of product, regardless of the status 304 | of the particular user or of the way in which the particular user 305 | actually uses, or expects or is expected to use, the product. A product 306 | is a consumer product regardless of whether the product has substantial 307 | commercial, industrial or non-consumer uses, unless such uses represent 308 | the only significant mode of use of the product. 309 | 310 | "Installation Information" for a User Product means any methods, 311 | procedures, authorization keys, or other information required to install 312 | and execute modified versions of a covered work in that User Product from 313 | a modified version of its Corresponding Source. The information must 314 | suffice to ensure that the continued functioning of the modified object 315 | code is in no case prevented or interfered with solely because 316 | modification has been made. 317 | 318 | If you convey an object code work under this section in, or with, or 319 | specifically for use in, a User Product, and the conveying occurs as 320 | part of a transaction in which the right of possession and use of the 321 | User Product is transferred to the recipient in perpetuity or for a 322 | fixed term (regardless of how the transaction is characterized), the 323 | Corresponding Source conveyed under this section must be accompanied 324 | by the Installation Information. But this requirement does not apply 325 | if neither you nor any third party retains the ability to install 326 | modified object code on the User Product (for example, the work has 327 | been installed in ROM). 328 | 329 | The requirement to provide Installation Information does not include a 330 | requirement to continue to provide support service, warranty, or updates 331 | for a work that has been modified or installed by the recipient, or for 332 | the User Product in which it has been modified or installed. Access to a 333 | network may be denied when the modification itself materially and 334 | adversely affects the operation of the network or violates the rules and 335 | protocols for communication across the network. 336 | 337 | Corresponding Source conveyed, and Installation Information provided, 338 | in accord with this section must be in a format that is publicly 339 | documented (and with an implementation available to the public in 340 | source code form), and must require no special password or key for 341 | unpacking, reading or copying. 342 | 343 | 7. Additional Terms. 344 | 345 | "Additional permissions" are terms that supplement the terms of this 346 | License by making exceptions from one or more of its conditions. 347 | Additional permissions that are applicable to the entire Program shall 348 | be treated as though they were included in this License, to the extent 349 | that they are valid under applicable law. If additional permissions 350 | apply only to part of the Program, that part may be used separately 351 | under those permissions, but the entire Program remains governed by 352 | this License without regard to the additional permissions. 353 | 354 | When you convey a copy of a covered work, you may at your option 355 | remove any additional permissions from that copy, or from any part of 356 | it. (Additional permissions may be written to require their own 357 | removal in certain cases when you modify the work.) You may place 358 | additional permissions on material, added by you to a covered work, 359 | for which you have or can give appropriate copyright permission. 360 | 361 | Notwithstanding any other provision of this License, for material you 362 | add to a covered work, you may (if authorized by the copyright holders of 363 | that material) supplement the terms of this License with terms: 364 | 365 | a) Disclaiming warranty or limiting liability differently from the 366 | terms of sections 15 and 16 of this License; or 367 | 368 | b) Requiring preservation of specified reasonable legal notices or 369 | author attributions in that material or in the Appropriate Legal 370 | Notices displayed by works containing it; or 371 | 372 | c) Prohibiting misrepresentation of the origin of that material, or 373 | requiring that modified versions of such material be marked in 374 | reasonable ways as different from the original version; or 375 | 376 | d) Limiting the use for publicity purposes of names of licensors or 377 | authors of the material; or 378 | 379 | e) Declining to grant rights under trademark law for use of some 380 | trade names, trademarks, or service marks; or 381 | 382 | f) Requiring indemnification of licensors and authors of that 383 | material by anyone who conveys the material (or modified versions of 384 | it) with contractual assumptions of liability to the recipient, for 385 | any liability that these contractual assumptions directly impose on 386 | those licensors and authors. 387 | 388 | All other non-permissive additional terms are considered "further 389 | restrictions" within the meaning of section 10. If the Program as you 390 | received it, or any part of it, contains a notice stating that it is 391 | governed by this License along with a term that is a further 392 | restriction, you may remove that term. If a license document contains 393 | a further restriction but permits relicensing or conveying under this 394 | License, you may add to a covered work material governed by the terms 395 | of that license document, provided that the further restriction does 396 | not survive such relicensing or conveying. 397 | 398 | If you add terms to a covered work in accord with this section, you 399 | must place, in the relevant source files, a statement of the 400 | additional terms that apply to those files, or a notice indicating 401 | where to find the applicable terms. 402 | 403 | Additional terms, permissive or non-permissive, may be stated in the 404 | form of a separately written license, or stated as exceptions; 405 | the above requirements apply either way. 406 | 407 | 8. Termination. 408 | 409 | You may not propagate or modify a covered work except as expressly 410 | provided under this License. Any attempt otherwise to propagate or 411 | modify it is void, and will automatically terminate your rights under 412 | this License (including any patent licenses granted under the third 413 | paragraph of section 11). 414 | 415 | However, if you cease all violation of this License, then your 416 | license from a particular copyright holder is reinstated (a) 417 | provisionally, unless and until the copyright holder explicitly and 418 | finally terminates your license, and (b) permanently, if the copyright 419 | holder fails to notify you of the violation by some reasonable means 420 | prior to 60 days after the cessation. 421 | 422 | Moreover, your license from a particular copyright holder is 423 | reinstated permanently if the copyright holder notifies you of the 424 | violation by some reasonable means, this is the first time you have 425 | received notice of violation of this License (for any work) from that 426 | copyright holder, and you cure the violation prior to 30 days after 427 | your receipt of the notice. 428 | 429 | Termination of your rights under this section does not terminate the 430 | licenses of parties who have received copies or rights from you under 431 | this License. If your rights have been terminated and not permanently 432 | reinstated, you do not qualify to receive new licenses for the same 433 | material under section 10. 434 | 435 | 9. Acceptance Not Required for Having Copies. 436 | 437 | You are not required to accept this License in order to receive or 438 | run a copy of the Program. Ancillary propagation of a covered work 439 | occurring solely as a consequence of using peer-to-peer transmission 440 | to receive a copy likewise does not require acceptance. However, 441 | nothing other than this License grants you permission to propagate or 442 | modify any covered work. These actions infringe copyright if you do 443 | not accept this License. Therefore, by modifying or propagating a 444 | covered work, you indicate your acceptance of this License to do so. 445 | 446 | 10. Automatic Licensing of Downstream Recipients. 447 | 448 | Each time you convey a covered work, the recipient automatically 449 | receives a license from the original licensors, to run, modify and 450 | propagate that work, subject to this License. You are not responsible 451 | for enforcing compliance by third parties with this License. 452 | 453 | An "entity transaction" is a transaction transferring control of an 454 | organization, or substantially all assets of one, or subdividing an 455 | organization, or merging organizations. If propagation of a covered 456 | work results from an entity transaction, each party to that 457 | transaction who receives a copy of the work also receives whatever 458 | licenses to the work the party's predecessor in interest had or could 459 | give under the previous paragraph, plus a right to possession of the 460 | Corresponding Source of the work from the predecessor in interest, if 461 | the predecessor has it or can get it with reasonable efforts. 462 | 463 | You may not impose any further restrictions on the exercise of the 464 | rights granted or affirmed under this License. For example, you may 465 | not impose a license fee, royalty, or other charge for exercise of 466 | rights granted under this License, and you may not initiate litigation 467 | (including a cross-claim or counterclaim in a lawsuit) alleging that 468 | any patent claim is infringed by making, using, selling, offering for 469 | sale, or importing the Program or any portion of it. 470 | 471 | 11. Patents. 472 | 473 | A "contributor" is a copyright holder who authorizes use under this 474 | License of the Program or a work on which the Program is based. The 475 | work thus licensed is called the contributor's "contributor version". 476 | 477 | A contributor's "essential patent claims" are all patent claims 478 | owned or controlled by the contributor, whether already acquired or 479 | hereafter acquired, that would be infringed by some manner, permitted 480 | by this License, of making, using, or selling its contributor version, 481 | but do not include claims that would be infringed only as a 482 | consequence of further modification of the contributor version. For 483 | purposes of this definition, "control" includes the right to grant 484 | patent sublicenses in a manner consistent with the requirements of 485 | this License. 486 | 487 | Each contributor grants you a non-exclusive, worldwide, royalty-free 488 | patent license under the contributor's essential patent claims, to 489 | make, use, sell, offer for sale, import and otherwise run, modify and 490 | propagate the contents of its contributor version. 491 | 492 | In the following three paragraphs, a "patent license" is any express 493 | agreement or commitment, however denominated, not to enforce a patent 494 | (such as an express permission to practice a patent or covenant not to 495 | sue for patent infringement). To "grant" such a patent license to a 496 | party means to make such an agreement or commitment not to enforce a 497 | patent against the party. 498 | 499 | If you convey a covered work, knowingly relying on a patent license, 500 | and the Corresponding Source of the work is not available for anyone 501 | to copy, free of charge and under the terms of this License, through a 502 | publicly available network server or other readily accessible means, 503 | then you must either (1) cause the Corresponding Source to be so 504 | available, or (2) arrange to deprive yourself of the benefit of the 505 | patent license for this particular work, or (3) arrange, in a manner 506 | consistent with the requirements of this License, to extend the patent 507 | license to downstream recipients. "Knowingly relying" means you have 508 | actual knowledge that, but for the patent license, your conveying the 509 | covered work in a country, or your recipient's use of the covered work 510 | in a country, would infringe one or more identifiable patents in that 511 | country that you have reason to believe are valid. 512 | 513 | If, pursuant to or in connection with a single transaction or 514 | arrangement, you convey, or propagate by procuring conveyance of, a 515 | covered work, and grant a patent license to some of the parties 516 | receiving the covered work authorizing them to use, propagate, modify 517 | or convey a specific copy of the covered work, then the patent license 518 | you grant is automatically extended to all recipients of the covered 519 | work and works based on it. 520 | 521 | A patent license is "discriminatory" if it does not include within 522 | the scope of its coverage, prohibits the exercise of, or is 523 | conditioned on the non-exercise of one or more of the rights that are 524 | specifically granted under this License. You may not convey a covered 525 | work if you are a party to an arrangement with a third party that is 526 | in the business of distributing software, under which you make payment 527 | to the third party based on the extent of your activity of conveying 528 | the work, and under which the third party grants, to any of the 529 | parties who would receive the covered work from you, a discriminatory 530 | patent license (a) in connection with copies of the covered work 531 | conveyed by you (or copies made from those copies), or (b) primarily 532 | for and in connection with specific products or compilations that 533 | contain the covered work, unless you entered into that arrangement, 534 | or that patent license was granted, prior to 28 March 2007. 535 | 536 | Nothing in this License shall be construed as excluding or limiting 537 | any implied license or other defenses to infringement that may 538 | otherwise be available to you under applicable patent law. 539 | 540 | 12. No Surrender of Others' Freedom. 541 | 542 | If conditions are imposed on you (whether by court order, agreement or 543 | otherwise) that contradict the conditions of this License, they do not 544 | excuse you from the conditions of this License. If you cannot convey a 545 | covered work so as to satisfy simultaneously your obligations under this 546 | License and any other pertinent obligations, then as a consequence you may 547 | not convey it at all. For example, if you agree to terms that obligate you 548 | to collect a royalty for further conveying from those to whom you convey 549 | the Program, the only way you could satisfy both those terms and this 550 | License would be to refrain entirely from conveying the Program. 551 | 552 | 13. Use with the GNU Affero General Public License. 553 | 554 | Notwithstanding any other provision of this License, you have 555 | permission to link or combine any covered work with a work licensed 556 | under version 3 of the GNU Affero General Public License into a single 557 | combined work, and to convey the resulting work. The terms of this 558 | License will continue to apply to the part which is the covered work, 559 | but the special requirements of the GNU Affero General Public License, 560 | section 13, concerning interaction through a network will apply to the 561 | combination as such. 562 | 563 | 14. Revised Versions of this License. 564 | 565 | The Free Software Foundation may publish revised and/or new versions of 566 | the GNU General Public License from time to time. Such new versions will 567 | be similar in spirit to the present version, but may differ in detail to 568 | address new problems or concerns. 569 | 570 | Each version is given a distinguishing version number. If the 571 | Program specifies that a certain numbered version of the GNU General 572 | Public License "or any later version" applies to it, you have the 573 | option of following the terms and conditions either of that numbered 574 | version or of any later version published by the Free Software 575 | Foundation. If the Program does not specify a version number of the 576 | GNU General Public License, you may choose any version ever published 577 | by the Free Software Foundation. 578 | 579 | If the Program specifies that a proxy can decide which future 580 | versions of the GNU General Public License can be used, that proxy's 581 | public statement of acceptance of a version permanently authorizes you 582 | to choose that version for the Program. 583 | 584 | Later license versions may give you additional or different 585 | permissions. However, no additional obligations are imposed on any 586 | author or copyright holder as a result of your choosing to follow a 587 | later version. 588 | 589 | 15. Disclaimer of Warranty. 590 | 591 | THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY 592 | APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT 593 | HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY 594 | OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, 595 | THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 596 | PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM 597 | IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF 598 | ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 599 | 600 | 16. Limitation of Liability. 601 | 602 | IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING 603 | WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS 604 | THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY 605 | GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE 606 | USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF 607 | DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD 608 | PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), 609 | EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF 610 | SUCH DAMAGES. 611 | 612 | 17. Interpretation of Sections 15 and 16. 613 | 614 | If the disclaimer of warranty and limitation of liability provided 615 | above cannot be given local legal effect according to their terms, 616 | reviewing courts shall apply local law that most closely approximates 617 | an absolute waiver of all civil liability in connection with the 618 | Program, unless a warranty or assumption of liability accompanies a 619 | copy of the Program in return for a fee. 620 | 621 | END OF TERMS AND CONDITIONS 622 | 623 | How to Apply These Terms to Your New Programs 624 | 625 | If you develop a new program, and you want it to be of the greatest 626 | possible use to the public, the best way to achieve this is to make it 627 | free software which everyone can redistribute and change under these terms. 628 | 629 | To do so, attach the following notices to the program. It is safest 630 | to attach them to the start of each source file to most effectively 631 | state the exclusion of warranty; and each file should have at least 632 | the "copyright" line and a pointer to where the full notice is found. 633 | 634 | 635 | Copyright (C) 636 | 637 | This program is free software: you can redistribute it and/or modify 638 | it under the terms of the GNU General Public License as published by 639 | the Free Software Foundation, either version 3 of the License, or 640 | (at your option) any later version. 641 | 642 | This program is distributed in the hope that it will be useful, 643 | but WITHOUT ANY WARRANTY; without even the implied warranty of 644 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 645 | GNU General Public License for more details. 646 | 647 | You should have received a copy of the GNU General Public License 648 | along with this program. If not, see . 649 | 650 | Also add information on how to contact you by electronic and paper mail. 651 | 652 | If the program does terminal interaction, make it output a short 653 | notice like this when it starts in an interactive mode: 654 | 655 | Copyright (C) 656 | This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. 657 | This is free software, and you are welcome to redistribute it 658 | under certain conditions; type `show c' for details. 659 | 660 | The hypothetical commands `show w' and `show c' should show the appropriate 661 | parts of the General Public License. Of course, your program's commands 662 | might be different; for a GUI interface, you would use an "about box". 663 | 664 | You should also get your employer (if you work as a programmer) or school, 665 | if any, to sign a "copyright disclaimer" for the program, if necessary. 666 | For more information on this, and how to apply and follow the GNU GPL, see 667 | . 668 | 669 | The GNU General Public License does not permit incorporating your program 670 | into proprietary programs. If your program is a subroutine library, you 671 | may consider it more useful to permit linking proprietary applications with 672 | the library. If this is what you want to do, use the GNU Lesser General 673 | Public License instead of this License. But first, please read 674 | . 675 | -------------------------------------------------------------------------------- /COPYING-HEADER: -------------------------------------------------------------------------------- 1 | # This program is free software; you can redistribute it and/or modify 2 | # it under the terms of the GNU General Public License as published by 3 | # the Free Software Foundation; either version 3 of the License, or 4 | # (at your option) any later version. 5 | # 6 | # This program is distributed in the hope that it will be useful, 7 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 8 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 9 | # GNU Library General Public License for more details. 10 | # 11 | # You should have received a copy of the GNU General Public License 12 | # along with this program; if not, write to the Free Software 13 | # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. 14 | # 15 | # Copyright (C) 2021- by Eric Wheeler, KJ7LNW. All rights reserved. 16 | # 17 | # The official website and doumentation for xnec2c-optimizer is available here: 18 | # https://www.xnec2c.org/ 19 | # 20 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # xnec2c-optimize 2 | 3 | xnec2c-optimize is an optimization framework to tune antenna geometries with 4 | [xnec2c](https://www.xnec2c.org). 5 | 6 | **Why tune our antenna? High gain and low VSWR, of course!** 7 | 8 | 1. [Before and after graphs](#examples) 9 | 2. [Getting Started](#getting-started) 10 | 3. [Writing NEC2 files with Perl](#writing-nec2-files-with-perl) 11 | 12 | # Examples 13 | 14 | ## Optimization 15 | 16 | ### Before: 17 | - 9.3 - 10.9 dB gain. 18 | - Huge 23.31 VSWR at 148 MHz, lets fix that. 19 | ![before xnec2c-optimize](https://github.com/KJ7LNW/xnec2c-optimize/blob/master/examples/yagi-before-xnec2c-optimize.png?raw=true) 20 | 21 | ### After: 22 | - **10.81 - 11.3 dB gain!** 23 | - **1.25 VSWR, hurray!** (Note that the graph scale changed) 24 | ![after xnec2c-optimize](https://github.com/KJ7LNW/xnec2c-optimize/blob/master/examples/yagi-after-xnec2c-optimize.png?raw=true) 25 | 26 | ## Writing NEC2 files with Perl 27 | 28 | From the [2m dipole example](https://github.com/KJ7LNW/xnec2c-optimize/blob/master/examples/dipole-2m.pl): 29 | 30 | ```perl 31 | use lib 'lib'; 32 | 33 | use NEC2; 34 | 35 | my $nec = NEC2->new(comment => "half-wave 2-meter dipole"); 36 | 37 | my $ns = 21; # number of segments 38 | 39 | $nec->add( 40 | GW( tag => 1, ns => $ns, z2 => 1), 41 | EX( ex_tag => 1, ex_seg => int($ns/2) ), 42 | RP, 43 | NH, 44 | NE, 45 | FR(mhz_min => 140, mhz_max => 148, n_freq => 10), 46 | ); 47 | 48 | $nec->save('dipole-2m.nec'); 49 | 50 | print $nec; 51 | ``` 52 | 53 | # Getting Started 54 | 55 | 1. Install the latest version of xnec2c from here: https://www.xnec2c.org/ 56 | - xnec2c v4.4.12 or later is recommended. 57 | 58 | 2. Install the following perl modules with `cpanm` using these commands: 59 | - Ubuntu: `sudo apt install cpanminus build-essential` 60 | - CentOS: 61 | ``` 62 | yum install perl-App-cpanminus 63 | yum groupinstall "Development tools" 64 | ``` 65 | 66 | 3. Install the dependencies with this command: 67 | 68 | cpanm PDL::Opt::Simplex::Simple PDL::IO::CSV Linux::Inotify2 Time::HiRes Math::Vector::Real Math::Matrix Math::Trig 69 | 70 | 4. See `yagi.conf` for an example to get started. Just run this: 71 | 72 | ./xnec2c-simplex.pl ./examples/yagi.conf 73 | 74 | ## More detail 75 | 76 | Now `xnec2c-simplex` does most of the work below automatically, so here is more information if you need it: 77 | 78 | 79 | Open `xnec2c -j NN examples/yagi.nec` where `NN` is the number of CPUs you 80 | have available on your system. 81 | - From the main window: select View->Frequency Plots 82 | - From the Frequency Data Plots window: Enable a graph, like VSWR. Configure whatever you would like to see during optimization. 83 | - Click the triangular "Play" button to run a frequency sweep. This setting must be active in order for the optimizer loop to function. 84 | - From the main window: 85 | - Click "File->Optimization Settings->After calculation, write \.csv". 86 | - Click "File->Optimization Settings->Reload and write data on .NEC file 87 | changes". 88 | - Optimization will then begin! 89 | 90 | See also: https://www.xnec2c.org/#Optimization 91 | 92 | -Eric, KJ7LNW 93 | -------------------------------------------------------------------------------- /add-license: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | if [ "$1" = "" ] ; then 4 | echo $0 'file1 [file2] ...' 5 | exit 1; 6 | fi 7 | 8 | for i in `find "$@" -type f `;do 9 | if ! grep -q Copyright $i; then 10 | cat COPYING-HEADER $i >$i.new && mv $i.new $i 11 | fi 12 | done 13 | -------------------------------------------------------------------------------- /examples/coax.pl: -------------------------------------------------------------------------------- 1 | #!/usr/bin/perl 2 | 3 | use strict; 4 | use warnings; 5 | use Data::Dumper; 6 | 7 | use lib 'lib'; 8 | 9 | use NEC2; 10 | 11 | my $filename = shift || die "usage: $0 filename.nec\n"; 12 | 13 | $SIG{__DIE__} = sub { print "\nDie: $_[0]" . Dumper _build_stack() ; }; 14 | 15 | 16 | my $nec = NEC2->new(comment => "coax cable"); 17 | 18 | # Coax cable endpoints. Use multiple points to "bend" the cable: 19 | my $points = [ 20 | [0,0,0], 21 | [1,1,1], 22 | 23 | # Try additional points. 24 | # Be careful: sharp corners could short the shield at the inflection 25 | # point: 26 | #[2,2,3], 27 | #[2,2,4] 28 | ]; 29 | 30 | # Coax cables are now implemented by NEC2::Coax. See also lib/NEC2/Coax.pm: 31 | $nec->add( 32 | Coax(tag => 1, 33 | points => $points, 34 | r_inner => 0.001, # inner conductor radius 35 | z0 => 50, # outer radius is calculated for 50-ohms 36 | excite => 1, # place an excitation on the first end of the coax 37 | terminate => 1, # place a 50-ohm termination at the last end of the coax 38 | 39 | # You may need to increase these for higher frequencies: 40 | n_circles => 20, 41 | n_shield => 8, 42 | ns => 20, 43 | ), 44 | RP, 45 | NH, 46 | NE, 47 | FR(mhz_min => 10, mhz_max => 1000, n_freq => 100), 48 | ); 49 | 50 | $nec->save($filename); 51 | 52 | print $nec; 53 | 54 | exit 0; 55 | 56 | 57 | ## functions 58 | 59 | sub _build_stack 60 | { 61 | my $i = 0; 62 | my @msg; 63 | while (my @c = caller($i++)) { 64 | my @c0 = caller($i); 65 | my $caller = ''; 66 | $caller = " ($c0[3])" if (@c0); 67 | push @msg, " $i. $c[1]:$c[2]:$caller while calling $c[3]"; 68 | } 69 | 70 | return \@msg; 71 | } 72 | 73 | -------------------------------------------------------------------------------- /examples/dipole-2m.pl: -------------------------------------------------------------------------------- 1 | #!/usr/bin/perl 2 | 3 | use strict; 4 | use warnings; 5 | use Data::Dumper; 6 | 7 | use lib 'lib'; 8 | 9 | use NEC2; 10 | 11 | $SIG{__DIE__} = sub { print "\nDie: $_[0]" . Dumper _build_stack() ; }; 12 | 13 | my $nec = NEC2->new(comment => "half-wave 2-meter dipole"); 14 | 15 | my $ns = 21; # number of segments 16 | 17 | $nec->add( 18 | GW( tag => 1, ns => $ns, z2 => 1), 19 | EX( ex_tag => 1, ex_seg => int($ns/2) ), 20 | RP, 21 | NH, 22 | NE, 23 | FR(mhz_min => 140, mhz_max => 148, n_freq => 10), 24 | ); 25 | 26 | 27 | $nec->save('dipole-2m.nec'); 28 | 29 | print $nec; 30 | 31 | exit 0; 32 | ## functions 33 | 34 | sub _build_stack 35 | { 36 | my $i = 0; 37 | my @msg; 38 | while (my @c = caller($i++)) { 39 | my @c0 = caller($i); 40 | my $caller = ''; 41 | $caller = " ($c0[3])" if (@c0); 42 | push @msg, " $i. $c[1]:$c[2]:$caller while calling $c[3]"; 43 | } 44 | 45 | return \@msg; 46 | } 47 | -------------------------------------------------------------------------------- /examples/helix.pl: -------------------------------------------------------------------------------- 1 | #!/usr/bin/perl 2 | # This program is free software; you can redistribute it and/or modify 3 | # it under the terms of the GNU General Public License as published by 4 | # the Free Software Foundation; either version 3 of the License, or 5 | # (at your option) any later version. 6 | # 7 | # This program is distributed in the hope that it will be useful, 8 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 9 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 10 | # GNU Library General Public License for more details. 11 | # 12 | # You should have received a copy of the GNU General Public License 13 | # along with this program; if not, write to the Free Software 14 | # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. 15 | # 16 | # Copyright (C) 2021- by Eric Wheeler, KJ7LNW and Zeke Wheeler, KJ7NLL. 17 | # All rights reserved. 18 | 19 | 20 | # This is an example of how to use the Perl code to create NEC2 geometries. 21 | # This is much simpler than "SY" implementations because the different cards 22 | # have variable name pneumonics that make easier to understand. Additionally, 23 | # math can be performed inline to each argument. While this example is not 24 | # intended for optimization, it could be modified has an optimized example. 25 | # 26 | # To run this example, follow the instructions in the comments below to make 27 | # any changes that you would like, and run the script as: 28 | # 29 | # ]$ ./examples/helix.pl helix.nec 30 | # 31 | # You can then open the resulting .NEC file using xnec2c. Using xnec2c 32 | # optimization feature, you can modify the Perl script, and when you run it, 33 | # xnec2c will automatically update when the output file has changed: 34 | # 35 | # ]$ xnec2c --optimize -j `nproc` helix.nec 36 | 37 | use strict; 38 | use warnings; 39 | use Data::Dumper; 40 | 41 | use lib 'lib'; 42 | 43 | use NEC2; 44 | use NEC2::Antenna::Yagi; 45 | 46 | if (!@ARGV) 47 | { 48 | print "usage: $0 outputfile.nec\n"; 49 | exit 1; 50 | } 51 | 52 | my $filename = shift; 53 | 54 | my $nec = NEC2->new(comment => [ 55 | "Dual-band coaxial helical antenna, by KJ7NLL", 56 | "Watch the presentation: https://youtu.be/vKeOOaMB4ZQ", 57 | "Excite helix1 or helix2 by setting EX to tag 91 or 92", 58 | ]); 59 | 60 | ####################################################################### 61 | # Set your excitation frequencies and bandwidth 62 | 63 | my $mhz1 = 146; 64 | my $mhz1_bandwidth = 4; 65 | 66 | my $mhz2 = 435; 67 | my $mhz2_bandwidth = 30; 68 | 69 | ####################################################################### 70 | # Choose which helix to excite. The other helix will be grounded: 71 | 72 | my $ex_tag = 91; # Excite helix1 73 | #my $ex_tag = 92; # Excite helix2 74 | 75 | ####################################################################### 76 | # Adjust the surface size if necessary. It defaults to the wavelength of mhz1 77 | # (divided by 2, because of +/- coordinates in the NEC2 cards below): 78 | my $surface_size_m = (300/$mhz1)/2; 79 | 80 | # Distance from helixes to the ground plane in meters: 81 | my $helix_loft = 0.050; 82 | 83 | $nec->add( 84 | ####################################################################### 85 | # Geometry Cards 86 | 87 | # helix1 88 | GH(tag => 1, ns => 500, s => 0.14, length => 0.14*21, 89 | rx1 => 300/($mhz1*2*3.14149), 90 | ry1 => 300/($mhz1*2*3.14149), 91 | rx2 => 300/($mhz1*2*3.14149), 92 | ry2 => 300/($mhz1*2*3.14149)), 93 | 94 | # helix2 95 | GH(tag => 2, ns => 800, s => 0.14, length => 0.14*21, 96 | rx1 => 300/($mhz2*2*3.14149), 97 | ry1 => 300/($mhz2*2*3.14149), 98 | rx2 => 300/($mhz2*2*3.14149), 99 | ry2 => 300/($mhz2*2*3.14149)), 100 | 101 | GM(tag_start => 1, sz => $helix_loft), # move +z 1cm for both helixes 102 | 103 | # Add a "+" of ground wires at the surface, and a circle around the excitation 104 | # so it connects well to the surface patch below. 105 | GW(tag => 4, x1 => -$surface_size_m, x2 => $surface_size_m), 106 | GW(tag => 4, y1 => -$surface_size_m, y2 => $surface_size_m), 107 | 108 | GA(tag => 5, ns => 100, rada => 300/($mhz1*2*3.14149) ), 109 | GA(tag => 6, ns => 100, rada => 300/($mhz2*2*3.14149) ), 110 | GM(tag_start => 5, rox => 90), # rotate the circles to be flat 111 | 112 | 113 | SM(nx => 8, ny => 8, 114 | x1 => -$surface_size_m, y1 => -$surface_size_m, z1 => 0, 115 | x2 => $surface_size_m, y2 => -$surface_size_m, z2 => 0, 116 | x3 => $surface_size_m, y3 => $surface_size_m, z3 => 0), 117 | 118 | # One of these get excited, so change $ex_tag above to excite one or the other. 119 | # These are always defined for both frequencies so the helix that is not excited 120 | # will be grounded: 121 | GW(tag => 91, ns => 10, x1 => 300/($mhz1*2*3.14149), x2 => 300/($mhz1*2*3.14149), z2 => $helix_loft), 122 | GW(tag => 92, ns => 10, x1 => 300/($mhz2*2*3.14149), x2 => 300/($mhz2*2*3.14149), z2 => $helix_loft), 123 | 124 | ####################################################################### 125 | # Commands Cards 126 | # 127 | GE(ground => 0), 128 | EX(ex_tag => $ex_tag), 129 | RP(ground => 0), 130 | NH, 131 | NE, 132 | #FR(mhz_min => 50, mhz_max => 54, n_freq => 10), 133 | 134 | # Draw the FR plot depending on the excited frequency. These 135 | # lines use Perl's ternary operator to only show the frequency for the excited 136 | # helix: 137 | $ex_tag == 91 138 | ? FR(mhz_min => $mhz1 - $mhz1_bandwidth/2, mhz_max => $mhz1 + $mhz1_bandwidth/2, n_freq => 20) 139 | : FR(mhz_min => $mhz2 - $mhz2_bandwidth/2, mhz_max => $mhz2 + $mhz2_bandwidth/2, n_freq => 20) 140 | 141 | # Xnec2c supports displaying multiple FR cards, so list them both if you wish by commenting 142 | # the 3 lines above and enabling these two: 143 | #FR(mhz_min => $mhz1 - $mhz1_bandwidth/2, mhz_max => $mhz1 + $mhz1_bandwidth/2, n_freq => 20), 144 | #FR(mhz_min => $mhz2 - $mhz2_bandwidth/2, mhz_max => $mhz2 + $mhz2_bandwidth/2, n_freq => 20), 145 | ); 146 | 147 | $nec->save($filename); 148 | 149 | print $nec; 150 | -------------------------------------------------------------------------------- /examples/yagi-after-xnec2c-optimize.nec: -------------------------------------------------------------------------------- 1 | CM Initial dimensions by Martin Steyer, DK7ZB 2 | CM http://www.qsl.net/dk7zb/PVC-Yagis/5-Ele-2m.htm 3 | CE 4 | GW 1 11 0.5125 0 0 -0.5125 0 0 0.002 5 | GW 2 11 0.501 0 0.268 -0.501 0 0.268 0.002 6 | GW 3 11 0.468 0 0.455 -0.468 0 0.455 0.002 7 | GW 4 11 0.463 0 1.061 -0.463 0 1.061 0.002 8 | GW 5 11 0.453 0 1.562 -0.453 0 1.562 0.002 9 | GE 0 0 0 0 0 0 0 0 0 10 | EX 0 2 6 0 1 0 0 0 0 0 11 | RP 0 19 37 1000 0 0 10 10 0 0 12 | NH 0 0 0 0 0 0 0 0 0 0 13 | NE 0 0 0 0 0 0 0 0 0 0 14 | ZO 50 0 0 0 0 0 0 0 0 0 15 | FR 0 100 0 0 144 0.0404040404040404 0 0 0 0 16 | EN 0 0 0 0 0 0 0 0 0 0 17 | -------------------------------------------------------------------------------- /examples/yagi-after-xnec2c-optimize.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/KJ7LNW/xnec2c-optimize/a5d24afd6eaf8863918e37d0c275d9d1cf8edf15/examples/yagi-after-xnec2c-optimize.png -------------------------------------------------------------------------------- /examples/yagi-before-xnec2c-optimize.nec: -------------------------------------------------------------------------------- 1 | CM Initial dimensions by Martin Steyer, DK7ZB 2 | CM http://www.qsl.net/dk7zb/PVC-Yagis/5-Ele-2m.htm 3 | CE 4 | GW 1 11 0.519 0 0 -0.519 0 0 0.002 5 | GW 2 11 0.4975 0 0.28 -0.4975 0 0.28 0.002 6 | GW 3 11 0.4795 0 0.43 -0.4795 0 0.43 0.002 7 | GW 4 11 0.4745 0 0.95 -0.4745 0 0.95 0.002 8 | GW 5 11 0.4675 0 1.48 -0.4675 0 1.48 0.002 9 | GE 0 0 0 0 0 0 0 0 0 10 | EX 0 2 6 0 1 0 0 0 0 0 11 | RP 0 19 37 1000 0 0 10 10 0 0 12 | NH 0 0 0 0 0 0 0 0 0 0 13 | NE 0 0 0 0 0 0 0 0 0 0 14 | ZO 50 0 0 0 0 0 0 0 0 0 15 | FR 0 100 0 0 144 0.0404040404040404 0 0 0 0 16 | EN 0 0 0 0 0 0 0 0 0 0 17 | -------------------------------------------------------------------------------- /examples/yagi-before-xnec2c-optimize.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/KJ7LNW/xnec2c-optimize/a5d24afd6eaf8863918e37d0c275d9d1cf8edf15/examples/yagi-before-xnec2c-optimize.png -------------------------------------------------------------------------------- /examples/yagi-optimized.nec: -------------------------------------------------------------------------------- 1 | CM Initial dimensions by Martin Steyer, DK7ZB 2 | CM http://www.qsl.net/dk7zb/PVC-Yagis/5-Ele-2m.htm 3 | CE 4 | GW 1 11 0.505 0 0 -0.505 0 0 0.002 5 | GW 2 11 0.4875 0 0.515 -0.4875 0 0.515 0.002 6 | GW 3 11 0.463 0 0.873 -0.463 0 0.873 0.002 7 | GW 4 11 0.459 0 1.477 -0.459 0 1.477 0.002 8 | GW 5 11 0.4505 0 2.0355 -0.4505 0 2.0355 0.002 9 | GE 0 0 0 0 0 0 0 0 0 10 | EX 0 2 6 0 1 0 0 0 0 0 11 | RP 0 19 37 1000 0 0 10 10 0 0 12 | NH 0 0 0 0 0 0 0 0 0 0 13 | NE 0 0 0 0 0 0 0 0 0 0 14 | ZO 50 0 0 0 0 0 0 0 0 0 15 | FR 0 100 0 0 144 0.0404040404040404 0 0 0 0 16 | EN 0 0 0 0 0 0 0 0 0 0 17 | -------------------------------------------------------------------------------- /examples/yagi.conf: -------------------------------------------------------------------------------- 1 | #!/usr/bin/perl 2 | # 3 | # This program is free software; you can redistribute it and/or modify 4 | # it under the terms of the GNU General Public License as published by 5 | # the Free Software Foundation; either version 3 of the License, or 6 | # (at your option) any later version. 7 | # 8 | # This program is distributed in the hope that it will be useful, 9 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | # GNU Library General Public License for more details. 12 | # 13 | # You should have received a copy of the GNU General Public License 14 | # along with this program; if not, write to the Free Software 15 | # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. 16 | # 17 | # Copyright (C) 2021- by Eric Wheeler, KJ7LNW. All rights reserved. 18 | # 19 | # The official website and doumentation for xnec2c-optimizer is available here: 20 | # https://www.xnec2c.org/ 21 | # 22 | 23 | 24 | # Performance tips: 25 | # - Make sure you launch xnec2c as `xnec2c -jN` where N is the number of CPU 26 | # cores you have available. 27 | # 28 | # - Modify the number of frequencies in the FR card in the 'nec2' section 29 | # below. Fewer frequencies will optimize faster. Default is 32 frequencies. 30 | # 31 | # - Ideally you want to have one frequency per CPU core because xnec2c 32 | # parallelizes per FR-card frequency. 33 | 34 | # Reduce the goal frequency range by 5MHz and increase the FR card's range so 35 | # we can see more of the graph than only what is being optimized. This variable 36 | # is not a configuration option, it is used below. 37 | my $mhz_shrink = 0; # MHz 38 | 39 | # Keep this line, the whole config is a hash evaluated by do() because 40 | # there are subs in the goal section. 41 | { 42 | 43 | ############################################################################ 44 | # Optimizer Selection: Simplex or Particle Swarm 45 | # - Scroll down for Particle Swarm 46 | 47 | # See PDL::Opt::Simplex::Simple for documentation on the simplex settings: 48 | optimizer => { 49 | # Use the Simplex optimizer: 50 | class => 'PDL::Opt::Simplex::Simple', 51 | 52 | # Additional Documentation: 53 | # https://metacpan.org/pod/PDL::Opt::Simplex::Simple 54 | 55 | # Initial simplex size: bigger values explore a wider variable range: 56 | opts => { 57 | ssize => 0.090, 58 | 59 | # Use multiple ssizes to run multiple simplex optimizations 60 | # at additional refinement: 61 | #ssize => [ 0.090, 0.075, 0.050, 0.025, 0.012 ], 62 | 63 | # Tolerance: end early if simplex isn't moving much: 64 | tolerance => 1e-9, 65 | }, 66 | 67 | # Max iterations: you may get a more refined result by increasing this: 68 | max_iter => 1500, 69 | 70 | # Maximum number of iterations that return a worse minima 71 | # than the previous minima before canceling the current 72 | # iteration due to stagnation. Typically this option 73 | # drives the optimization time because tolerance and 74 | # max_iter are more than they need to be. Setting this too 75 | # low will provide poor results, setting it too high will 76 | # just take longer when it gets stuck. 77 | stagnant_minima_count => 30, 78 | stagnant_minima_tolerance => 1e-6, 79 | 80 | # See documentation, you might want a smaller value than `tolerance` above: 81 | #stagnant_minima_tolerance => 1e-3, 82 | 83 | # Optional: 84 | # Seed the random number generator to make this deterministic. 85 | # Simplex makes use of random perturbation, so if you want to see 86 | # what random results come after multiple runs, then comment this seed: 87 | #'srand' => 1385659192, 88 | 89 | }, 90 | 91 | # Comment the previous section and un-comment the section below to try the 92 | # Particle Swarm optimizer. You can alternate between Particle Swarm and 93 | # Simplex to achieve better and better results: 94 | 95 | #optimizer => { 96 | # # Use the Particle Swarm optimizer: 97 | # class => 'PDL::Opt::ParticleSwarm::Simple', 98 | # 99 | # opts => { 100 | # # Additional Documentation: 101 | # # https://metacpan.org/pod/PDL::Opt::ParticleSwarm 102 | # # and 103 | # # https://metacpan.org/pod/PDL::Opt::ParticleSwarm::Simple 104 | # -numParticles => 15, 105 | # -numNeighbors => 5, 106 | # -iterations => 1000, 107 | # -inertia => 0.5, 108 | # 109 | # # search +/-15% near the values below under 'vars' 110 | # -searchSize => 0.15, 111 | # -stallSpeed => 1e-5, 112 | # -stallSearchScale => 1.2, 113 | # #-randStartVelocity => 1, 114 | # -meWeight => 0.3, 115 | # -themWeight => 0.7, 116 | # -verbose => -1, 117 | # }, 118 | # 119 | # # Documented above: 120 | # stagnant_minima_count => 30, 121 | # stagnant_minima_tolerance => 1e-6, 122 | #}, 123 | 124 | ############################################################################ 125 | # NEC2 Geometry Variables 126 | 127 | # These variables are optimized by simplex. You can use these variables 128 | # in the the NEC2 geometry function below, in the "nec2" section. 129 | # 130 | # See `perldoc PDL::Opt::Simplex::Simple` for full variable option documentation. 131 | 132 | # yagi dimensions from https://www.qsl.net/dk7zb/PVC-Yagis/5-Ele-2m.htm 133 | vars => { 134 | # Element lengths 135 | lengths => { 136 | values => [ 1.038, 0.995, 0.959, 0.949, 0.935 ], 137 | enabled => [ 1, 1, 1, 1, 1 ], 138 | 139 | # Max size range for the "values" above. 140 | minmax => [ 0.85 => 1.5 ], 141 | 142 | # Round each iteration to benefit from caching 143 | # when the variables are nearly the same: 144 | round_each => 0.0001, 145 | 146 | # Round to 1.0mm (instead of 0.5mm) when saving the 147 | # final .nec output because the geometry sub below 148 | # divides by 2: 149 | round_result => 0.001, 150 | 151 | # Optional: Perturb the lengths less than the spaces while optimizing: 152 | #perturb_scale => .2, 153 | }, 154 | 155 | # Spaces between elements: 156 | spaces => { 157 | values => [ 0.000, 0.280, 0.15, 0.520, 0.53 ], 158 | enabled => [ 0, 1, 1, 1, 1 ], 159 | 160 | # Max size range for the "values" above. 161 | minmax => [ 0.1 => 0.7 ], 162 | 163 | # Round each iteration to benefit from caching 164 | # when the variables are nearly the same: 165 | round_each => 0.0001, 166 | 167 | # Round to 0.5mm when saving the final .nec output: 168 | round_result => 0.0005, 169 | 170 | # Optional: Perturb the spaces less than the lengths while optimizing: 171 | #perturb_scale => 1.5, 172 | }, 173 | 174 | wire_rad => { 175 | values => 0.002, # 2mm 176 | enabled => 0, 177 | }, 178 | 179 | # must be odd! 180 | wire_segments => { 181 | values => 11, 182 | enabled => 0, 183 | } 184 | }, 185 | 186 | 187 | ############################################################################ 188 | # NEC2 Geometry 189 | 190 | # This function takes a $vars as passed to f() by PDL::Opt::Simplex::Simple 191 | # in the simplified format. (The vars are defined above in the expanded 192 | # format so fields can be enabled/disabled.) 193 | # 194 | # This nec2 coderef must return an NEC2 perl object that defines the entire 195 | # NEC2 geometry. Here we use the NEC2::Antenna::Yagi for convenience but 196 | # you can $nec2->add(GW(x1 => 0, x2 => 1, ...)) for custom geometries: 197 | 198 | nec2 => sub { 199 | use strict; 200 | 201 | my $vars = shift; 202 | 203 | # See `perldoc NEC2` and `perldoc NEC2::Card` for NEC2 documentation. 204 | my $nec2 = NEC2->new(comment => [ 205 | 'Initial dimensions by Martin Steyer, DK7ZB', 206 | 'http://www.qsl.net/dk7zb/PVC-Yagis/5-Ele-2m.htm' 207 | ]); 208 | 209 | # The Yagi constructor takes the same vars format that 210 | # is specified above: 211 | my $yagi = NEC2::Antenna::Yagi->new(%$vars); 212 | 213 | 214 | # Here we add the $yagi object, but you could do your own geometry 215 | # elements (eg GW, GH, etc) and $nec2->add(...) them here: 216 | $nec2->add($yagi); 217 | 218 | # Note that the NEC2::Antenna::Yagi class includes 219 | # an EX card in the center of the 2nd radiated element, so 220 | # the EX card is not defined here: 221 | $nec2->add( 222 | # Free Space: 223 | GE(ground => 0), 224 | RP(ground => 0), 225 | 226 | # With ground: 227 | #GE(ground => 1), 228 | #GN(type => 1), 229 | #RP(ground => 1), 230 | 231 | NH, 232 | NE, 233 | Z0(z0 => 50), 234 | FR(mhz_min => 144-$mhz_shrink, mhz_max => 148+$mhz_shrink, n_freq => 32), 235 | ); 236 | 237 | return $nec2; 238 | }, 239 | 240 | # See `perldoc NEC2::xnec2c::optimize` for full goal documentation. 241 | goals => [ 242 | { 243 | name => 'Max gain', 244 | field => 'gain_max', 245 | enabled => 1, 246 | 247 | # Reduce the goal frequency range by 5MHz from the FR card's range so 248 | # we can see more of the graph than only what is being optimized. 249 | mhz_shrink => $mhz_shrink, 250 | 251 | weight => 10, 252 | 253 | type => 'avg', # calculate minima by sum, avg, min, max, or mag 254 | 255 | # Simplex minimizes, so return a negative value and raise it to a power. 256 | # A higher power makes the max-gain higher, raises VSWR 257 | # A lower power makes a flater curve, lowers VSWR 258 | result => sub { my ($gain,$mhz)=@_; return -$gain**0.5; } 259 | }, 260 | 261 | { 262 | name => 'VSWR', 263 | field => 'vswr', 264 | enabled => 1, 265 | 266 | mhz_shrink => $mhz_shrink, 267 | 268 | # Multiplicative weight, relative scale of the goal 269 | weight => 5, 270 | 271 | # Goal calculation type: 'max' gives a flatter SWR at the cost of 272 | # a slightly lower gain: 273 | #type => 'max', 274 | type => 'avg', 275 | 276 | # The optmizer minimizes results, penalize VSWR exponentially: 277 | # A larger base provides a flatter VSWR 278 | # A lower base reduces the strength of the VSWR penalty. 279 | result => sub { my ($vswr,$mhz)=@_; return 2**$vswr; } 280 | }, 281 | { 282 | name => 'Front/Back Ratio', 283 | field => 'fb_ratio', 284 | enabled => 1, 285 | 286 | mhz_shrink => $mhz_shrink, 287 | 288 | weight => 1, 289 | 290 | type => 'avg', 291 | 292 | # Experiment with different result functions: 293 | #result => sub { my ($fb,$mhz)=@_; return -$fb**1.0; } 294 | #result => sub { my ($fb,$mhz)=@_; return 2**(20/$fb); } 295 | result => sub { my ($fb,$mhz)=@_; return (20/$fb); } 296 | }, 297 | 298 | # Make sure the antenna is pointing up! This is important for particle 299 | # swarm: 300 | { 301 | name => 'gain_max_theta', 302 | field => 'gain_max_theta', 303 | enabled => 1, 304 | 305 | mhz_shrink => $mhz_shrink, 306 | 307 | weight => 1, 308 | 309 | type => 'avg', 310 | 311 | # Experiment with different result functions: 312 | #result => sub { my ($fb,$mhz)=@_; return -$fb**1.0; } 313 | #result => sub { my ($fb,$mhz)=@_; return 2**(20/$fb); } 314 | result => sub { my ($phi,$mhz)=@_; return 1000*abs(90-$phi)**2; } 315 | }, 316 | 317 | # Optional (disabled): Minimize antenna length. 318 | # 'field' is undefined, so this function will return a final goal result 319 | # independent of all frequencies. 320 | { 321 | name => 'Antenna Length', 322 | enabled => 0, 323 | weight => 10, 324 | result => sub { 325 | my ($vars, $csv) = @_; 326 | my $spaces = $vars->{spaces}; 327 | my ($zoff); 328 | 329 | $zoff += $_ foreach @$spaces; 330 | 331 | return $zoff - $spaces->[0]; 332 | } 333 | }, 334 | 335 | # Optional (disabled): Minimize antenna width 336 | # 'field' is undefined, so this function will return a final goal result 337 | # independent of all frequencies. 338 | { 339 | name => 'Antenna Width', 340 | enabled => 0, 341 | weight => 10, 342 | result => sub { 343 | my ($vars, $csv) = @_; 344 | my $lengths = $vars->{lengths}; 345 | my $width = 0; 346 | 347 | # find max width 348 | $width = ($_ > $width ? $_ : $width) foreach @$lengths; 349 | 350 | return $width; 351 | } 352 | } 353 | ] 354 | 355 | 356 | }; #end of file 357 | 358 | 359 | -------------------------------------------------------------------------------- /lib/NEC2.pm: -------------------------------------------------------------------------------- 1 | # This program is free software; you can redistribute it and/or modify 2 | # it under the terms of the GNU General Public License as published by 3 | # the Free Software Foundation; either version 3 of the License, or 4 | # (at your option) any later version. 5 | # 6 | # This program is distributed in the hope that it will be useful, 7 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 8 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 9 | # GNU Library General Public License for more details. 10 | # 11 | # You should have received a copy of the GNU General Public License 12 | # along with this program; if not, write to the Free Software 13 | # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. 14 | # 15 | # Copyright (C) 2021- by Eric Wheeler, KJ7LNW. All rights reserved. 16 | # 17 | # The official website and doumentation for xnec2c-optimizer is available here: 18 | # https://www.xnec2c.org/ 19 | # 20 | package NEC2; 21 | 22 | use strict; 23 | use warnings; 24 | 25 | use Exporter; 26 | 27 | use Math::Vector::Real; 28 | use Math::Matrix; 29 | use IO::Handle; 30 | 31 | use NEC2::Card::CM; 32 | use NEC2::Card::EN; 33 | use NEC2::Card::EX; 34 | use NEC2::Card::FR; 35 | use NEC2::Card::GA; 36 | use NEC2::Card::GC; 37 | use NEC2::Card::GE; 38 | use NEC2::Card::GH; 39 | use NEC2::Card::GM; 40 | use NEC2::Card::GN; 41 | use NEC2::Card::GR; 42 | use NEC2::Card::GS; 43 | use NEC2::Card::GW; 44 | use NEC2::Card::GX; 45 | use NEC2::Card::LD; 46 | use NEC2::Card::NE; 47 | use NEC2::Card::NT; 48 | use NEC2::Card::RP; 49 | use NEC2::Card::SC; 50 | use NEC2::Card::SM; 51 | use NEC2::Card::SP; 52 | use NEC2::Card::TL; 53 | use NEC2::Card::ZO; 54 | 55 | require NEC2::Polyline; 56 | require NEC2::Coax; 57 | 58 | BEGIN { 59 | our @ISA = qw(Exporter); 60 | our @EXPORT = ( 61 | 62 | # Comment cards 63 | qw/CM CE/, 64 | 65 | # Geo card functions 66 | qw/GA GE GF GH GM GR GS GW GC GX SP SM/, 67 | 68 | # Program card functions 69 | qw/CP EK EN EX FR GD GN KH LD NE NH NT NX PQ PT RP TL WG XQ/, 70 | 71 | # xnec2c extensions: 72 | qw/ZO Z0/, 73 | 74 | # Perl NEC2 extensions: 75 | qw/Polyline Coax/, 76 | ); 77 | } 78 | 79 | use overload '""' => \&stringify; 80 | 81 | sub new 82 | { 83 | my ($class, %args) = @_; 84 | 85 | my $self = bless(\%args, $class); 86 | 87 | $self->{comment} //= 'A blank comment'; 88 | 89 | return $self; 90 | } 91 | 92 | sub add 93 | { 94 | my ($self, @cards) = @_; 95 | 96 | foreach my $card (@cards) 97 | { 98 | push @{ $self->{comment_cards} }, $card->comment_cards(); 99 | push @{ $self->{geo_cards} }, $card->geo_cards(); 100 | push @{ $self->{program_cards} }, $card->program_cards(); 101 | } 102 | 103 | return $self; 104 | } 105 | 106 | 107 | sub comment_cards 108 | { 109 | my $self = shift; 110 | return @{ $self->{comment_cards} }; 111 | } 112 | 113 | sub geo_cards 114 | { 115 | my $self = shift; 116 | return @{ $self->{geo_cards} }; 117 | } 118 | 119 | sub program_cards 120 | { 121 | my $self = shift; 122 | return @{ $self->{program_cards} }; 123 | } 124 | 125 | sub card_filter 126 | { 127 | my ($self, $card, @cards) = @_; 128 | 129 | if (!@cards) 130 | { 131 | die "card_filter($card): No cards were defined for filtering"; 132 | } 133 | 134 | $card = uc($card); 135 | 136 | my @filter; 137 | if ($card =~ s/^!//) { 138 | @filter = grep { $_->card_name ne $card } @cards; 139 | } 140 | else 141 | { 142 | @filter = grep { $_->card_name eq $card } @cards; 143 | } 144 | 145 | die "filter for $card returned >1 result, but !wantarray" if (@filter > 1 && !wantarray); 146 | return @filter if (wantarray); 147 | return $filter[0]; 148 | } 149 | 150 | sub geo_card_filter 151 | { 152 | my ($self, $card) = @_; 153 | 154 | return $self->card_filter($card, $self->geo_cards()); 155 | } 156 | 157 | sub program_card_filter 158 | { 159 | my ($self, $card) = @_; 160 | 161 | return $self->card_filter($card, $self->program_cards()); 162 | } 163 | 164 | 165 | # True if $a >= $b within $tolerance 166 | sub approx_greater 167 | { 168 | my ($a, $b, $tolerance) = @_; 169 | 170 | $tolerance //= 1e-6; 171 | 172 | return ($a-$b) > -$tolerance; 173 | } 174 | 175 | # True if $a <= $b within $tolerance 176 | sub approx_lesser 177 | { 178 | my ($a, $b, $tolerance) = @_; 179 | 180 | $tolerance //= 1e-6; 181 | 182 | return ($b-$a) > -$tolerance; 183 | } 184 | 185 | # True if $a == $b within $tolerance 186 | sub approx_equal 187 | { 188 | my ($a, $b, $tolerance) = @_; 189 | 190 | $tolerance //= 1e-6; 191 | 192 | return abs($a-$b) < $tolerance; 193 | } 194 | 195 | 196 | # Returns shortest line: [ [x1,y1,z1], [x2,y2,z2], distance ]. 197 | # If askew lines cross (or nearly-intersect) then xyz1 and xyz2 are undefined 198 | # and only distance is returned. 199 | # 200 | # Thank you to @Fnord: https://stackoverflow.com/a/18994296/14055985 201 | # 202 | # 1e-6 is used to adjust for floating point error. 203 | sub line_intersect 204 | { 205 | # Map @_ as vectors: 206 | my ($a0, $a1, $b0, $b1) = map { V(@{ $_ }) } @_; 207 | 208 | my $A = ($a1-$a0); 209 | my $B = ($b1-$b0); 210 | 211 | my $magA = abs($A); 212 | my $magB = abs($B); 213 | 214 | # If length line segment: 215 | if ($magA == 0 || $magB == 0) 216 | { 217 | return V(undef, undef, 0); 218 | } 219 | 220 | my $_A = $A / $magA; 221 | my $_B = $B / $magB; 222 | 223 | my $cross = $_A x $_B; 224 | my $denom = $cross->norm2; 225 | 226 | # If lines are parallel (denom=0) test if lines overlap. 227 | # If they don't overlap then there is a closest point solution. 228 | # If they do overlap, there are infinite closest positions, but there is a closest distance 229 | #if ($denom == 0) 230 | if (approx_equal($denom, 0)) 231 | { 232 | my $d0 = $_A * ($b0-$a0); 233 | my $d1 = $_A * ($b1-$a0); 234 | 235 | # Is segment B before A? 236 | #if ($d0 <= 0 && 0 >= $d1) 237 | if (approx_lesser($d0, 0) && approx_greater(0, $d1)) 238 | { 239 | if (abs($d0) < abs($d1)) 240 | { 241 | return V($a0, $b0, abs($a0-$b0)); 242 | } 243 | else 244 | { 245 | return V($a0, $b1, abs($a0-$b1)); 246 | } 247 | } 248 | # Is segment B after A? 249 | #elsif ($d0 >= $magA && $magA <= $d1) 250 | elsif (approx_greater($d0, $magA) && approx_lesser($magA, $d1)) 251 | { 252 | if (abs($d0) < abs($d1)) 253 | { 254 | return V($a1, $b0, abs($a1-$b0)); 255 | } 256 | else 257 | { 258 | return V($a1, $b1, abs($a1-$b1)); 259 | } 260 | } 261 | else 262 | { 263 | # Segments overlap, return distance between parallel segments 264 | return V(V(), V(), abs((($d0*$_A)+$a0)-$b0)); 265 | } 266 | 267 | } 268 | else 269 | { 270 | # Lines criss-cross: Calculate the projected closest points 271 | my $t = ($b0 - $a0); 272 | 273 | # Math::Matrix won't wirth with Math::Vector::Real 274 | # even though they are blessed arrays, 275 | # so convert them to arrays and back to refs: 276 | my $detA = Math::Matrix->new([ [ @$t ], [ @$_B ], [ @$cross] ])->det; 277 | my $detB = Math::Matrix->new([ [ @$t ], [ @$_A ], [ @$cross] ])->det; 278 | 279 | my $t0 = $detA / $denom; 280 | my $t1 = $detB / $denom; 281 | 282 | my $pA = $a0 + ($_A * $t0); # Projected closest point on segment A 283 | my $pB = $b0 + ($_B * $t1); # Projected closest point on segment A 284 | 285 | if ($t0 < 0) 286 | { 287 | $pA = $a0; 288 | } 289 | elsif ($t0 > $magA) 290 | { 291 | $pA = $a1; 292 | } 293 | 294 | if ($t1 < 0) 295 | { 296 | $pB = $b0; 297 | } 298 | elsif ($t1 > $magB) 299 | { 300 | $pB = $b1; 301 | } 302 | 303 | # Clamp projection A 304 | if ($t0 < 0 || $t0 > $magA) 305 | { 306 | my $dot = $_B * ($pA-$b0); 307 | if ($dot < 0) 308 | { 309 | $dot = 0; 310 | } 311 | elsif ($dot > $magB) 312 | { 313 | $dot = $magB; 314 | } 315 | 316 | $pB = $b0 + ($_B * $dot) 317 | } 318 | 319 | # Clamp projection B 320 | if ($t1 < 0 || $t1 > $magB) 321 | { 322 | my $dot = $_A * ($pB-$a0); 323 | if ($dot < 0) 324 | { 325 | $dot = 0; 326 | } 327 | elsif ($dot > $magA) 328 | { 329 | $dot = $magA; 330 | } 331 | 332 | $pA = $a0 + ($_A * $dot) 333 | } 334 | 335 | return V($pA, $pB, abs($pA-$pB)); 336 | } 337 | } 338 | 339 | sub test_gw_intersections 340 | { 341 | my ($self) = @_; 342 | 343 | =pod 344 | # validation: 345 | print "sample: " . line_intersect( 346 | [ '-13.203', 0, '17.39' ], [ '-25.3685', 0, '17.39' ], 347 | [ 0, 0, '17.39' ], [ '-13.203', 0, '17.39' ] 348 | ) . "\n" ; 349 | 350 | print "example: " . line_intersect( 351 | [13.43, 21.77, 46.81 ], [27.83, 31.74, -26.60 ], 352 | [77.54, 7.53, 6.22 ], [26.99, 12.39, 11.18 ]) . "\n" ; 353 | 354 | print "contiguous: " . line_intersect( 355 | [0, 0, 0 ], [ 0, 0, 1 ], 356 | [0, 0, 1 ], [ 0, 0, 2 ], 357 | ) . "\n" ; 358 | 359 | print "contiguous 90: " . line_intersect( 360 | [0, 0, 0 ], [ 0, 0, 1 ], 361 | [0, 0, 1 ], [ 0, 1, 1 ], 362 | ) . "\n" ; 363 | 364 | print "colinear separate: " . line_intersect( 365 | [0, 0, 0 ], [ 0, 0, 1 ], 366 | [0, 0, 2 ], [ 0, 0, 3 ], 367 | ) . "\n" ; 368 | 369 | print "cross: " . line_intersect( 370 | [1, 1, 0 ], [ -1, -1, 0 ], 371 | [-1, 1, 0 ], [ 1, -1, 0 ], 372 | ) . "\n" ; 373 | 374 | print "cross+z: " . line_intersect( 375 | [1, 1, 0 ], [ -1, -1, 0 ], 376 | [-1, 1, 1 ], [ 1, -1, 1 ], 377 | ) . "\n" ; 378 | 379 | print "full overlap1: " . line_intersect( 380 | [2, 0, 0 ], [ 5, 0, 0 ], 381 | [3, 0, 0 ], [ 4, 0, 0 ], 382 | ) . "\n" ; 383 | 384 | print "full overlap2: " . line_intersect( 385 | [3, 0, 0 ], [ 4, 0, 0 ], 386 | [2, 0, 0 ], [ 5, 0, 0 ], 387 | ) . "\n" ; 388 | 389 | print "partial overlap1: " . line_intersect( 390 | [2, 0, 0 ], [ 5, 0, 0 ], 391 | [3, 0, 0 ], [ 6, 0, 0 ], 392 | ) . "\n" ; 393 | 394 | print "partial overlap2: " . line_intersect( 395 | [3, 0, 0 ], [ 6, 0, 0 ], 396 | [2, 0, 0 ], [ 5, 0, 0 ], 397 | ) . "\n" ; 398 | 399 | print "parallel: " . line_intersect( 400 | [3, 0, 0 ], [ 6, 0, 0 ], 401 | [3, 0, 1 ], [ 6, 0, 1 ], 402 | ) . "\n" ; 403 | #exit; 404 | 405 | # output: 406 | #example: {{20.29994361624, 26.5264817954106, 11.7875999397098}, {26.99, 12.39, 11.18}, 15.6513944955904} 407 | #contiguous: {{0, 0, 1}, {0, 0, 1}, 0} 408 | #contiguous 90: {{0, 0, 1}, {0, 0, 1}, 0} 409 | #colinear separate{{0, 0, 1}, {0, 0, 2}, 1} 410 | #cross: {{-2.22044604925031e-16, -2.22044604925031e-16, 0}, 411 | # {2.22044604925031e-16, -2.22044604925031e-16, 0}, 412 | # 4.44089209850063e-16} 413 | #cross+z: {{-2.22044604925031e-16, -2.22044604925031e-16, 0}, 414 | # {2.22044604925031e-16, -2.22044604925031e-16, 1}, 415 | # 1} 416 | #full overlap1: {{}, {}, 0} 417 | #full overlap2: {{}, {}, 0} 418 | #partial overlap1: {{}, {}, 0} 419 | #partial overlap2: {{}, {}, 0} 420 | #parallel: {{}, {}, 1} 421 | 422 | =cut 423 | 424 | my @gw = $self->geo_card_filter('GW'); 425 | my @intersecting_cards; 426 | 427 | foreach my $A (@gw) 428 | { 429 | my $tag1 = $A->get('tag'); 430 | 431 | foreach my $B (@gw) 432 | { 433 | my $tag2 = $B->get('tag'); 434 | 435 | 436 | # Skip the GW of itself. FIXME: This breaks if the tags are the same! 437 | next if $tag1 == $tag2; 438 | 439 | my $inter = line_intersect($A->get_points, $B->get_points); 440 | #print "inter[$tag1, $tag2]: $inter\n"; 441 | 442 | # Skip if they have a nontrivial distance: 443 | next if $inter->[2] < -1e-6 || $inter->[2] > 1e-6; 444 | 445 | # Skip if they are connected endpoints with a nearly-zero distance: 446 | next if @{$inter->[0]} == 3 && @{$inter->[1]} == 3 && 447 | abs($inter->[0] - $inter->[1])<1e-6 && # approximately thsame point 448 | -1e-6 < $inter->[2] && $inter->[2] < 1e-6; 449 | 450 | # Skip if they have a distance: 451 | next if ($inter->[2] < -1e-6 || 1e-6 < $inter->[2]) ; 452 | 453 | push @intersecting_cards, [$A, $B]; 454 | # Report all others: 455 | print "inter[$tag1, $tag2]: $inter\n"; 456 | use Data::Dumper; 457 | print "Overlap tags $tag1 and $tag2: " . Dumper([$A->get_points], [$B->get_points]); 458 | } 459 | } 460 | 461 | return @intersecting_cards; 462 | } 463 | 464 | sub stringify 465 | { 466 | my ($self) = @_; 467 | 468 | my $ret = ''; 469 | 470 | if (defined($self->{comment}) && ref($self->{comment}) eq 'ARRAY') 471 | { 472 | $ret .= CM(comment => $_) foreach (@{ $self->{comment} }); 473 | } 474 | else { 475 | $ret .= CM(comment => $self->{comment}); 476 | } 477 | 478 | $ret .= $_ foreach ($self->comment_cards); 479 | 480 | $ret .= CE(); 481 | 482 | # always put the GE card at the end of the geometry. Default to freespace GE if 483 | # none was defined: 484 | my $GE = $self->geo_card_filter('GE'); 485 | $GE //= GE(); 486 | 487 | # exclude GE, it goes above 488 | my @geo = $self->geo_card_filter('!GE'); 489 | 490 | # exclude EN cards because they go at the end: 491 | my @program = $self->program_card_filter('!EN'); 492 | 493 | $ret .= join('', @geo); 494 | $ret .= $GE; 495 | 496 | $ret .= join('', map { "$_" } @program); 497 | $ret .= EN(); 498 | 499 | return $ret; 500 | } 501 | 502 | sub save 503 | { 504 | my ($self, $fn) = @_; 505 | 506 | $fn or die "invalid filename: $fn"; 507 | 508 | # Write the outputfile. The extra flushing is to get the data on disk 509 | # before xnec2c opens the file when optimizing. If it gets a partial 510 | # copy it may crash. 511 | open(my $structure, ">", $fn) or die "$!: $fn"; 512 | $structure->autoflush(); 513 | print $structure $self; 514 | $structure->flush(); 515 | $structure->sync(); 516 | close($structure); 517 | } 518 | 519 | 520 | ##################################################################### 521 | # Static Functions 522 | 523 | ########################################################### 524 | # Card shortcuts: 525 | 526 | # Comment cards 527 | sub CM { return NEC2::Card::CM->new(@_) } # Comment 528 | sub CE { return NEC2::Card::CE->new(@_) } # Comment End 529 | 530 | # Geo cards 531 | sub GA { return NEC2::Card::GA->new(@_) } # Arc 532 | sub GH { return NEC2::Card::GH->new(@_) } # Helix 533 | sub GW { return NEC2::Card::GW->new(@_) } # Wire 534 | sub GC { return NEC2::Card::GC->new(@_) } # Tapered wire 535 | sub SP { return NEC2::Card::SP->new(@_) } # Surface Patch 536 | sub SM { return NEC2::Card::SM->new(@_) } # Multiple Surface Patch 537 | sub SC { return NEC2::Card::SC->new(@_) } # Additional SC card for SP and SM (internal) 538 | 539 | # Transform cards 540 | sub GM { return NEC2::Card::GM->new(@_) } # Move 541 | sub GR { return NEC2::Card::GR->new(@_) } # Rotate 542 | sub GS { return NEC2::Card::GS->new(@_) } # Scale 543 | sub GX { return NEC2::Card::GX->new(@_) } # Reflection 544 | 545 | # Special cards 546 | sub GF { return NEC2::Card::GF->new(@_) } # Read NGF File 547 | sub GE { return NEC2::Card::GE->new(@_) } # Geo End 548 | 549 | # Program card shortcuts: 550 | sub CP { return NEC2::Card::CP->new(@_) } # Maximum coupling Calculation 551 | sub EK { return NEC2::Card::EK->new(@_) } # Extended Thin-Wire Kernel 552 | sub EN { return NEC2::Card::EN->new(@_) } # End of Run 553 | sub EX { return NEC2::Card::EX->new(@_) } # Excitation 554 | sub FR { return NEC2::Card::FR->new(@_) } # Frequency 555 | sub GD { return NEC2::Card::GD->new(@_) } # Additional Ground Parameters 556 | sub GN { return NEC2::Card::GN->new(@_) } # Ground Parameters 557 | sub KH { return NEC2::Card::KH->new(@_) } # Interaction Approximation Range 558 | sub LD { return NEC2::Card::LD->new(@_) } # Loading 559 | sub NE { return NEC2::Card::NE->new(@_) } # Near Fields (E: E field) 560 | sub NH { return NEC2::Card::NH->new(@_) } # Near Fields (H: Mag field) 561 | sub NT { return NEC2::Card::NT->new(@_) } # Networks 562 | sub NX { return NEC2::Card::NX->new(@_) } # Next Structure 563 | sub PQ { return NEC2::Card::PQ->new(@_) } # Print Control for Charge on Wires 564 | sub PT { return NEC2::Card::PT->new(@_) } # Print Control for Current on Wires 565 | sub RP { return NEC2::Card::RP->new(@_) } # Radiation Pattern 566 | sub TL { return NEC2::Card::TL->new(@_) } # Transmission Line 567 | sub WG { return NEC2::Card::WG->new(@_) } # Write NGF File 568 | sub XQ { return NEC2::Card::XQ->new(@_) } # Execute 569 | 570 | # xnec2c extensions 571 | sub ZO { return NEC2::Card::ZO->new(@_) } # Charectaristic Impedance 572 | sub Z0 { return NEC2::Card::ZO->new(@_) } # Charectaristic Impedance (Z-zero alias) 573 | 574 | # Perl NEC2 extensions: 575 | sub Polyline { return NEC2::Polyline->new(@_) } # GW card generator 576 | sub Coax { return NEC2::Coax->new(@_) } # Coax line 577 | 578 | 1; 579 | -------------------------------------------------------------------------------- /lib/NEC2/Antenna/Yagi.pm: -------------------------------------------------------------------------------- 1 | # This program is free software; you can redistribute it and/or modify 2 | # it under the terms of the GNU General Public License as published by 3 | # the Free Software Foundation; either version 3 of the License, or 4 | # (at your option) any later version. 5 | # 6 | # This program is distributed in the hope that it will be useful, 7 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 8 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 9 | # GNU Library General Public License for more details. 10 | # 11 | # You should have received a copy of the GNU General Public License 12 | # along with this program; if not, write to the Free Software 13 | # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. 14 | # 15 | # Copyright (C) 2021- by Eric Wheeler, KJ7LNW. All rights reserved. 16 | # 17 | # The official website and doumentation for xnec2c-optimizer is available here: 18 | # https://www.xnec2c.org/ 19 | # 20 | package NEC2::Antenna::Yagi; 21 | 22 | use NEC2; 23 | 24 | use parent 'NEC2::Shape'; 25 | 26 | use strict; 27 | use warnings; 28 | 29 | 30 | sub defaults 31 | { 32 | return (plane => 'xz', 33 | wire_segments => 31, 34 | wire_rad => 0.002, 35 | tag => 1); 36 | } 37 | 38 | sub new 39 | { 40 | my ($class, %args) = @_; 41 | 42 | my $self = bless(\%args, $class); 43 | 44 | my %defaults = $self->defaults; 45 | foreach my $k (keys %defaults) 46 | { 47 | $self->{$k} //= $defaults{$k}; 48 | } 49 | 50 | #use Data::Dumper; 51 | #print Dumper $self; 52 | 53 | die "plane must be xy, xz, or yz: $self->{plane}" if ($self->{plane} !~ /^xy|xz|yz$/); 54 | die "wire_segments must be odd: $self->{wire_segments}" if ($self->{wire_segments} % 2 == 0); 55 | 56 | $self->gen_cards; 57 | 58 | return $self; 59 | } 60 | 61 | sub gen_cards 62 | { 63 | my $self = shift; 64 | my (%vars) = @_; 65 | 66 | my $lengths = $self->{lengths}; 67 | my $spaces = $self->{spaces}; 68 | 69 | my $n_segments = $self->{wire_segments} ; # must be odd! 70 | 71 | #print "lengths: $lengths\n"; 72 | #print "spaces: $spaces\n"; 73 | my @geo; 74 | my @program; 75 | 76 | $self->{geo_cards} = \@geo; 77 | $self->{program_cards} = \@program; 78 | 79 | 80 | my ($a, $b) = split //, $self->{plane}; 81 | 82 | my $zoff = 0; 83 | for (my $i = 0 ; $i < @$lengths ; $i++) { 84 | my $l = $lengths->[$i]; 85 | my $s = $spaces->[$i]; 86 | 87 | # spaces are distances between so accumulate the Z offset 88 | $zoff += $s; 89 | 90 | # left and right of zero 91 | $l /= 2; 92 | push(@geo, 93 | GW( 94 | tag => $self->{tag} + $i, 95 | ns => $n_segments, 96 | "${a}1" => $l, "${a}2" => -$l, 97 | "${b}1" => $zoff, "${b}2" => $zoff, 98 | rad => $self->{wire_rad} 99 | 100 | ) 101 | ); 102 | 103 | } 104 | 105 | # End of geometry and program parameters: 106 | push @program, 107 | EX( 108 | ex_tag => $self->{tag} + 1, 109 | ex_segment => int($n_segments / 2) + 1); 110 | 111 | 112 | # Really nothing to return, geo and program cards are stored in the class 113 | return $self; 114 | } 115 | 116 | sub geo_cards 117 | { 118 | my $self = shift; 119 | 120 | return @{ $self->{geo_cards} }; 121 | } 122 | 123 | sub program_cards 124 | { 125 | my $self = shift; 126 | 127 | return @{ $self->{program_cards} }; 128 | } 129 | 130 | 1; 131 | -------------------------------------------------------------------------------- /lib/NEC2/Card.pm: -------------------------------------------------------------------------------- 1 | # This program is free software; you can redistribute it and/or modify 2 | # it under the terms of the GNU General Public License as published by 3 | # the Free Software Foundation; either version 3 of the License, or 4 | # (at your option) any later version. 5 | # 6 | # This program is distributed in the hope that it will be useful, 7 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 8 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 9 | # GNU Library General Public License for more details. 10 | # 11 | # You should have received a copy of the GNU General Public License 12 | # along with this program; if not, write to the Free Software 13 | # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. 14 | # 15 | # Copyright (C) 2021- by Eric Wheeler, KJ7LNW. All rights reserved. 16 | # 17 | # The official website and doumentation for xnec2c-optimizer is available here: 18 | # https://www.xnec2c.org/ 19 | # 20 | package NEC2::Card; 21 | 22 | use strict; 23 | use warnings; 24 | 25 | use overload '""' => \&stringify; 26 | 27 | use constant geo_card_names => (qw/GA GE GF GH GM GR GS GW GC GX SP SM SC/); 28 | use constant program_card_names => (qw/CP EK EN EX FR GD GN KH LD NE NH NT NX PQ PT RP TL WG XQ ZO/); 29 | 30 | sub stringify 31 | { 32 | my ($self) = @_; 33 | 34 | my $card = $self->card_name(); 35 | return "$card " . join("\t", $self->card_vals) . "\n"; 36 | } 37 | 38 | sub new 39 | { 40 | my ($class, @args) = @_; 41 | 42 | die "NEC2::Card->new called with a ref?" if ref ($class); 43 | 44 | my %h; 45 | 46 | my $self = bless({}, $class); 47 | 48 | # Default the card to all 0's. So far as I can tell, program 49 | # cards have 10 options and geometry cards have 9 options. 50 | 51 | if ($self->is_geo_card()) 52 | { 53 | $self->{card} = [ map { 0 } (1..9) ] 54 | } 55 | elsif ($self->is_program_card()) 56 | { 57 | $self->{card} = [ map { 0 } (1..10) ] 58 | } 59 | elsif (ref($self) !~ /NEC2::Card::C[ME]/) # ignore comment cards, not geo nor program. 60 | { 61 | die "Unknown card type: $class"; 62 | } 63 | 64 | # Exclude defaults that are defined in @args so they only get 65 | # set once. This is important for things like FR's mhz_min and 66 | # mhz_max that will break if they are done out of order, especially 67 | # if the defaults do not maintain a mindefaults; 71 | my %ignore; 72 | for (my $i = 0; $i < @args; $i += 2) 73 | { 74 | $ignore{$args[$i]} = 1; 75 | } 76 | 77 | my @use_defaults; 78 | for (my $i = 0; $i < @defaults; $i += 2) 79 | { 80 | push (@use_defaults, $defaults[$i], $defaults[$i+1]) if !$ignore{$defaults[$i]}; 81 | } 82 | 83 | 84 | # Set passed values. 85 | # Note that the order is important here, so we pass a list not a hash: 86 | $self->set(@use_defaults, @args); 87 | 88 | return $self; 89 | } 90 | 91 | 92 | ##################################################################### 93 | # Functions intended for overload by child classes: 94 | 95 | # Map friendly names to NEC2 registers like I1, F2, ... 96 | # Must be overriden by child class. 97 | sub param_map 98 | { 99 | my $self = shift; 100 | 101 | die "Incomplete class, param_map must be defined: ".ref($self); 102 | 103 | # Should return { var => 'f1', ... mappings }; 104 | } 105 | 106 | # Default values for classes to instantiate without arguments 107 | sub defaults 108 | { 109 | return () 110 | } 111 | 112 | # Return all geometry cards in the class. Overload this 113 | # if a child class may require multiple geo cards to 114 | # represent itself, such as GW and GC for tapered wires. 115 | sub geo_cards 116 | { 117 | my $self = shift; 118 | 119 | return $self if ($self->is_geo_card()); 120 | return (); 121 | 122 | } 123 | 124 | # Used for antenna models that may return program cards such as EX 125 | # for the excited element to drive the antenna. 126 | sub program_cards 127 | { 128 | my $self = shift; 129 | 130 | return $self if ($self->is_program_card()); 131 | return (); 132 | } 133 | 134 | sub comment_cards 135 | { 136 | my $self = shift; 137 | 138 | return $self if ($self->is_comment_card()); 139 | return (); 140 | } 141 | 142 | 143 | 144 | # Override this if the card name doesn't take the format of 145 | # NEC2::(CARDNAME) from the class 146 | sub card_name 147 | { 148 | my ($self) = @_; 149 | 150 | my $class = ref $self || $self; 151 | 152 | if ($class =~ /NEC2::Card::([A-Z]{2})(:|$)/) 153 | { 154 | return $1; 155 | } 156 | else 157 | { 158 | die "Cannot figure out card name for class: $class"; 159 | } 160 | } 161 | 162 | 163 | # The child class may create internal "special variables" by 164 | # implementing set_special and get_special. 165 | # get_special must return a defined value if the variable is defined. See FR's mhz_max feature. 166 | sub get_special 167 | { 168 | return undef; 169 | } 170 | 171 | # set_special must return a true value if that variable is supported. 172 | # Note that this true/false return behavior for set is different than 173 | # the defined/undefined return behavior for get. 174 | sub set_special 175 | { 176 | return 0; 177 | } 178 | 179 | ##################################################################### 180 | # Core functions 181 | # get card (or class) value: 182 | # 183 | sub get_card_var 184 | { 185 | my ($self, $var) = @_; 186 | 187 | my $val = $self->get_special($var); 188 | 189 | if (!defined $val) 190 | { 191 | my $idx = $self->_get_var_idx($var); 192 | if (defined($idx)) 193 | { 194 | $val = $self->{card}->[$idx]; 195 | } 196 | } 197 | 198 | if (!defined($val)) 199 | { 200 | die "Unknown var for class " . ref($self) . ": $var"; 201 | } 202 | 203 | return $val; 204 | } 205 | 206 | sub get 207 | { 208 | my ($self, $var) = @_; 209 | 210 | our $depth++; 211 | die "get: depth is too deep (maybe get_card_var instead of get?): $var" if ($depth > 10); 212 | 213 | my $val = $self->get_special($var); 214 | if (!defined($val)) 215 | { 216 | $val = $self->get_card_var($var); 217 | } 218 | 219 | $depth--; 220 | 221 | return $val; 222 | } 223 | 224 | # directly sets a card variable, will not call set_special(). 225 | sub set_card_var 226 | { 227 | my ($self, $var, $val) = @_; 228 | 229 | my $idx = $self->_get_var_idx($var); 230 | if (!defined $idx) 231 | { 232 | die "Unknown var for class " . ref($self) . ": $var"; 233 | } 234 | else 235 | { 236 | $self->{card}->[$idx] = $val; 237 | } 238 | 239 | return $self; 240 | } 241 | 242 | # Sets a single var, val tuple. 243 | sub set_one 244 | { 245 | my ($self, $var, $val) = @_; 246 | 247 | our $depth++; 248 | die "set_one: depth is too deep (maybe set_card_var instead of set?): $var => $val" if ($depth > 10); 249 | 250 | # first try set_special in case the value is overriden: 251 | if (!$self->set_special($var, $val)) 252 | { 253 | $self->set_card_var($var, $val); 254 | } 255 | 256 | $depth--; 257 | 258 | return $self; 259 | } 260 | 261 | # Sets a hash of values: 262 | # example: $self->set(tag => 3, f4 => 7, ...) 263 | sub set 264 | { 265 | my ($self, @var_vals) = @_; 266 | 267 | # maintain the order of the list because some vars 268 | # like FR's mhz_min and mhz_max are order-dependent. 269 | while (my ($var, $val) = splice(@var_vals, 0, 2)) 270 | { 271 | #print $self->card_name . ": $var => $val\n"; 272 | $self->set_one($var, $val); 273 | } 274 | 275 | # return object for easy chaining 276 | return $self; 277 | } 278 | 279 | 280 | ##################################################################### 281 | # Classful functions, not intended for overloading. 282 | 283 | sub card_vals 284 | { 285 | my $self = shift; 286 | return @{ $self->{card} || [] }; 287 | } 288 | 289 | 290 | # Return nonzero if this is a tagged geometry. 291 | sub tagged 292 | { 293 | my $self = shift; 294 | return $self->is_geo_card; 295 | } 296 | 297 | sub is_geo_card 298 | { 299 | my $self = shift; 300 | my $card_name = $self->card_name; 301 | return scalar(grep { $_ eq $card_name } geo_card_names()); 302 | } 303 | 304 | sub is_program_card 305 | { 306 | my $self = shift; 307 | my $card_name = $self->card_name; 308 | return scalar(grep { $_ eq $card_name } program_card_names()); 309 | } 310 | 311 | sub is_comment_card 312 | { 313 | my $self = shift; 314 | return $self->card_name eq 'CM'; 315 | } 316 | 317 | 318 | sub is_card 319 | { 320 | my $self = shift; 321 | return $self->is_geo_card || $self->is_program_card; 322 | } 323 | 324 | 325 | ##################################################################### 326 | # Classful internal functions, not intended for user use. 327 | 328 | 329 | # Given a variable, return the index into the card array by 330 | # looking up the location in its param_map. 331 | sub _get_var_idx 332 | { 333 | my ($self, $var) = @_; 334 | 335 | # always lowercase 336 | $var = lc($var); 337 | 338 | my $idx = $var; 339 | my $param_map = $self->_get_param_map(); 340 | 341 | # If the param_map returns a non-integer then remap it 342 | my $depth = 0; 343 | while (defined($idx) && $idx !~ /^[0-9]+$/) 344 | { 345 | $idx = $param_map->{$idx}; 346 | #print "$var: $idx\n"; 347 | die "Variable cycle detected for class " . ref($self) . ": $var" if $depth++ > 10; 348 | } 349 | 350 | return $idx; 351 | } 352 | 353 | # build the paramater map based on the card type. 354 | sub _get_param_map 355 | { 356 | my ($self) = @_; 357 | 358 | my $map = $self->param_map; 359 | 360 | my %type_map; 361 | %type_map = _program_card_param_maps() if ($self->is_program_card); 362 | %type_map = _geo_card_param_maps() if ($self->is_geo_card); 363 | 364 | return { %$map, %type_map }; 365 | } 366 | 367 | sub _program_card_param_maps 368 | { 369 | 370 | # nec card names, can be overridden in the child class: 371 | return ( 372 | # integer register values for card: 373 | i1 => 0, 374 | i2 => 1, 375 | i3 => 2, 376 | i4 => 3, 377 | 378 | # floating-point register values for card: 379 | f1 => 4, 380 | f2 => 5, 381 | f3 => 6, 382 | f4 => 7, 383 | f5 => 8, 384 | f6 => 9); 385 | 386 | } 387 | 388 | sub _geo_card_param_maps 389 | { 390 | 391 | # nec card names, can be overridden in the child class: 392 | return ( 393 | # integer register values for card: 394 | i1 => 0, 395 | i2 => 1, 396 | 397 | # floating-point register values for card: 398 | f1 => 2, 399 | f2 => 3, 400 | f3 => 4, 401 | f4 => 5, 402 | f5 => 6, 403 | f6 => 7, 404 | f7 => 8); 405 | } 406 | 407 | 1; 408 | -------------------------------------------------------------------------------- /lib/NEC2/Card/CM.pm: -------------------------------------------------------------------------------- 1 | # This program is free software; you can redistribute it and/or modify 2 | # it under the terms of the GNU General Public License as published by 3 | # the Free Software Foundation; either version 3 of the License, or 4 | # (at your option) any later version. 5 | # 6 | # This program is distributed in the hope that it will be useful, 7 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 8 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 9 | # GNU Library General Public License for more details. 10 | # 11 | # You should have received a copy of the GNU General Public License 12 | # along with this program; if not, write to the Free Software 13 | # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. 14 | # 15 | # Copyright (C) 2021- by Eric Wheeler, KJ7LNW. All rights reserved. 16 | # 17 | # The official website and doumentation for xnec2c-optimizer is available here: 18 | # https://www.xnec2c.org/ 19 | # 20 | package NEC2::Card::CM; 21 | 22 | use strict; 23 | use warnings; 24 | 25 | use parent 'NEC2::Card'; 26 | 27 | use overload '""' => \&stringify; 28 | 29 | 30 | sub param_map 31 | { 32 | return { comment => 0 }; 33 | } 34 | 35 | 36 | # this seems cumbersome to override stringify, but CM is neither a geo_card nor a program_card 37 | # so ... this works to split the card into multiple lines. Maybe there should be 38 | # comment_cards() and is_comment_card() functions. 39 | sub stringify 40 | { 41 | my $self = shift; 42 | 43 | my @ret; 44 | if (defined($self->{card}) && defined($self->{card}->[0]) && $self->{card}->[0] =~ /[\r\n]/s) 45 | { 46 | foreach my $comment (split /[\r\n]/, $self->{card}->[0]) 47 | { 48 | push @ret, NEC2::Card::CM->new(comment => $comment); 49 | } 50 | } 51 | else 52 | { 53 | return $self->SUPER::stringify(); 54 | } 55 | 56 | return join('', @ret); 57 | } 58 | 59 | 1; 60 | 61 | 62 | # Nothing special about CE, so use CM. 63 | package NEC2::Card::CE; 64 | use parent 'NEC2::Card::CM'; 65 | 66 | 1; 67 | -------------------------------------------------------------------------------- /lib/NEC2/Card/EN.pm: -------------------------------------------------------------------------------- 1 | # This program is free software; you can redistribute it and/or modify 2 | # it under the terms of the GNU General Public License as published by 3 | # the Free Software Foundation; either version 3 of the License, or 4 | # (at your option) any later version. 5 | # 6 | # This program is distributed in the hope that it will be useful, 7 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 8 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 9 | # GNU Library General Public License for more details. 10 | # 11 | # You should have received a copy of the GNU General Public License 12 | # along with this program; if not, write to the Free Software 13 | # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. 14 | # 15 | # Copyright (C) 2021- by Eric Wheeler, KJ7LNW. All rights reserved. 16 | # 17 | # The official website and doumentation for xnec2c-optimizer is available here: 18 | # https://www.xnec2c.org/ 19 | # 20 | package NEC2::Card::EN; 21 | use parent 'NEC2::Card'; 22 | 23 | 1; 24 | 25 | -------------------------------------------------------------------------------- /lib/NEC2/Card/EX.pm: -------------------------------------------------------------------------------- 1 | # This program is free software; you can redistribute it and/or modify 2 | # it under the terms of the GNU General Public License as published by 3 | # the Free Software Foundation; either version 3 of the License, or 4 | # (at your option) any later version. 5 | # 6 | # This program is distributed in the hope that it will be useful, 7 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 8 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 9 | # GNU Library General Public License for more details. 10 | # 11 | # You should have received a copy of the GNU General Public License 12 | # along with this program; if not, write to the Free Software 13 | # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. 14 | # 15 | # Copyright (C) 2021- by Eric Wheeler, KJ7LNW. All rights reserved. 16 | # 17 | # The official website and doumentation for xnec2c-optimizer is available here: 18 | # https://www.xnec2c.org/ 19 | # 20 | package NEC2::Card::EX; 21 | 22 | use strict; 23 | use warnings; 24 | 25 | use parent 'NEC2::Card'; 26 | 27 | sub defaults 28 | { 29 | return (type => 0, ex_tag => 1, ex_segment => 1, v_real => 1, v_imag => 0); 30 | } 31 | 32 | 33 | # human-readable terms, some have multiple aliases for the same thing: 34 | sub param_map 35 | { 36 | return { 37 | 38 | type => 'i1', 39 | ex_tag => 'i2', 40 | 41 | ex_seg => 'i3', 42 | ex_segment => 'i3', 43 | 44 | v_real => 'f1', 45 | v_imag => 'f2', 46 | }; 47 | } 48 | 49 | 1; 50 | 51 | -------------------------------------------------------------------------------- /lib/NEC2/Card/FR.pm: -------------------------------------------------------------------------------- 1 | # This program is free software; you can redistribute it and/or modify 2 | # it under the terms of the GNU General Public License as published by 3 | # the Free Software Foundation; either version 3 of the License, or 4 | # (at your option) any later version. 5 | # 6 | # This program is distributed in the hope that it will be useful, 7 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 8 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 9 | # GNU Library General Public License for more details. 10 | # 11 | # You should have received a copy of the GNU General Public License 12 | # along with this program; if not, write to the Free Software 13 | # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. 14 | # 15 | # Copyright (C) 2021- by Eric Wheeler, KJ7LNW. All rights reserved. 16 | # 17 | # The official website and doumentation for xnec2c-optimizer is available here: 18 | # https://www.xnec2c.org/ 19 | # 20 | package NEC2::Card::FR; 21 | 22 | use strict; 23 | use warnings; 24 | 25 | use parent 'NEC2::Card'; 26 | 27 | # default to 10 steps in the 2m amature radio band: 28 | sub defaults 29 | { 30 | # note that NEC2 does not define mhz_max, it is calculated in the 31 | # overloaded set_special() function below. 32 | return (type => 0, n_freq => 10); 33 | } 34 | 35 | # human-readable terms, some have multiple aliases for the same thing: 36 | sub param_map 37 | { 38 | return { 39 | 40 | type => 'i1', 41 | n_freq => 'i2', 42 | mhz => 'f1', 43 | mhz_min => 'f1', 44 | 45 | mhz_inc => 'f2', 46 | mhz_step => 'f2', 47 | delfrq => 'f2', 48 | }; 49 | } 50 | 51 | sub get_special 52 | { 53 | my ($self, $var) = @_; 54 | 55 | if ($var eq 'mhz_max') 56 | { 57 | return $self->get('mhz_min') + ($self->get('n_freq')-1)*$self->get('mhz_inc'); 58 | } 59 | 60 | return undef; 61 | } 62 | 63 | sub set_special 64 | { 65 | my ($self, $var, $val) = @_; 66 | 67 | if ($var eq 'mhz_max') 68 | { 69 | $self->{mhz_max} = $val; 70 | } 71 | elsif ($var eq 'mhz_inc') 72 | { 73 | # If they specify mhz_inc, then mhz_max is (probably) no longer valid 74 | delete $self->{mhz_max}; 75 | 76 | # Still set the card var for mhz_inc: 77 | $self->set_card_var($var, $val); 78 | } 79 | elsif ($var eq 'mhz_min' || $var eq 'n_freq') 80 | { 81 | $self->set_card_var($var, $val); 82 | } 83 | else 84 | { 85 | return 0; 86 | } 87 | 88 | $self->_FR_update_mhz_min_max(); 89 | 90 | return 1; 91 | } 92 | 93 | sub _FR_update_mhz_min_max 94 | { 95 | my $self = shift; 96 | 97 | my $n_freq = $self->get('n_freq'); 98 | my $mhz_min = $self->get('mhz_min'); 99 | my $mhz_max = $self->{mhz_max}; 100 | 101 | if (defined($self->{mhz_max}) && defined($n_freq) && $n_freq > 1) 102 | { 103 | die "FR: mhz_min !< mhz_max: $mhz_min !< $mhz_max" if ($mhz_min >= $mhz_max); 104 | 105 | # set mhz_inc accordingly: 106 | $self->set_card_var('mhz_inc', ($mhz_max - $mhz_min) / ($n_freq-1)); 107 | } 108 | 109 | my $mhz_inc = $self->get('mhz_inc'); 110 | 111 | if ($n_freq > 1 && (!defined($mhz_inc) || $mhz_inc <= 0)) 112 | { 113 | $mhz_inc //= '(undef)'; 114 | die "FR: n_freq > 1, but mhz_inc is invalid: $mhz_inc"; 115 | } 116 | } 117 | 118 | 1; 119 | 120 | -------------------------------------------------------------------------------- /lib/NEC2/Card/GA.pm: -------------------------------------------------------------------------------- 1 | # This program is free software; you can redistribute it and/or modify 2 | # it under the terms of the GNU General Public License as published by 3 | # the Free Software Foundation; either version 3 of the License, or 4 | # (at your option) any later version. 5 | # 6 | # This program is distributed in the hope that it will be useful, 7 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 8 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 9 | # GNU Library General Public License for more details. 10 | # 11 | # You should have received a copy of the GNU General Public License 12 | # along with this program; if not, write to the Free Software 13 | # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. 14 | # 15 | # Copyright (C) 2021- by Eric Wheeler, KJ7LNW. All rights reserved. 16 | # 17 | # The official website and doumentation for xnec2c-optimizer is available here: 18 | # https://www.xnec2c.org/ 19 | # 20 | package NEC2::Card::GA; 21 | 22 | use strict; 23 | use warnings; 24 | 25 | use parent 'NEC2::Card'; 26 | 27 | sub defaults 28 | { 29 | # Defaults to 2mm wire, ~12 AWG 30 | return (rad => 0.002, ang1 => 0, ang2 => 360); 31 | } 32 | 33 | sub param_map 34 | { 35 | return { 36 | 37 | itg => 'i1', 38 | tag => 'i1', 39 | 40 | ns => 'i2', 41 | 42 | rada => 'f1', 43 | arc_rad => 'f1', 44 | 45 | ang1 => 'f2', 46 | ang2 => 'f3', 47 | 48 | rad => 'f4', 49 | wire_rad => 'f4', 50 | }; 51 | } 52 | 53 | 1; 54 | -------------------------------------------------------------------------------- /lib/NEC2/Card/GC.pm: -------------------------------------------------------------------------------- 1 | # This program is free software; you can redistribute it and/or modify 2 | # it under the terms of the GNU General Public License as published by 3 | # the Free Software Foundation; either version 3 of the License, or 4 | # (at your option) any later version. 5 | # 6 | # This program is distributed in the hope that it will be useful, 7 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 8 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 9 | # GNU Library General Public License for more details. 10 | # 11 | # You should have received a copy of the GNU General Public License 12 | # along with this program; if not, write to the Free Software 13 | # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. 14 | # 15 | # Copyright (C) 2021- by Eric Wheeler, KJ7LNW. All rights reserved. 16 | # 17 | # The official website and doumentation for xnec2c-optimizer is available here: 18 | # https://www.xnec2c.org/ 19 | # 20 | package NEC2::Card::GC; 21 | 22 | use strict; 23 | use warnings; 24 | 25 | use parent 'NEC2::Card'; 26 | 27 | sub param_map 28 | { 29 | return { 30 | 31 | # for tapered GC card 32 | rdel => 'f1', 33 | rad1 => 'f2', 34 | rad2 => 'f3' 35 | }; 36 | } 37 | 38 | 1; 39 | -------------------------------------------------------------------------------- /lib/NEC2/Card/GE.pm: -------------------------------------------------------------------------------- 1 | # This program is free software; you can redistribute it and/or modify 2 | # it under the terms of the GNU General Public License as published by 3 | # the Free Software Foundation; either version 3 of the License, or 4 | # (at your option) any later version. 5 | # 6 | # This program is distributed in the hope that it will be useful, 7 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 8 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 9 | # GNU Library General Public License for more details. 10 | # 11 | # You should have received a copy of the GNU General Public License 12 | # along with this program; if not, write to the Free Software 13 | # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. 14 | # 15 | # Copyright (C) 2021- by Eric Wheeler, KJ7LNW. All rights reserved. 16 | # 17 | # The official website and doumentation for xnec2c-optimizer is available here: 18 | # https://www.xnec2c.org/ 19 | # 20 | package NEC2::Card::GE; 21 | 22 | use strict; 23 | use warnings; 24 | 25 | use parent 'NEC2::Card'; 26 | 27 | sub defaults 28 | { 29 | # gpflag - Geometry ground plain flag. 30 | 31 | # 0 - no ground plane is present. 32 | # 1 - Indicates a ground plane is present. Structure symmetry is modified 33 | # as required, and the current expansion is modified so that the currents 34 | # an segments touching the ground (x, Y plane) are interpolated to their 35 | # images below the ground (charge at base is zero) 36 | # -1 - indicates a ground is present. Structure symmetry is modified as 37 | # required. Current expansion, however, is not modified, Thus, currents 38 | # on segments touching the ground will go to zero at the ground. 39 | return (gpflag => 0); 40 | } 41 | 42 | sub param_map 43 | { 44 | return { 45 | i1 => 0, 46 | 47 | gpflag => 'i1', 48 | ground => 'i1' 49 | }; 50 | } 51 | 52 | 1; 53 | 54 | -------------------------------------------------------------------------------- /lib/NEC2/Card/GH.pm: -------------------------------------------------------------------------------- 1 | # This program is free software; you can redistribute it and/or modify 2 | # it under the terms of the GNU General Public License as published by 3 | # the Free Software Foundation; either version 3 of the License, or 4 | # (at your option) any later version. 5 | # 6 | # This program is distributed in the hope that it will be useful, 7 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 8 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 9 | # GNU Library General Public License for more details. 10 | # 11 | # You should have received a copy of the GNU General Public License 12 | # along with this program; if not, write to the Free Software 13 | # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. 14 | # 15 | # Copyright (C) 2021- by Eric Wheeler, KJ7LNW. All rights reserved. 16 | # 17 | # The official website and doumentation for xnec2c-optimizer is available here: 18 | # https://www.xnec2c.org/ 19 | # 20 | package NEC2::Card::GH; 21 | 22 | use strict; 23 | use warnings; 24 | 25 | use parent 'NEC2::Card'; 26 | 27 | sub defaults 28 | { 29 | # Defaults to 2mm wire, ~12 AWG 30 | return (rad => 0.002, spiral_turns => 0); 31 | } 32 | 33 | sub param_map 34 | { 35 | return { 36 | 37 | itg => 'i1', 38 | tag => 'i1', 39 | 40 | ns => 'i2', 41 | 42 | s => 'f1', 43 | spacing => 'f1', 44 | 45 | hl => 'f2', 46 | length => 'f2', 47 | 48 | a1 => 'f3', 49 | rx1 => 'f3', 50 | 51 | b1 => 'f4', 52 | ry1 => 'f4', 53 | 54 | a2 => 'f5', 55 | rx2 => 'f5', 56 | 57 | b2 => 'f6', 58 | ry2 => 'f6', 59 | 60 | rad => 'f7', 61 | wire_rad => 'f7', 62 | }; 63 | } 64 | 65 | sub set_special 66 | { 67 | my ($self, $var, $val) = @_; 68 | 69 | return 0 unless ($var eq 'length' || $var eq 'ns' || $var eq 'spiral_turns'); 70 | 71 | if ($var eq 'length' || $var eq 'ns') 72 | { 73 | $self->set_card_var($var, $val); 74 | } 75 | elsif ($var eq 'spiral_turns') 76 | { 77 | # 'spiral_turns' is internal, so set it in the class 78 | $self->{spiral_turns} = $val; 79 | } 80 | 81 | # This will update when ns or spiral_turns is updated: 82 | if ($self->get('length') == 0 && 83 | $self->get('ns') > 0 && 84 | $self->{spiral_turns} > 0) 85 | { 86 | # The number of spiral spiral_turns is calculated 87 | # as spacing=$segments/$spiral_turns, but only when 88 | # length==0. Not sure if this is an xnec2c extension or 89 | # if other NEC2 interpreters support it, too: 90 | $self->set_card_var('spacing', $self->get('ns')/$self->{spiral_turns}); 91 | } 92 | 93 | return 1; 94 | } 95 | 96 | 1; 97 | -------------------------------------------------------------------------------- /lib/NEC2/Card/GM.pm: -------------------------------------------------------------------------------- 1 | # This program is free software; you can redistribute it and/or modify 2 | # it under the terms of the GNU General Public License as published by 3 | # the Free Software Foundation; either version 3 of the License, or 4 | # (at your option) any later version. 5 | # 6 | # This program is distributed in the hope that it will be useful, 7 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 8 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 9 | # GNU Library General Public License for more details. 10 | # 11 | # You should have received a copy of the GNU General Public License 12 | # along with this program; if not, write to the Free Software 13 | # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. 14 | # 15 | # Copyright (C) 2021- by Eric Wheeler, KJ7LNW. All rights reserved. 16 | # 17 | # The official website and doumentation for xnec2c-optimizer is available here: 18 | # https://www.xnec2c.org/ 19 | # 20 | package NEC2::Card::GM; 21 | 22 | use strict; 23 | use warnings; 24 | 25 | use parent 'NEC2::Card'; 26 | 27 | sub param_map 28 | { 29 | return { 30 | 31 | itsi => 'i1', 32 | nrpt => 'i2', 33 | rox => 'f1', 34 | roy => 'f2', 35 | roz => 'f3', 36 | xs => 'f4', 37 | ys => 'f5', 38 | zs => 'f6', 39 | its => 'f7', 40 | 41 | tag_inc => 'itsi', 42 | tag_start => 'its', 43 | 44 | # Number of copies 45 | new => 'nrpt', 46 | num => 'nrpt', 47 | n => 'nrpt', 48 | 49 | # Rotate about x/y/z 50 | rx => 'rox', 51 | ry => 'roy', 52 | rz => 'roz', 53 | 54 | # Translate x/y/z 55 | sx => 'xs', 56 | sy => 'ys', 57 | sz => 'zs', 58 | }; 59 | } 60 | 61 | 1; 62 | 63 | -------------------------------------------------------------------------------- /lib/NEC2/Card/GN.pm: -------------------------------------------------------------------------------- 1 | # This program is free software; you can redistribute it and/or modify 2 | # it under the terms of the GNU General Public License as published by 3 | # the Free Software Foundation; either version 3 of the License, or 4 | # (at your option) any later version. 5 | # 6 | # This program is distributed in the hope that it will be useful, 7 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 8 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 9 | # GNU Library General Public License for more details. 10 | # 11 | # You should have received a copy of the GNU General Public License 12 | # along with this program; if not, write to the Free Software 13 | # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. 14 | # 15 | # Copyright (C) 2021- by Eric Wheeler, KJ7LNW. All rights reserved. 16 | # 17 | # The official website and doumentation for xnec2c-optimizer is available here: 18 | # https://www.xnec2c.org/ 19 | # 20 | package NEC2::Card::GN; 21 | 22 | use strict; 23 | use warnings; 24 | 25 | use parent 'NEC2::Card'; 26 | 27 | sub defaults 28 | { 29 | # default to free-space 30 | return (type => -1); 31 | } 32 | 33 | sub param_map 34 | { 35 | return { 36 | type => 'i1', 37 | n_radials => 'i2', 38 | epse => 'f1', 39 | 40 | # ohms/meter 41 | sig => 'f2', 42 | conductivity => 'f2', 43 | 44 | # if n_radials > 0 45 | screen_rad => 'f3', 46 | screen_wire_rad => 'f4', 47 | 48 | # not sure about values for nradl=0, someone else please make human-readable values 49 | # or the user can just use f1-6 as appropriate 50 | }; 51 | } 52 | 53 | 1; 54 | -------------------------------------------------------------------------------- /lib/NEC2/Card/GR.pm: -------------------------------------------------------------------------------- 1 | # This program is free software; you can redistribute it and/or modify 2 | # it under the terms of the GNU General Public License as published by 3 | # the Free Software Foundation; either version 3 of the License, or 4 | # (at your option) any later version. 5 | # 6 | # This program is distributed in the hope that it will be useful, 7 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 8 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 9 | # GNU Library General Public License for more details. 10 | # 11 | # You should have received a copy of the GNU General Public License 12 | # along with this program; if not, write to the Free Software 13 | # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. 14 | # 15 | # Copyright (C) 2021- by Eric Wheeler, KJ7LNW. All rights reserved. 16 | # 17 | # The official website and doumentation for xnec2c-optimizer is available here: 18 | # https://www.xnec2c.org/ 19 | # 20 | package NEC2::Card::GR; 21 | 22 | use strict; 23 | use warnings; 24 | 25 | use parent 'NEC2::Card'; 26 | 27 | sub param_map 28 | { 29 | return { 30 | 31 | itsi => 'i1', 32 | nrpt => 'i2', 33 | n => 'i2', 34 | num => 'i2', 35 | }; 36 | } 37 | 38 | 1; 39 | 40 | -------------------------------------------------------------------------------- /lib/NEC2/Card/GS.pm: -------------------------------------------------------------------------------- 1 | # This program is free software; you can redistribute it and/or modify 2 | # it under the terms of the GNU General Public License as published by 3 | # the Free Software Foundation; either version 3 of the License, or 4 | # (at your option) any later version. 5 | # 6 | # This program is distributed in the hope that it will be useful, 7 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 8 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 9 | # GNU Library General Public License for more details. 10 | # 11 | # You should have received a copy of the GNU General Public License 12 | # along with this program; if not, write to the Free Software 13 | # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. 14 | # 15 | # Copyright (C) 2021- by Eric Wheeler, KJ7LNW. All rights reserved. 16 | # 17 | # The official website and doumentation for xnec2c-optimizer is available here: 18 | # https://www.xnec2c.org/ 19 | # 20 | package NEC2::Card::GS; 21 | 22 | use strict; 23 | use warnings; 24 | 25 | use parent 'NEC2::Card'; 26 | 27 | sub param_map 28 | { 29 | return { 30 | 31 | scale => 'f1', 32 | }; 33 | } 34 | 35 | 1; 36 | 37 | -------------------------------------------------------------------------------- /lib/NEC2/Card/GW.pm: -------------------------------------------------------------------------------- 1 | # This program is free software; you can redistribute it and/or modify 2 | # it under the terms of the GNU General Public License as published by 3 | # the Free Software Foundation; either version 3 of the License, or 4 | # (at your option) any later version. 5 | # 6 | # This program is distributed in the hope that it will be useful, 7 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 8 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 9 | # GNU Library General Public License for more details. 10 | # 11 | # You should have received a copy of the GNU General Public License 12 | # along with this program; if not, write to the Free Software 13 | # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. 14 | # 15 | # Copyright (C) 2021- by Eric Wheeler, KJ7LNW. All rights reserved. 16 | # 17 | # The official website and doumentation for xnec2c-optimizer is available here: 18 | # https://www.xnec2c.org/ 19 | # 20 | package NEC2::Card::GW; 21 | 22 | use strict; 23 | use warnings; 24 | 25 | use parent 'NEC2::Card'; 26 | 27 | # this is a multi-card card when rad = 0, see the GC card documented on the GW page. 28 | # GC is added below in the overloaded geo_cards() function. 29 | 30 | sub defaults 31 | { 32 | # Defaults to 2mm wire, ~12 AWG 33 | return (rad => 0.002, ns => 1); 34 | } 35 | 36 | sub param_map 37 | { 38 | return { 39 | 40 | # original NEC2 terms: 41 | itg => 'i1', 42 | ns => 'i2', 43 | xw1 => 'f1', 44 | yw1 => 'f2', 45 | zw1 => 'f3', 46 | xw2 => 'f4', 47 | yw2 => 'f5', 48 | zw2 => 'f6', 49 | rad => 'f7', 50 | 51 | # shorthand: 52 | tag => 'itg', 53 | x => 'xw1', 54 | x1 => 'xw1', 55 | 56 | y => 'yw1', 57 | y1 => 'yw1', 58 | 59 | z => 'zw1', 60 | z1 => 'zw1', 61 | 62 | x2 => 'xw2', 63 | y2 => 'yw2', 64 | z2 => 'zw2', 65 | 66 | wire_rad => 'f7', 67 | rad => 'f7', 68 | }; 69 | } 70 | 71 | sub geo_cards 72 | { 73 | my $self = shift; 74 | 75 | my @cards = ($self); 76 | 77 | # if rad == 0 then there must be a GC card. 78 | if ($self->get('rad') == 0) 79 | { 80 | if (!$self->{rdel} || $self->{rad1} || $self->{rad2}) 81 | { 82 | die "GW: rdel, rad1, and rad2 must be defined when rad=0"; 83 | } 84 | 85 | push @cards, 86 | NEC2::Card::GC->new( 87 | rdel => $self->{rdel}, 88 | rad1 => $self->{rad1}, 89 | rad2 => $self->{rad2}); 90 | 91 | } 92 | 93 | return @cards; 94 | 95 | } 96 | 97 | sub set_special 98 | { 99 | my ($self, $var, $val) = @_; 100 | 101 | # [ [x1,y1,z1] => [x2,y2,z2] ] 102 | if ($var eq 'points') 103 | { 104 | $self->set_card_var("x1", $val->[0]->[0]); 105 | $self->set_card_var("y1", $val->[0]->[1]); 106 | $self->set_card_var("z1", $val->[0]->[2]); 107 | 108 | $self->set_card_var("x2", $val->[1]->[0]); 109 | $self->set_card_var("y2", $val->[1]->[1]); 110 | $self->set_card_var("z2", $val->[1]->[2]); 111 | 112 | return 1; 113 | } 114 | 115 | return 0; 116 | } 117 | 118 | sub get_points 119 | { 120 | my $self = shift; 121 | return ( 122 | [ $self->get_card_var('x1'), $self->get_card_var('y1'), $self->get_card_var('z1')], 123 | 124 | [ $self->get_card_var('x2'), $self->get_card_var('y2'), $self->get_card_var('z2') ] 125 | ); 126 | } 127 | 128 | 1; 129 | -------------------------------------------------------------------------------- /lib/NEC2/Card/GX.pm: -------------------------------------------------------------------------------- 1 | # This program is free software; you can redistribute it and/or modify 2 | # it under the terms of the GNU General Public License as published by 3 | # the Free Software Foundation; either version 3 of the License, or 4 | # (at your option) any later version. 5 | # 6 | # This program is distributed in the hope that it will be useful, 7 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 8 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 9 | # GNU Library General Public License for more details. 10 | # 11 | # You should have received a copy of the GNU General Public License 12 | # along with this program; if not, write to the Free Software 13 | # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. 14 | # 15 | # Copyright (C) 2021- by Eric Wheeler, KJ7LNW. All rights reserved. 16 | # 17 | # The official website and doumentation for xnec2c-optimizer is available here: 18 | # https://www.xnec2c.org/ 19 | # 20 | package NEC2::Card::GX; 21 | 22 | use strict; 23 | use warnings; 24 | 25 | use parent 'NEC2::Card'; 26 | 27 | sub param_map 28 | { 29 | return { 30 | 31 | itsi => 'i1', 32 | planes => 'i2', 33 | }; 34 | } 35 | 36 | 1; 37 | 38 | -------------------------------------------------------------------------------- /lib/NEC2/Card/LD.pm: -------------------------------------------------------------------------------- 1 | # This program is free software; you can redistribute it and/or modify 2 | # it under the terms of the GNU General Public License as published by 3 | # the Free Software Foundation; either version 3 of the License, or 4 | # (at your option) any later version. 5 | # 6 | # This program is distributed in the hope that it will be useful, 7 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 8 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 9 | # GNU Library General Public License for more details. 10 | # 11 | # You should have received a copy of the GNU General Public License 12 | # along with this program; if not, write to the Free Software 13 | # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. 14 | # 15 | # Copyright (C) 2021- by Eric Wheeler, KJ7LNW. All rights reserved. 16 | # 17 | # The official website and doumentation for xnec2c-optimizer is available here: 18 | # https://www.xnec2c.org/ 19 | # 20 | package NEC2::Card::LD; 21 | 22 | use strict; 23 | use warnings; 24 | 25 | use parent 'NEC2::Card'; 26 | 27 | sub defaults 28 | { 29 | return (); 30 | } 31 | 32 | 33 | # human-readable terms, some have multiple aliases for the same thing: 34 | sub param_map 35 | { 36 | return { 37 | ldtyp => 'i1', 38 | type => 'i1', 39 | 40 | ldtag => 'i2', 41 | ld_tag => 'i2', 42 | 43 | ldtagf => 'i3', 44 | ld_tagf => 'i3', 45 | 46 | ldtagt => 'i4', 47 | ld_tagt => 'i4', 48 | 49 | zlr => 'f1', 50 | zli => 'f2', 51 | zlc => 'f3', 52 | }; 53 | } 54 | 55 | 1; 56 | 57 | -------------------------------------------------------------------------------- /lib/NEC2/Card/NE.pm: -------------------------------------------------------------------------------- 1 | # This program is free software; you can redistribute it and/or modify 2 | # it under the terms of the GNU General Public License as published by 3 | # the Free Software Foundation; either version 3 of the License, or 4 | # (at your option) any later version. 5 | # 6 | # This program is distributed in the hope that it will be useful, 7 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 8 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 9 | # GNU Library General Public License for more details. 10 | # 11 | # You should have received a copy of the GNU General Public License 12 | # along with this program; if not, write to the Free Software 13 | # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. 14 | # 15 | # Copyright (C) 2021- by Eric Wheeler, KJ7LNW. All rights reserved. 16 | # 17 | # The official website and doumentation for xnec2c-optimizer is available here: 18 | # https://www.xnec2c.org/ 19 | # 20 | package NEC2::Card::NE; 21 | 22 | use strict; 23 | use warnings; 24 | 25 | use parent 'NEC2::Card'; 26 | 27 | sub defaults 28 | { 29 | # default rectangular coordinates: 30 | return (near => 0); 31 | } 32 | 33 | sub param_map 34 | { 35 | return { 36 | near => 'i1', 37 | 38 | # rectangular coordinates (near = 0): 39 | nrx => 'i2', 40 | nry => 'i3', 41 | nrz => 'i4', 42 | 43 | xnr => 'f1', 44 | ynr => 'f2', 45 | znr => 'f3', 46 | 47 | dxnr => 'f4', 48 | dynr => 'f5', 49 | dznr => 'f6', 50 | 51 | # spherical coordinates (near = 1): 52 | n_r => 'i2', 53 | n_phi => 'i3', 54 | n_theta => 'i4', 55 | 56 | pos_r => 'f1', 57 | pos_phi => 'f2', 58 | pos_theta => 'f3', 59 | 60 | step_r => 'f4', 61 | step_phi => 'f5', 62 | step_theta => 'f6', 63 | 64 | }; 65 | } 66 | 67 | 1; 68 | 69 | package NEC2::Card::NH; 70 | 71 | use parent 'NEC2::Card::NE'; 72 | 1; 73 | -------------------------------------------------------------------------------- /lib/NEC2/Card/NT.pm: -------------------------------------------------------------------------------- 1 | # This program is free software; you can redistribute it and/or modify 2 | # it under the terms of the GNU General Public License as published by 3 | # the Free Software Foundation; either version 3 of the License, or 4 | # (at your option) any later version. 5 | # 6 | # This program is distributed in the hope that it will be useful, 7 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 8 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 9 | # GNU Library General Public License for more details. 10 | # 11 | # You should have received a copy of the GNU General Public License 12 | # along with this program; if not, write to the Free Software 13 | # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. 14 | # 15 | # Copyright (C) 2021- by Eric Wheeler, KJ7LNW. All rights reserved. 16 | # 17 | # The official website and doumentation for xnec2c-optimizer is available here: 18 | # https://www.xnec2c.org/ 19 | # 20 | package NEC2::Card::NT; 21 | 22 | use strict; 23 | use warnings; 24 | 25 | use parent 'NEC2::Card'; 26 | 27 | sub defaults 28 | { 29 | } 30 | 31 | 32 | # human-readable terms, some have multiple aliases for the same thing: 33 | sub param_map 34 | { 35 | return { 36 | tag_p1 => 'i1', 37 | seg_p1 => 'i2', 38 | tag_p2 => 'i3', 39 | seg_p2 => 'i4', 40 | 41 | y11r => 'f1', 42 | y11i => 'f2', 43 | 44 | y12r => 'f3', 45 | y12i => 'f4', 46 | 47 | # The admittance matrix is symmetric so it is unnecessary to specify element (2, 1). 48 | # so this is an alias: 49 | y21r => 'f3', 50 | y21i => 'f4', 51 | 52 | y22r => 'f5', 53 | y22i => 'f6', 54 | }; 55 | } 56 | 57 | 1; 58 | 59 | -------------------------------------------------------------------------------- /lib/NEC2/Card/RP.pm: -------------------------------------------------------------------------------- 1 | # This program is free software; you can redistribute it and/or modify 2 | # it under the terms of the GNU General Public License as published by 3 | # the Free Software Foundation; either version 3 of the License, or 4 | # (at your option) any later version. 5 | # 6 | # This program is distributed in the hope that it will be useful, 7 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 8 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 9 | # GNU Library General Public License for more details. 10 | # 11 | # You should have received a copy of the GNU General Public License 12 | # along with this program; if not, write to the Free Software 13 | # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. 14 | # 15 | # Copyright (C) 2021- by Eric Wheeler, KJ7LNW. All rights reserved. 16 | # 17 | # The official website and doumentation for xnec2c-optimizer is available here: 18 | # https://www.xnec2c.org/ 19 | # 20 | package NEC2::Card::RP; 21 | 22 | use strict; 23 | use warnings; 24 | 25 | use parent 'NEC2::Card'; 26 | 27 | # Note: see below for NEC2::Card::RP::Freespace and NEC2::Card::RP::Ground 28 | 29 | sub defaults 30 | { 31 | return (ground => 0); 32 | } 33 | 34 | sub set_special 35 | { 36 | my ($self, $var, $val) = @_; 37 | 38 | # If ground is nonzero then use the defaults for having a ground. 39 | # Setting 'ground' will reset all other defaults so set it first 40 | # before changing other values. 41 | if ($var eq 'ground') 42 | { 43 | if ($val) 44 | { 45 | $self->set(NEC2::Card::RP::Ground::defaults()); 46 | } 47 | else 48 | { 49 | $self->set(NEC2::Card::RP::Freespace::defaults()); 50 | } 51 | 52 | return 1; 53 | } 54 | 55 | return 0; 56 | } 57 | 58 | # short-hand terms: 59 | sub param_map 60 | { 61 | return { 62 | 63 | type => 'i1', 64 | 65 | nth => 'i2', 66 | n_theta => 'i2', 67 | 68 | nph => 'i3', 69 | n_phi => 'i3', 70 | 71 | xnda => 'i4', 72 | 73 | thets => 'f1', 74 | theta_initial => 'f1', 75 | 76 | phis => 'f2', 77 | phi_initial => 'f2', 78 | 79 | dth => 'f3', 80 | theta_inc => 'f3', 81 | 82 | dph => 'f4', 83 | phi_inc => 'f4', 84 | 85 | rfld => 'f5', 86 | gnor => 'f6', 87 | }; 88 | } 89 | 90 | 1; 91 | 92 | 93 | # No ground, 360-degrees of phi (same as above) 94 | package NEC2::Card::RP::Freespace; 95 | use parent 'NEC2::Card::RP'; 96 | 97 | sub defaults 98 | { 99 | return ( 100 | type => 0, 101 | n_theta => 19, 102 | n_phi => 37, 103 | xnda => '1000', 104 | theta_initial => 0, 105 | phi_initial => 0, 106 | theta_inc => 10, 107 | phi_inc => 10); 108 | } 109 | 110 | 1; 111 | 112 | 113 | # With ground, only 180-degrees of theta 114 | package NEC2::Card::RP::Ground; 115 | use parent 'NEC2::Card::RP'; 116 | 117 | sub defaults 118 | { 119 | return ( 120 | type => 0, 121 | n_theta => 19, 122 | n_phi => 37, 123 | xnda => '1000', 124 | theta_initial => 0, 125 | phi_initial => 0, 126 | theta_inc => 5, 127 | phi_inc => 10); 128 | } 129 | 130 | 1; 131 | 132 | -------------------------------------------------------------------------------- /lib/NEC2/Card/SC.pm: -------------------------------------------------------------------------------- 1 | # This program is free software; you can redistribute it and/or modify 2 | # it under the terms of the GNU General Public License as published by 3 | # the Free Software Foundation; either version 3 of the License, or 4 | # (at your option) any later version. 5 | # 6 | # This program is distributed in the hope that it will be useful, 7 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 8 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 9 | # GNU Library General Public License for more details. 10 | # 11 | # You should have received a copy of the GNU General Public License 12 | # along with this program; if not, write to the Free Software 13 | # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. 14 | # 15 | # Copyright (C) 2021- by Eric Wheeler, KJ7LNW. All rights reserved. 16 | # 17 | # The official website and doumentation for xnec2c-optimizer is available here: 18 | # https://www.xnec2c.org/ 19 | # 20 | package NEC2::Card::SC; 21 | 22 | use strict; 23 | use warnings; 24 | 25 | use parent 'NEC2::Card'; 26 | 27 | 28 | sub param_map 29 | { 30 | return { 31 | 32 | # used by SP, not SM: 33 | ns => 'i2', 34 | shape => 'i2', 35 | 36 | # used by SP and SM: 37 | x3 => 'f1', 38 | y3 => 'f2', 39 | z3 => 'f3', 40 | 41 | # used by SP only: 42 | x4 => 'f4', 43 | y4 => 'f5', 44 | z4 => 'f6', 45 | }; 46 | } 47 | 48 | 1; 49 | -------------------------------------------------------------------------------- /lib/NEC2/Card/SM.pm: -------------------------------------------------------------------------------- 1 | # This program is free software; you can redistribute it and/or modify 2 | # it under the terms of the GNU General Public License as published by 3 | # the Free Software Foundation; either version 3 of the License, or 4 | # (at your option) any later version. 5 | # 6 | # This program is distributed in the hope that it will be useful, 7 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 8 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 9 | # GNU Library General Public License for more details. 10 | # 11 | # You should have received a copy of the GNU General Public License 12 | # along with this program; if not, write to the Free Software 13 | # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. 14 | # 15 | # Copyright (C) 2021- by Eric Wheeler, KJ7LNW. All rights reserved. 16 | # 17 | # The official website and doumentation for xnec2c-optimizer is available here: 18 | # https://www.xnec2c.org/ 19 | # 20 | package NEC2::Card::SM; 21 | 22 | use strict; 23 | use warnings; 24 | 25 | use NEC2::Card::SC; 26 | use parent 'NEC2::Card'; 27 | 28 | 29 | sub param_map 30 | { 31 | return { 32 | 33 | nx => 'i1', 34 | ny => 'i2', 35 | 36 | x1 => 'f1', 37 | y1 => 'f2', 38 | z1 => 'f3', 39 | 40 | x2 => 'f4', 41 | y2 => 'f5', 42 | z2 => 'f6', 43 | }; 44 | } 45 | 46 | sub get_special 47 | { 48 | my ($self, $var) = @_; 49 | 50 | if ($var =~ /^[xyz]3$/) 51 | { 52 | return $self->{$var} 53 | } 54 | 55 | return undef; 56 | } 57 | 58 | sub set_special 59 | { 60 | my ($self, $var, $val) = @_; 61 | 62 | if ($var =~ /^[xyz]3$/) 63 | { 64 | $self->{$var} = $val; 65 | return 1; 66 | } 67 | 68 | return 0; 69 | } 70 | 71 | sub geo_cards 72 | { 73 | my $self = shift; 74 | 75 | return ($self, 76 | NEC2::Card::SC->new( 77 | x3 => $self->{x3}, 78 | y3 => $self->{y3}, 79 | z3 => $self->{z3}, 80 | ) 81 | ); 82 | } 83 | 84 | 1; 85 | -------------------------------------------------------------------------------- /lib/NEC2/Card/SP.pm: -------------------------------------------------------------------------------- 1 | # This program is free software; you can redistribute it and/or modify 2 | # it under the terms of the GNU General Public License as published by 3 | # the Free Software Foundation; either version 3 of the License, or 4 | # (at your option) any later version. 5 | # 6 | # This program is distributed in the hope that it will be useful, 7 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 8 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 9 | # GNU Library General Public License for more details. 10 | # 11 | # You should have received a copy of the GNU General Public License 12 | # along with this program; if not, write to the Free Software 13 | # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. 14 | # 15 | # Copyright (C) 2021- by Eric Wheeler, KJ7LNW. All rights reserved. 16 | # 17 | # The official website and doumentation for xnec2c-optimizer is available here: 18 | # https://www.xnec2c.org/ 19 | # 20 | package NEC2::Card::SP; 21 | 22 | use strict; 23 | use warnings; 24 | 25 | use parent 'NEC2::Card'; 26 | 27 | 28 | sub param_map 29 | { 30 | return { 31 | 32 | ns => 'i2', 33 | shape => 'i2', 34 | 35 | x1 => 'f1', 36 | y1 => 'f2', 37 | z1 => 'f3', 38 | 39 | x2 => 'f4', 40 | elevation => 'f4', 41 | 42 | y2 => 'f5', 43 | azimuth => 'f5', 44 | 45 | z2 => 'f6', 46 | area => 'f6', 47 | 48 | }; 49 | } 50 | 51 | sub geo_cards 52 | { 53 | my $self = shift; 54 | 55 | 56 | # if ns is 1,2,3 then a second SC card exists: 57 | if ($self->get('ns') =~ /^[12]$/) 58 | { 59 | if ( 60 | !$self->{x3} || 61 | !$self->{y3} || 62 | !$self->{z3}) 63 | { 64 | die "GW: x3, y3, and z3 must be defined when ns is 2, or 3"; 65 | } 66 | } 67 | 68 | if ($self->get('ns') == 3) 69 | { 70 | if ( 71 | !$self->{x4} || 72 | !$self->{y4} || 73 | !$self->{z4}) 74 | { 75 | die "GW: x4, y4, and z4 must be defined when ns is 3"; 76 | } 77 | } 78 | 79 | 80 | my @cards = ($self); 81 | 82 | if ($self->get('ns') =~ /^[123]$/) 83 | { 84 | push @cards, 85 | NEC2::Card::SC->new( 86 | i2 => $self->get('ns'), 87 | x3 => $self->{x3}, 88 | y3 => $self->{y3}, 89 | z3 => $self->{z3}, 90 | 91 | x4 => $self->{x4}, 92 | y4 => $self->{y4}, 93 | z4 => $self->{z4}, 94 | ); 95 | 96 | } 97 | 98 | return @cards; 99 | } 100 | 101 | sub get_special 102 | { 103 | my ($self, $var) = @_; 104 | 105 | if ($var =~ /^[xyz][34]$/) 106 | { 107 | return $self->{$var} 108 | } 109 | 110 | return undef; 111 | } 112 | 113 | sub set_special 114 | { 115 | my ($self, $var, $val) = @_; 116 | 117 | if ($var =~ /^[xyz][34]$/) 118 | { 119 | $self->{$var} = $val; 120 | return 1; 121 | } 122 | 123 | return 0; 124 | } 125 | 126 | 1; 127 | -------------------------------------------------------------------------------- /lib/NEC2/Card/TL.pm: -------------------------------------------------------------------------------- 1 | # This program is free software; you can redistribute it and/or modify 2 | # it under the terms of the GNU General Public License as published by 3 | # the Free Software Foundation; either version 3 of the License, or 4 | # (at your option) any later version. 5 | # 6 | # This program is distributed in the hope that it will be useful, 7 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 8 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 9 | # GNU Library General Public License for more details. 10 | # 11 | # You should have received a copy of the GNU General Public License 12 | # along with this program; if not, write to the Free Software 13 | # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. 14 | # 15 | # Copyright (C) 2021- by Eric Wheeler, KJ7LNW. All rights reserved. 16 | # 17 | # The official website and doumentation for xnec2c-optimizer is available here: 18 | # https://www.xnec2c.org/ 19 | # 20 | package NEC2::Card::TL; 21 | 22 | use strict; 23 | use warnings; 24 | 25 | use parent 'NEC2::Card'; 26 | 27 | sub defaults 28 | { 29 | return (z0 => 50); 30 | } 31 | 32 | 33 | # human-readable terms, some have multiple aliases for the same thing: 34 | sub param_map 35 | { 36 | return { 37 | tag_p1 => 'i1', 38 | seg_p1 => 'i2', 39 | tag_p2 => 'i3', 40 | seg_p2 => 'i4', 41 | 42 | z0 => 'f1', 43 | length => 'f2', 44 | shunt_re_p1 => 'f3', 45 | shunt_im_p1 => 'f4', 46 | shunt_re_p2 => 'f5', 47 | shunt_im_p2 => 'f6', 48 | }; 49 | } 50 | 51 | 1; 52 | 53 | -------------------------------------------------------------------------------- /lib/NEC2/Card/ZO.pm: -------------------------------------------------------------------------------- 1 | # This program is free software; you can redistribute it and/or modify 2 | # it under the terms of the GNU General Public License as published by 3 | # the Free Software Foundation; either version 3 of the License, or 4 | # (at your option) any later version. 5 | # 6 | # This program is distributed in the hope that it will be useful, 7 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 8 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 9 | # GNU Library General Public License for more details. 10 | # 11 | # You should have received a copy of the GNU General Public License 12 | # along with this program; if not, write to the Free Software 13 | # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. 14 | # 15 | # Copyright (C) 2021- by Eric Wheeler, KJ7LNW. All rights reserved. 16 | # 17 | # The official website and doumentation for xnec2c-optimizer is available here: 18 | # https://www.xnec2c.org/ 19 | # 20 | # Charectaristic Impedance: xnec2c extension: https://www.xnec2c.org/#InputFile 21 | package NEC2::Card::ZO; 22 | 23 | use strict; 24 | use warnings; 25 | 26 | use parent 'NEC2::Card'; 27 | 28 | sub defaults 29 | { 30 | return (z0 => 50); 31 | } 32 | 33 | 34 | # human-readable terms, some have multiple aliases for the same thing: 35 | sub param_map 36 | { 37 | return { 38 | z0 => 'i1', 39 | zo => 'i1', 40 | }; 41 | } 42 | 43 | 1; 44 | 45 | -------------------------------------------------------------------------------- /lib/NEC2/Coax.pm: -------------------------------------------------------------------------------- 1 | # This program is free software; you can redistribute it and/or modify 2 | # it under the terms of the GNU General Public License as published by 3 | # the Free Software Foundation; either version 3 of the License, or 4 | # (at your option) any later version. 5 | # 6 | # This program is distributed in the hope that it will be useful, 7 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 8 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 9 | # GNU Library General Public License for more details. 10 | # 11 | # You should have received a copy of the GNU General Public License 12 | # along with this program; if not, write to the Free Software 13 | # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. 14 | # 15 | # Copyright (C) 2021- by Eric Wheeler, KJ7LNW. All rights reserved. 16 | # 17 | # The official website and doumentation for xnec2c-optimizer is available here: 18 | # https://www.xnec2c.org/ 19 | # 20 | package NEC2::Coax; 21 | 22 | use parent 'NEC2::Shape'; 23 | 24 | use strict; 25 | use warnings; 26 | 27 | use NEC2; 28 | 29 | use Math::Trig qw/:radial pi rad2deg deg2rad/; 30 | 31 | sub defaults 32 | { 33 | return ( 34 | tag => 1, 35 | z0 => 50, 36 | er => 1, # NEC2 doesn't support non-freespace Er values (but NEC4/5 do?) 37 | ns => 21, # number of segments across the coax core and shield 38 | n_circles => 20, # Number of shield circles drawn along coax. 39 | n_shield => 8, # Number of wires drawn as the outer coax shield 40 | r_inner => undef, # radius of inner conductor 41 | r_outer => undef, # will be calculated 42 | shield_scale => 0.1, # scale the shielding wire by 1/10 of r_inner. 43 | terminate => 0, # apply a terminating resistor? 44 | excite => 0, # apply excitation to {tag}? 45 | ); 46 | } 47 | 48 | 49 | # points => [ [x,y,z], [x,y,z], [x,y,z], ... ] 50 | sub new 51 | { 52 | my ($class, %args) = @_; 53 | my $self = bless(\%args, $class); 54 | 55 | my %defaults = $self->defaults; 56 | foreach my $k (keys %defaults) 57 | { 58 | $self->{$k} //= $defaults{$k}; 59 | } 60 | 61 | $self->gen_cards; 62 | 63 | return $self; 64 | } 65 | 66 | sub gen_coax 67 | { 68 | my ($self, $p1, $p2, $do_excite, $do_terminate) = @_; 69 | 70 | my @coax; 71 | 72 | my $vec = [ ($p2->[0] - $p1->[0]), ($p2->[1] - $p1->[1]), ($p2->[2] - $p1->[2]) ]; 73 | my ($rho, $theta, $phi) = cartesian_to_spherical(@$vec); 74 | $theta = rad2deg($theta); 75 | $phi = rad2deg($phi); 76 | 77 | my $nwires = $self->{n_shield}; 78 | my $ncircles = $self->{n_circles}; 79 | my $coax_len = sqrt($vec->[0]**2 + $vec->[1]**2 + $vec->[2]**2); 80 | my $ns = $self->{ns}; # number of segments 81 | 82 | my $z0 = $self->{z0}; 83 | my $er = $self->{er}; 84 | 85 | # Given inner, find outer at $z0: 86 | my $ri = $self->{r_inner}; 87 | my $ro = $self->{r_outer}; 88 | my ($d, $D); 89 | 90 | if (defined($ri) && !defined($ro)) 91 | { 92 | $d = $ri*2; 93 | $D = _coax_solve_outer($d, $z0, $er); 94 | $self->{r_outer} = $D/2; 95 | } 96 | elsif (!defined($ri) && defined($ro)) 97 | { 98 | $D = $ro*2; 99 | $d = _coax_solve_inner($D, $z0, $er); 100 | $self->{r_inner} = $d/2; 101 | } 102 | elsif (!defined($ri) && !defined($ro)) 103 | { 104 | die "You must specify either r_inner or r_outer"; 105 | } 106 | else 107 | { 108 | $d = $ri*2; 109 | $D = $ro*2; 110 | } 111 | 112 | print "d=$d D=$D\n"; 113 | my $shield_rad = $d * $self->{shield_scale}; 114 | 115 | my $first_tag = $self->{_tag}; 116 | 117 | # Excitation? 118 | if ($do_excite) 119 | { 120 | push @coax, 121 | GW( tag => $self->{_tag}++, ns => 1, wire_rad => $d/2, z2 => $D/2); 122 | 123 | push @{ $self->{_program_cards} }, 124 | EX( ex_tag => $self->{_tag}-1, ex_seg => 1 ); 125 | } 126 | 127 | # Center conductor: 128 | push @coax, GW( tag => $self->{_tag}++, ns => $ns, wire_rad => $d/2, x2 => $coax_len); 129 | 130 | # Shielding wires: 131 | push @coax, GW( tag => $self->{_tag}++, ns => $ns, wire_rad => $shield_rad, z1 => -$D/2, z2 => -$D/2, x2 => $coax_len); 132 | push @coax, GM( tag_start => $self->{_tag}-1, nrpt => $nwires-1, rox => 360/$nwires); 133 | 134 | # Shielding circles: For the arc, NS is the number of shielding wires so the 135 | # polygon lines up to the shape created by the wires. 136 | push @coax, GA( tag => $self->{_tag}++, ns => $nwires, wire_rad => $shield_rad, rada => $D/2); 137 | push @coax, GM( tag_start => $self->{_tag}-1, roz => 90); 138 | push @coax, GM( tag_start => $self->{_tag}-1, nrpt => $ncircles, sx => $coax_len/$ncircles); 139 | 140 | #GW(tag => $self->{_tag}++, points => [[0,0,0]=>$p1]); 141 | #GW(tag => $self->{_tag}++, points => [[0,0,0]=>$p2]); 142 | 143 | # Terminating resistor? 144 | if ($do_terminate) 145 | { 146 | push @coax, 147 | GW( tag => $self->{_tag}++, ns => 1, wire_rad => $d/2, 148 | x1 => $coax_len, x2 => $coax_len, z2 => $D/2); 149 | 150 | push @{ $self->{_program_cards} }, 151 | LD( ldtag => $self->{_tag}-1, type => 0, zlr => 50); 152 | } 153 | 154 | # Rotate into position 155 | push @coax, GM( tag_start => $first_tag, roy => - (90-$phi), roz => $theta, 156 | sx => $p1->[0], sy => $p1->[1], sz => $p1->[2]); 157 | 158 | push @{ $self->{_geo_cards}}, @coax; 159 | } 160 | 161 | sub gen_cards 162 | { 163 | my $self = shift; 164 | 165 | my (@geo, @program); 166 | my $prev; 167 | 168 | die "Must have at least 2 points" if (scalar(@{ $self->{points} }) < 2); 169 | 170 | $self->{_tag} = $self->{tag}; 171 | 172 | my $first = 1; 173 | my @points = @{ $self->{points} }; 174 | while (my $point = shift @points) 175 | { 176 | print join(", ", @$point) . "\n"; 177 | if ($prev) 178 | { 179 | # All parameters in $self except 'points' and 'tag' are passed to GW: 180 | 181 | $self->gen_coax($prev => $point, 182 | $first && $self->{excite}, 183 | !@points && $self->{terminate}); 184 | 185 | $first = 0; 186 | } 187 | $prev = $point; 188 | 189 | } 190 | } 191 | 192 | # Formulas from: https://www.everythingrf.com/rf-calculators/coaxial-cable-calculator 193 | # Given outer diameter, Z0 and Er, return the inner diameter 194 | sub _coax_solve_inner 195 | { 196 | my ($D, $z0, $er) = @_; 197 | return $D / (10**($z0*sqrt($er)/138)); 198 | } 199 | 200 | # Given inner diameter, Z0 and Er, return the outer diameter 201 | sub _coax_solve_outer 202 | { 203 | my ($d, $z0, $er) = @_; 204 | return ($d*10**($z0*sqrt($er)/138)); 205 | } 206 | 207 | # Given inner and outer diameter, and Er, return the cutoff frequency in MHz. 208 | sub cutoff_mhz 209 | { 210 | my $self = shift; 211 | my ($d, $D, $er) = ($self->{r_inner}*2, $self->{r_outer}*2, $self->{er}); 212 | return 1e3 * 11.8/(sqrt($er)*pi*(($D+$d)/2)); 213 | } 214 | 215 | sub geo_cards 216 | { 217 | my $self = shift; 218 | 219 | return @{ $self->{_geo_cards} }; 220 | } 221 | 222 | sub program_cards 223 | { 224 | my $self = shift; 225 | 226 | return @{ $self->{_program_cards} // [] }; 227 | } 228 | 229 | 1; 230 | -------------------------------------------------------------------------------- /lib/NEC2/Polyline.pm: -------------------------------------------------------------------------------- 1 | # This program is free software; you can redistribute it and/or modify 2 | # it under the terms of the GNU General Public License as published by 3 | # the Free Software Foundation; either version 3 of the License, or 4 | # (at your option) any later version. 5 | # 6 | # This program is distributed in the hope that it will be useful, 7 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 8 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 9 | # GNU Library General Public License for more details. 10 | # 11 | # You should have received a copy of the GNU General Public License 12 | # along with this program; if not, write to the Free Software 13 | # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. 14 | # 15 | # Copyright (C) 2021- by Eric Wheeler, KJ7LNW. All rights reserved. 16 | # 17 | # The official website and doumentation for xnec2c-optimizer is available here: 18 | # https://www.xnec2c.org/ 19 | # 20 | package NEC2::Polyline; 21 | 22 | use parent 'NEC2::Shape'; 23 | 24 | use strict; 25 | use warnings; 26 | 27 | use NEC2; 28 | 29 | 30 | sub defaults 31 | { 32 | return ( ); 33 | } 34 | 35 | 36 | # points => [ [x,y,z], [x,y,z], [x,y,z], ... ] 37 | sub new 38 | { 39 | my ($class, %args) = @_; 40 | my $self = bless(\%args, $class); 41 | 42 | my %defaults = $self->defaults; 43 | foreach my $k (keys %defaults) 44 | { 45 | $self->{$k} //= $defaults{$k}; 46 | } 47 | 48 | $self->gen_cards; 49 | 50 | return $self; 51 | } 52 | 53 | 54 | sub gen_cards 55 | { 56 | my $self = shift; 57 | 58 | my (@geo, @program); 59 | my $prev; 60 | 61 | die "Must have at least 2 points" if (scalar(@{ $self->{points} }) < 2); 62 | 63 | $self->{_tag} = $self->{tag}; 64 | 65 | foreach my $point (@{ $self->{points} }) 66 | { 67 | if ($prev) 68 | { 69 | # All parameters in $self except 'points' and 'tag' are passed to GW: 70 | my $gw = GW(points => [ $prev => $point ], 71 | tag => $self->{_tag}++, 72 | map { $_ => $self->{$_} } grep { $_ !~ /(^_|points$|tag$)/ } keys %$self); 73 | 74 | push @geo, $gw; 75 | } 76 | $prev = $point; 77 | } 78 | 79 | $self->{_geo_cards} = \@geo; 80 | } 81 | 82 | sub geo_cards 83 | { 84 | my $self = shift; 85 | 86 | return @{ $self->{_geo_cards} }; 87 | } 88 | 89 | sub program_cards 90 | { 91 | my $self = shift; 92 | 93 | return (); 94 | } 95 | 96 | 1; 97 | -------------------------------------------------------------------------------- /lib/NEC2/Shape.pm: -------------------------------------------------------------------------------- 1 | # This program is free software; you can redistribute it and/or modify 2 | # it under the terms of the GNU General Public License as published by 3 | # the Free Software Foundation; either version 3 of the License, or 4 | # (at your option) any later version. 5 | # 6 | # This program is distributed in the hope that it will be useful, 7 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 8 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 9 | # GNU Library General Public License for more details. 10 | # 11 | # You should have received a copy of the GNU General Public License 12 | # along with this program; if not, write to the Free Software 13 | # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. 14 | # 15 | # Copyright (C) 2021- by Eric Wheeler, KJ7LNW. All rights reserved. 16 | # 17 | # The official website and doumentation for xnec2c-optimizer is available here: 18 | # https://www.xnec2c.org/ 19 | # 20 | # Base class for objects that need to behave similar to an NEC2::Card but are not official NEC2 cards. 21 | package NEC2::Shape; 22 | 23 | sub comment_cards { return () } 24 | sub geo_cards { return () } 25 | sub program_cards { return () } 26 | 27 | 1; 28 | -------------------------------------------------------------------------------- /lib/NEC2/Word.pm: -------------------------------------------------------------------------------- 1 | # This program is free software; you can redistribute it and/or modify 2 | # it under the terms of the GNU General Public License as published by 3 | # the Free Software Foundation; either version 3 of the License, or 4 | # (at your option) any later version. 5 | # 6 | # This program is distributed in the hope that it will be useful, 7 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 8 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 9 | # GNU Library General Public License for more details. 10 | # 11 | # You should have received a copy of the GNU General Public License 12 | # along with this program; if not, write to the Free Software 13 | # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. 14 | # 15 | # Copyright (C) 2021- by Eric Wheeler, KJ7LNW. All rights reserved. 16 | # 17 | # The official website and doumentation for xnec2c-optimizer is available here: 18 | # https://www.xnec2c.org/ 19 | # 20 | package NEC2::Word; 21 | 22 | use strict; 23 | use warnings; 24 | 25 | use NEC2; 26 | 27 | use Font::FreeType; 28 | use Math::Bezier; 29 | 30 | sub defaults 31 | { 32 | return ( 33 | ttf => '/usr/share/fonts/liberation/LiberationSans-Regular.ttf', 34 | size => 1, 35 | dpi => 0.001, 36 | text => 'a', 37 | 38 | # Number of segments for each line 39 | ns => 1, 40 | 41 | # Number of lines per curve 42 | ns_curve => 3, 43 | 44 | # Starting tag 45 | tag => 1, 46 | 47 | # x,y,z starting position 48 | x => 0, 49 | y => 0, 50 | z => 0, 51 | 52 | 53 | # Not implemented, but specifies the plane for drawing 54 | #plane => 'xy', 55 | ); 56 | } 57 | 58 | sub new 59 | { 60 | my ($class, %args) = @_; 61 | my $self = bless(\%args, $class); 62 | 63 | my %defaults = $self->defaults; 64 | foreach my $k (keys %defaults) 65 | { 66 | $self->{$k} //= $defaults{$k}; 67 | } 68 | use Data::Dumper; 69 | print Dumper $self; 70 | 71 | $self->gen_cards; 72 | 73 | return $self; 74 | } 75 | 76 | 77 | sub gen_cards 78 | { 79 | my $self = shift; 80 | 81 | my (@geo, @program); 82 | 83 | my $face = Font::FreeType->new->face($self->{ttf}); 84 | 85 | $face->set_char_size($self->{size}, $self->{size}, $self->{dpi}, $self->{dpi}); 86 | 87 | $self->{_geo_cards} = \@geo; 88 | $self->{_program_cards} = \@program; 89 | 90 | my $z = $self->{z}; 91 | 92 | my $n = 0; 93 | my $letter_offset = 0; 94 | 95 | $self->{_tag} = $self->{tag}; 96 | 97 | foreach my $char (split //, $self->{text}) 98 | { 99 | my $glyph = $face->glyph_from_char($char); 100 | my $width = $glyph->horizontal_advance; 101 | my $height = $glyph->vertical_advance; 102 | 103 | my @cur_pos; 104 | 105 | print "=== $char ===\n"; 106 | 107 | $glyph->outline_decompose( 108 | move_to => sub { 109 | my ( $x, $y ) = @_; 110 | 111 | 112 | $x += $letter_offset + $self->{x}; 113 | $y += $self->{y}; 114 | 115 | if (@cur_pos) 116 | { 117 | return if (sqrt(($cur_pos[0]-$x)**2 + ($cur_pos[1]-$y)**2) < 0.0001); 118 | print "move: $cur_pos[0],$cur_pos[1] -> $x,$y\n"; 119 | $self->add_wire(x1 => $cur_pos[0], y1 => $cur_pos[1], x2 => $x, y2 => $y, z1 => $z, z2 => $z); 120 | } 121 | @cur_pos = ($x, $y); 122 | }, 123 | line_to => sub { 124 | my ( $x, $y ) = @_; 125 | 126 | return if (sqrt(($cur_pos[0]-$x)**2 +($cur_pos[1]-$y)**2) < 0.0001); 127 | 128 | $x += $letter_offset + $self->{x}; 129 | $y += $self->{y}; 130 | 131 | if (@cur_pos) 132 | { 133 | print "line: $cur_pos[0],$cur_pos[1] -> $x,$y\n"; 134 | $self->add_wire(x1 => $cur_pos[0], y1 => $cur_pos[1], x2 => $x, y2 => $y, z1 => $z, z2 => $z) if @cur_pos; 135 | } 136 | 137 | @cur_pos = ($x, $y); 138 | }, 139 | cubic_to => sub { 140 | my ( $x, $y, $cx1, $cy1, $cx2, $cy2 ) = @_; 141 | 142 | return if (sqrt(($cur_pos[0]-$x)**2 +($cur_pos[1]-$y)**2) < 0.0001); 143 | 144 | $x += $letter_offset + $self->{x}; 145 | $cx1 += $letter_offset + $self->{x}; 146 | $cx2 += $letter_offset + $self->{x}; 147 | 148 | $y += $self->{y}; 149 | $cy1 += $self->{y}; 150 | $cy2 += $self->{y}; 151 | 152 | if (@cur_pos) 153 | { 154 | $self->bezier([ @cur_pos, $cx1, $cy1, $cx2, $cy2, $x, $y ]); 155 | print "cubc: $cur_pos[0],$cur_pos[1] -> $cx1,$cy1 $cx2,$cy2 $x,$y\n"; 156 | } 157 | 158 | @cur_pos = ($x, $y); 159 | }, 160 | ); 161 | 162 | $n++; 163 | 164 | $letter_offset += $width; 165 | } 166 | } 167 | 168 | sub geo_cards 169 | { 170 | my $self = shift; 171 | 172 | return @{ $self->{_geo_cards} }; 173 | } 174 | 175 | sub program_cards 176 | { 177 | my $self = shift; 178 | 179 | return @{ $self->{_program_cards} }; 180 | } 181 | 182 | sub bezier 183 | { 184 | my ($self, $points) = @_; 185 | 186 | my $n = $self->{ns_curve}; 187 | 188 | my $b = Math::Bezier->new($points); 189 | my $i; 190 | my @cur_pos = @$points[0,1]; 191 | my $first = 1; 192 | for ($i = 1; $i < $n; $i++) 193 | { 194 | my $pct = $i * (1/($n-1)); 195 | my ($x, $y) = $b->point($pct); 196 | my $len = sqrt(($cur_pos[0]-$x)**2 + ($cur_pos[1]-$y)**2); 197 | print " bezier[$pct]: $x, $y (len=$len)\n"; 198 | 199 | next if ($len < 0.0001); 200 | 201 | $self->add_wire(x1 => $cur_pos[0], y1 => $cur_pos[1], x2 => $x, y2 => $y, z1 => $self->{z}, z2 => $self->{z}); 202 | 203 | @cur_pos = ($x, $y); 204 | } 205 | } 206 | 207 | sub add_wire 208 | { 209 | my ($self, %vars) = @_; 210 | 211 | return if ( $vars{x1} == $vars{x2} && $vars{y1} == $vars{y2} && $vars{z1} == $vars{z2}); 212 | 213 | return push @{ $self->{_geo_cards} }, GW(%vars, ns => $self->{ns}, tag => $self->{_tag}++) 214 | } 215 | 216 | 1; 217 | -------------------------------------------------------------------------------- /lib/NEC2/xnec2c/optimize.pm: -------------------------------------------------------------------------------- 1 | # This program is free software; you can redistribute it and/or modify 2 | # it under the terms of the GNU General Public License as published by 3 | # the Free Software Foundation; either version 3 of the License, or 4 | # (at your option) any later version. 5 | # 6 | # This program is distributed in the hope that it will be useful, 7 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 8 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 9 | # GNU Library General Public License for more details. 10 | # 11 | # You should have received a copy of the GNU General Public License 12 | # along with this program; if not, write to the Free Software 13 | # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. 14 | # 15 | # Copyright (C) 2021- by Eric Wheeler, KJ7LNW. All rights reserved. 16 | # 17 | # The official website and doumentation for xnec2c-optimizer is available here: 18 | # https://www.xnec2c.org/ 19 | # 20 | package NEC2::xnec2c::optimize; 21 | 22 | use strict; 23 | use warnings; 24 | 25 | use PDL; 26 | use PDL::IO::CSV qw(rcsv1D); 27 | use PDL::Opt::Simplex; 28 | use PDL::Opt::Simplex::Simple; 29 | use PDL::Opt::ParticleSwarm::Simple; 30 | 31 | use Linux::Inotify2; 32 | 33 | use Data::Dumper; 34 | 35 | sub new 36 | { 37 | my ($class, %args) = @_; 38 | 39 | my $self = bless(\%args, $class); 40 | 41 | if (!defined($self->{filename_nec})) 42 | { 43 | die "Output file must be defined"; 44 | } 45 | 46 | if (!defined($self->{nec2}) || ref($self->{nec2}) ne 'CODE') 47 | { 48 | die "NEC2 geometry callback function is undefined or invalid"; 49 | } 50 | 51 | if (!defined($self->{filename_nec_csv})) 52 | { 53 | $self->{filename_nec_csv} = $self->{filename_nec} . ".csv"; 54 | } 55 | else 56 | { 57 | warn "specifying filename_nec_csv is unsupported: xnec2c will always append .csv to the .nec file"; 58 | 59 | } 60 | 61 | if (defined($self->{simplex})) 62 | { 63 | warn "deprecated: the section 'simplex' has been changed to 'optimizer' and requires a 'class' attribute. Setting class='PDL::Opt::Simplex::Simple'"; 64 | $self->{optimizer} = delete $self->{simplex}; 65 | $self->{optimizer}{class} = 'PDL::Opt::Simplex::Simple'; 66 | } 67 | 68 | $self->{_opt_class} //= delete $self->{optimizer}{class}; 69 | 70 | if (!defined($self->{_opt_class})) 71 | { 72 | die "You must define an optimizer class in your config as optimizer=>{class=>'CLASSNAME'}}"; 73 | } 74 | 75 | my $oclass = $self->{_opt_class}; 76 | 77 | $self->{fuzz_count} //= 0; 78 | $self->{fuzz_range} //= 0.1; # 10% 79 | $self->{fuzz_ignore} = { map { $_ => 1 } split('\s*,\s*', $self->{fuzz_ignore}//'') }; 80 | 81 | die "fuzz_count cannot be negative" if $self->{fuzz_count} < 0; 82 | 83 | $self->{opt} = $oclass->new( 84 | vars => $self->{vars}, 85 | f => sub { $self->_xnec2c_optimize_callback(@_) }, 86 | log => sub { $self->_log(@_) }, 87 | %{ $self->{optimizer} }); 88 | 89 | #print Dumper $self; 90 | 91 | # opt, vars, nec2, goals, filename_nec_csv, filename_nec 92 | return $self; 93 | } 94 | 95 | sub optimize 96 | { 97 | my $self = shift; 98 | 99 | my $result = $self->{opt}->optimize(); 100 | 101 | # Call one more time to make sure the best result is rendered by xnec2c 102 | # in case the optimizer's last iteration was worse than the best. 103 | $self->_xnec2c_optimize_callback($self->{opt}->get_result_simple()); 104 | 105 | return $self->{opt}->get_result_expanded(); 106 | } 107 | 108 | sub print_vars 109 | { 110 | my ($self, $vars) = @_; 111 | 112 | print "===== Variables ==== \n"; 113 | print Dumper $vars; 114 | } 115 | 116 | sub print_vars_initial 117 | { 118 | my $self = shift; 119 | print "===== Initial Condition ==== \n"; 120 | 121 | print Dumper($self->{opt}->get_vars_simple) . "\n"; 122 | } 123 | 124 | sub print_vars_result 125 | { 126 | my $self = shift; 127 | print "===== Result ==== \n"; 128 | 129 | print "srand: $self->{opt}->{'srand'}\n"; 130 | 131 | print Dumper($self->{opt}->get_result_simple) . "\n"; 132 | } 133 | 134 | sub print_nec2_initial 135 | { 136 | my $self = shift; 137 | print $self->{nec2}->($self->{opt}->get_vars_simple); 138 | } 139 | 140 | sub print_nec2_result 141 | { 142 | my $self = shift; 143 | print $self->{nec2}->($self->{opt}->get_result_simple); 144 | } 145 | 146 | sub print_goal_status 147 | { 148 | my $self = shift; 149 | print "===== Goal Status ==== \n"; 150 | print Dumper($self->{goal_status}); 151 | } 152 | 153 | sub save_nec_initial 154 | { 155 | my $self = shift; 156 | 157 | $self->_xnec2c_optimize_callback($self->{opt}->get_vars_simple); 158 | } 159 | 160 | sub save_nec_result 161 | { 162 | my $self = shift; 163 | 164 | $self->_xnec2c_optimize_callback($self->{opt}->get_result_simple); 165 | } 166 | 167 | sub fuzzinate 168 | { 169 | my ($val, $range_pct) = @_; 170 | 171 | if (ref($val) eq 'ARRAY') 172 | { 173 | return [ map { fuzzinate($_, $range_pct) } @$val ]; 174 | } 175 | 176 | my $range = $val * $range_pct; 177 | 178 | my $rand = rand(); 179 | 180 | my $ret = $val + ($rand*$range*2 - $range); 181 | 182 | my $diff = $ret-$val; 183 | #print STDERR " val=$val range=$range ret=$ret diff=$diff rand=$rand\n"; 184 | 185 | return $ret; 186 | } 187 | 188 | sub _xnec2c_optimize_callback 189 | { 190 | my ($self, $orig_vars) = @_; 191 | 192 | my $filename = $self->{filename_nec_csv}; 193 | 194 | my @values; 195 | 196 | for (my $i = 0; $i < $self->{fuzz_count}+1; $i++) 197 | { 198 | my $vars; 199 | 200 | if ($i == 0) 201 | { 202 | $vars = $orig_vars; 203 | } 204 | else 205 | { 206 | # randomize around the original 207 | foreach my $v (keys(%$orig_vars)) 208 | { 209 | if ($self->{fuzz_ignore}{$v}) 210 | { 211 | # use default values for ignored fields: 212 | $vars->{$v} = $orig_vars->{$v}; 213 | next; 214 | } 215 | 216 | $vars->{$v} = fuzzinate($orig_vars->{$v}, $self->{fuzz_range}); 217 | } 218 | } 219 | 220 | #print STDERR "===================== $filename fuzz try $i: "; 221 | 222 | my $inotify = Linux::Inotify2->new; 223 | if (! -e "$filename" ) 224 | { 225 | open(my $csv, ">", "$filename"); 226 | close($csv); 227 | } 228 | $inotify->watch("$filename", IN_CLOSE_WRITE) 229 | or die "inotify: $!: $filename"; 230 | 231 | $self->{_last_nec} = $self->{nec2}->($vars); 232 | 233 | # save and wait for the CSV to be written by xnec2c: 234 | $self->{_last_nec}->save($self->{filename_nec}); 235 | $inotify->read; 236 | 237 | my $csv = _load_csv("$filename"); 238 | 239 | my $goal = $self->_goal_eval_all($self->{goals}, $vars, $csv); 240 | 241 | #print STDERR " Goal: $goal\n"; 242 | #print Dumper($vars); 243 | 244 | push @values, $goal; 245 | } 246 | 247 | my $max; 248 | 249 | foreach my $v (@values) 250 | { 251 | $max //= $v; 252 | 253 | $max = $v if ($v > $max); 254 | } 255 | 256 | return $max; 257 | } 258 | 259 | sub _log 260 | { 261 | my ($self, $vars, $status) = @_; 262 | 263 | my $minima = $status->{minima}; 264 | my $ssize = $status->{ssize}; 265 | 266 | $status->{elapsed} //= -1; 267 | 268 | if (!$status->{cancel}) 269 | { 270 | $self->print_vars($vars); 271 | $self->print_goal_status; 272 | 273 | print "\n\n"; 274 | } 275 | 276 | printf "%s %.2fs pass #%d/%d (best=%d): %d/%d %.3g > %.3g, minima=%.3g (retries: %d/%d hit/miss: %d/%d)\n", 277 | $status->{cancel} ? 'CANCEL' : 'LOG', 278 | $status->{elapsed}, 279 | $status->{optimization_pass}, $status->{num_passes}, $status->{best_pass}, 280 | $status->{log_count}, $self->{opt}->{max_iter}, 281 | $ssize, $self->{opt}->{tolerance} // 0, 282 | $minima, 283 | $status->{prev_minima_count}, $self->{opt}->{stagnant_minima_count}, 284 | $status->{cache_hits}//0, $status->{cache_misses}//0, 285 | ; 286 | } 287 | 288 | sub _load_csv 289 | { 290 | my $fn = shift; 291 | 292 | (-e $fn) or die "CSV file is missing: $fn"; 293 | 294 | my @pdls = rcsv1D($fn, { header => 1 }); 295 | 296 | # Expected headers as of xnec2c v4.4.11: 297 | my %headers = map { $_ => 1 } qw/ 298 | mhz zreal zimag zmag zphase vswr s11 s11_real s11_imag s11_ang 299 | gain_max gain_net gain_max_theta gain_max_phi gain_viewer 300 | gain_viewer_net fb_ratio/; 301 | 302 | 303 | my %h; 304 | foreach my $p (@pdls) 305 | { 306 | my $header = $p->hdr->{col_name}; 307 | if ($header) 308 | { 309 | $header =~ s/^\s*|\s*$//g; 310 | $h{$header} = $p 311 | } 312 | } 313 | 314 | foreach my $key (keys %headers) 315 | { 316 | warn "CSV: missing header: $key" if !defined($h{$key}); 317 | } 318 | 319 | 320 | return \%h; 321 | } 322 | 323 | sub _goal_eval_mhz 324 | { 325 | my ($self, $mhz_range, $vars, $csv, $goal) = @_; 326 | 327 | my ($mhz_min, $mhz_max) = @$mhz_range; 328 | 329 | my $weight = $goal->{weight} || 1; 330 | my $type = $goal->{type} // 'sum'; 331 | 332 | # Find the index for the given frequency range: 333 | my $mhz; 334 | my $idx_min; 335 | my $idx_max; 336 | my $i = 0; 337 | for ($i = 0; $i < nelem($csv->{mhz}); $i++) 338 | { 339 | $mhz = $csv->{mhz}->slice("($i)"); 340 | $idx_min = $i if ($mhz >= $mhz_min && !$idx_min); 341 | $idx_max = $i if ($mhz <= $mhz_max); 342 | } 343 | 344 | if (!defined($idx_min)) 345 | { 346 | die "can't find index for frequency in CSV at $mhz_min MHz"; 347 | } 348 | 349 | if (!defined($idx_max)) 350 | { 351 | die "can't find index for frequency in CSV at $mhz_max MHz"; 352 | } 353 | 354 | 355 | # $p contains the goal parameters in the frequency min/max that we are testing: 356 | my $p = $csv->{$goal->{field}}; 357 | if (!defined($p)) 358 | { 359 | die "Undefined field in CSV: $goal->{field}\n"; 360 | } 361 | 362 | $p = $p->slice("$idx_min:$idx_max"); 363 | $mhz = $csv->{mhz}->slice("$idx_min:$idx_max"); 364 | 365 | 366 | # Sum the goal function and return the result: 367 | my $n = nelem($p); 368 | my $sum = pdl 0; 369 | my $mag = pdl 0; 370 | my $avg = pdl 0; 371 | my $min; 372 | my $max; 373 | for ($i = 0; $i < $n; $i++) 374 | { 375 | my $pval = $p->slice("($i)"); 376 | my $mhzval = $mhz->slice("($i)"); 377 | my $v = $goal->{result}->($pval, $mhzval); 378 | 379 | if ($v == pdl(['inf']) || "$v" eq 'inf') 380 | { 381 | #warn "result($goal->{field}) at $mhzval MHz (index $i) is +inf, using 1e6"; 382 | $v = pdl 1e6; 383 | } 384 | elsif ($v == pdl(['-inf']) || "$v" eq '-inf') 385 | { 386 | #warn "result($goal->{field}) at $mhzval MHz (index $i) is -inf, using -1e6"; 387 | $v = pdl -1e6; 388 | } 389 | elsif ($v == pdl(['nan']) || $v == pdl(['-nan']) || "$v" eq 'nan' || "$v" eq '-nan') 390 | { 391 | warn "result($goal->{field}) at $mhzval MHz (index $i) is NaN, skipping"; 392 | next; 393 | } 394 | 395 | $min = $v if (!defined($min) || $v < $min); 396 | $max = $v if (!defined($max) || $v > $max); 397 | $sum += $v; 398 | 399 | $mag += $v**2; 400 | } 401 | 402 | # Actual min/max of the parameter: 403 | my ($value_min, $value_max) = minmax($p); 404 | $self->{goal_status}->{$goal->{name}}{"$mhz_range->[0]-$mhz_range->[1]_min"} = $value_min; 405 | $self->{goal_status}->{$goal->{name}}{"$mhz_range->[0]-$mhz_range->[1]_max"} = $value_max; 406 | 407 | $avg = $sum / $n; 408 | $mag = sqrt($mag); 409 | 410 | my $ret = $sum; 411 | 412 | if ($type eq 'sum') { $ret = $sum; } 413 | elsif ($type eq 'avg') { $ret = $avg; } 414 | elsif ($type eq 'min') { $ret = $min; } 415 | elsif ($type eq 'max') { $ret = $max; } 416 | elsif ($type eq 'mag') { $ret = $mag; } 417 | elsif ($type eq 'diff') { $ret = $max - $min; } 418 | elsif ($type eq '+diff') { $ret = $max - $min; } 419 | elsif ($type eq '-diff') { $ret = -($max - $min); } 420 | 421 | $ret *= $weight; 422 | 423 | my $name = $goal->{name} // ''; 424 | 425 | return $ret; 426 | } 427 | 428 | sub _goal_eval_all 429 | { 430 | my ($self, $goals, $vars, $csv) = @_; 431 | 432 | my $nec2 = $self->{_last_nec}; 433 | 434 | my $ret = 0; 435 | foreach my $g (@$goals) { 436 | 437 | # Default to enabled if undefined. 438 | $g->{enabled} //= 1; 439 | next if (!$g->{enabled}); 440 | 441 | 442 | # If no field is defined, just call the goal 443 | # function on the variables and pass the $csv. 444 | if (!defined($g->{field})) 445 | { 446 | my $v = $g->{result}->($vars, $csv); 447 | $ret += $v * $g->{weight}; 448 | next; 449 | } 450 | 451 | my @mhz_ranges; 452 | 453 | # Use all ranges from FR cards: 454 | if (!defined($g->{mhz_ranges})) 455 | { 456 | @mhz_ranges = map { [ $_->get('mhz_min') => $_->get('mhz_max') ] } 457 | $nec2->program_card_filter('FR'); 458 | } 459 | 460 | # Convert to array of arrayrefs: 461 | elsif (defined($g->{mhz_ranges}) && 462 | ref($g->{mhz_ranges}) eq 'ARRAY' && 463 | ref($g->{mhz_ranges}->[0]) eq '') 464 | { 465 | @mhz_ranges = ($g->{mhz_ranges}); 466 | } 467 | 468 | else { 469 | @mhz_ranges = @{ $g->{mhz_ranges} }; 470 | } 471 | 472 | foreach my $mhz_range (@mhz_ranges) 473 | { 474 | # Shrink the min/max values in case the FR cards are bigger than the range of the g 475 | # Typically you would increase the FR cards by (for example) 5MHz so you can see more 476 | # curve but you only want to optimize in the frequency band you care about. 477 | if (defined($g->{mhz_shrink}) && ( 478 | $mhz_range->[0]+$g->{mhz_shrink} > $mhz_range->[1] || 479 | $mhz_range->[0]+$g->{mhz_shrink} < $mhz_range->[0])) 480 | { 481 | die "mhz_shrink'ing $mhz_range->[0] to $mhz_range->[1] MHz by $g->{mhz_shrink} MHz is out of range"; 482 | } 483 | if (defined($g->{mhz_shrink})) 484 | { 485 | $mhz_range->[0] += $g->{mhz_shrink}; 486 | $mhz_range->[1] -= $g->{mhz_shrink}; 487 | } 488 | 489 | # Shift the frequency to the left (-) or right (+) in case the curve needs to move. 490 | 491 | if (defined($g->{mhz_shift}) && ( 492 | $mhz_range->[0]+$g->{mhz_shift} > $mhz_range->[1] || 493 | $mhz_range->[0]+$g->{mhz_shift} < $mhz_range->[0])) 494 | { 495 | die "mhz_shift from $mhz_range->[0] of $g->{mhz_shift} MHz is out of range"; 496 | } 497 | 498 | if (defined($g->{mhz_shift})) 499 | { 500 | $mhz_range->[0] += $g->{mhz_shift}; 501 | $mhz_range->[1] += $g->{mhz_shift}; 502 | } 503 | 504 | my $result = $self->_goal_eval_mhz($mhz_range, $vars, $csv, $g); 505 | 506 | # Return a non-pdl scalar value for logging, this is the value used 507 | # by the optimizer to determine the goal: 508 | $self->{goal_status}->{$g->{name}}{"$mhz_range->[0]-$mhz_range->[1]_spx"} = $result->sclr; 509 | 510 | # Add the result to the final value: 511 | $ret += $result; 512 | } 513 | } 514 | 515 | return $ret; 516 | } 517 | 518 | 1; 519 | 520 | __END__ 521 | 522 | 523 | ############################################################################ 524 | # Goal Options 525 | 526 | # field: the name of the field in the .csv. 527 | # Available fields: 528 | # Freq-MHz, Z-real, Z-imag, Z-magn, Z-phase, VSWR, Gain-max, Gain-viewer, F/B Ratio, Direct-tht, Direct-phi 529 | # 530 | # If field is left undefined, then instead of passing field values to the result function 531 | # it will pass the current optimized vector and csv so the function can do its 532 | # own computation. This could be used to minimize the length of the antenna. 533 | # 534 | # name: an optional field to display the name of the goal 535 | # 536 | # enabled: 1 or 0 to enable/disable. Not if enable is undefined then it defaults to enabled. 537 | # 538 | # mhz_min: The minimum frequency for which the goal applies 539 | # mhz_max: The maximum frequency for which the goal applies 540 | # 541 | # result: a subroutine (coderef) that is passed a measurement (and frequency) and returns the 542 | # value that should be minimized. The result should always return a smaller 543 | # value when the it is closer to the goal because Simplex works to 544 | # find a minima. The value can be negative. The frequency being evaluated by the 545 | # result function could be used to scale the goal, for example, if the shape of the 546 | # goal should vary with frequency. The name of the variables in the function do not 547 | # matter, just know that the values being passed are that which is measured from 'field' 548 | # and the 2nd argument is always the frequency in MHz being evaluated. 549 | # 550 | # Once all goals are evaulated independantly for each measurement they are summed 551 | # together. Thus, it is important the the scale of one goal against another 552 | # is similar. If one goal swings the total sum of all goals too much then 553 | # the subtle (and possibly important) effects of a different goal will be lost 554 | # in the noise of the "louder" goal. Work could be done here to normalize all 555 | # goal results against eachother before summing them together. 556 | # 557 | # For values like VSWR where lower values are better, you can 558 | # penalize larger values by raising them to a power. For example: 559 | # result => sub { my ($swr,$mhz) = @_; return $swr**2; } 560 | # This forces a flatter SWR curve because higher values are quadratically 561 | # worse than lower values. 562 | # 563 | # For values like gain where higher values are better, the value needs to be 564 | # inverted for the optimizer. The simplest way to do this is to make it negative: 565 | # result => sub { my ($gain,$mhz) = @_; return -$gain; } 566 | # 567 | # However you may also create a bias by raising it to a fractional power: 568 | # result => sub { my ($gain,$mhz) = @_; return -$gain*0.5; } 569 | # A higher power makes the max-gain higher because higher gains get a greater 570 | # negative score, thus being "better" in terms of how Simplex evaluates it. 571 | # A lower power makes a flater curve for the opposite reason. 572 | # 573 | # You can also experiment with creating a synthetic goal and exponentiating 574 | # the goal as a fraction. For example: 575 | # result => sub { my ($gain,$mhz) = @_; return 2**(12/$gain); } 576 | # This creates a "goal" of 12dB gain such that when the exponent reaches 12/12 it 577 | # will evaluate as "2". If gain is less than 12dB it will score exponentially 578 | # worse. This also has the effect of normalizing the result against the goal 579 | # which makes the goals more even (you could also adjust the weights). 580 | # 581 | # For SWR, invert the fraction so that lower is better: 582 | # result => sub { my ($swr,$mhz)=@_; return 2**($swr/1.5); } 583 | # 584 | # 585 | # 586 | # type: aggregation type, what to do with of the return from result subroutine for each frequency. 587 | # avg: add them together and divide by the count 588 | # min: return the minimum from the set 589 | # Min will return the "best" value across all frequencies, 590 | # so the optimizer will not work as hard to push it down. 591 | # max: return the maximum from the set 592 | # Max will return the "worst" value across all frequencies, 593 | # so the optimizer will work harder to push it down. Good for VSWR. 594 | # diff: subtract min from max ($max-$min). 595 | # This is useful if you want a minimal difference between the two, but the 596 | # value itself doesn't matter as much. For example, if you need VSWR to 597 | # be consistent across the band but the VSWR value doesn't matter because you 598 | # plan to use an external impedance matching circuit. 599 | # -diff: sames as diff, but negative. Useful when you want a large difference 600 | # between min and max 601 | # +diff: alias for 'diff' 602 | # mag: take the vector magnitude: sum the square of each result and take the sqrt 603 | # sum: add them together 604 | # 605 | # Note that avg/[+-]diff/min/max scale similarly because they represent (approximately) one 606 | # single value across all frequencies. Whereas, "sum" will add all goal results together 607 | # and increase the result by the number of frequency points. The "mag" type may work similarly, 608 | # but to a lesser degree than sum. This effect can be adjusted by decreasing the goal weight. 609 | # 610 | # weight: Multiplicative weight, relative scale of the goal. 611 | # This weight is multiplied times the result of the aggregation type 612 | -------------------------------------------------------------------------------- /xnec2c-simplex.pl: -------------------------------------------------------------------------------- 1 | #!/usr/bin/perl 2 | # 3 | # This program is free software; you can redistribute it and/or modify 4 | # it under the terms of the GNU General Public License as published by 5 | # the Free Software Foundation; either version 3 of the License, or 6 | # (at your option) any later version. 7 | # 8 | # This program is distributed in the hope that it will be useful, 9 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | # GNU Library General Public License for more details. 12 | # 13 | # You should have received a copy of the GNU General Public License 14 | # along with this program; if not, write to the Free Software 15 | # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. 16 | # 17 | # Copyright (C) 2021- by Eric Wheeler, KJ7LNW. All rights reserved. 18 | # 19 | # The official website and doumentation for xnec2c-optimizer is available here: 20 | # https://www.xnec2c.org/ 21 | # 22 | 23 | use strict; 24 | use warnings; 25 | 26 | use lib 'lib'; 27 | 28 | use Getopt::Long qw(:config bundling); 29 | 30 | use POSIX; 31 | 32 | use NEC2; 33 | use NEC2::xnec2c::optimize; 34 | use NEC2::Antenna::Yagi; 35 | 36 | use Data::Dumper; 37 | $Data::Dumper::Sortkeys = 1; 38 | 39 | my %optimizer_opts; 40 | my %opts; 41 | my $xnec2c_pid; 42 | 43 | #$SIG{__WARN__} = sub { print "\nWarning: $_[0]" . Dumper _build_stack() ; }; 44 | $SIG{__DIE__} = sub { if ($^S) { die $_[0] }; print "\nDie: $_[0]" . Dumper _build_stack() ; }; 45 | 46 | 47 | GetOptions( 48 | # cmdline tool options 49 | "batch|b" => \$opts{batch}, 50 | "no-exec|n" => \$opts{no_exec}, 51 | 52 | # optimzer options 53 | "fuzz-count|z=s" => \$optimizer_opts{fuzz_count}, 54 | "fuzz-range-pct|Z=s" => \$optimizer_opts{fuzz_range}, 55 | "fuzz-ignore=s" => \$optimizer_opts{fuzz_ignore}, 56 | ) or usage(); 57 | 58 | if (!@ARGV) 59 | { 60 | usage(); 61 | } 62 | 63 | my $filename_config = $ARGV[0]; 64 | my $filename_nec = $ARGV[0]; 65 | my $filename_save = $ARGV[0]; 66 | 67 | $filename_nec =~ s/\.conf$//; 68 | $filename_nec .= ".nec"; 69 | 70 | my $filename_nec_csv = "$filename_nec.csv"; 71 | 72 | $filename_save =~ s/\.conf$//; 73 | $filename_save = $filename_save . strftime("-%F_%H-%M-%S.save", localtime); 74 | 75 | die "file not found: $filename_config" if (! -e $filename_config); 76 | 77 | my $config = do($filename_config); 78 | die $@ if $@; 79 | 80 | if ($ARGV[1]) 81 | { 82 | my $load = do($ARGV[1]); 83 | die $@ if $@; 84 | foreach my $var (keys(%$load)) 85 | { 86 | $config->{vars}{$var}{values} = $load->{$var}->{values}; 87 | } 88 | } 89 | 90 | my $xnec2c = NEC2::xnec2c::optimize->new( 91 | filename_nec => $filename_nec, 92 | %$config, 93 | %optimizer_opts); 94 | 95 | $xnec2c->print_vars_initial(); 96 | 97 | print "===== Writing NEC2 output to $filename_nec =====\n\n"; 98 | 99 | start_xnec2c(); 100 | 101 | $xnec2c->save_nec_initial(); 102 | 103 | print $xnec2c->print_nec2_initial(); 104 | 105 | if (!$opts{batch}) 106 | { 107 | print "\nPress enter to start\n"; 108 | ; 109 | } 110 | 111 | print "\n===== Starting Optimization ==== \n"; 112 | 113 | my $result = $xnec2c->optimize(); 114 | 115 | if (open(my $save, ">", $filename_save)) 116 | { 117 | print $save Dumper($result); 118 | close($save); 119 | print "Saved state to $filename_save\n"; 120 | } 121 | else 122 | { 123 | warn "$filename_save: $!"; 124 | } 125 | 126 | print "\n===== Done! ==== \n"; 127 | 128 | 129 | $xnec2c->print_goal_status; 130 | $xnec2c->print_vars_result(); 131 | 132 | print "\n===== $filename_nec ==== \n"; 133 | $xnec2c->save_nec_result(); 134 | print $xnec2c->print_nec2_result(); 135 | 136 | if ($opts{batch} && !$opts{no_exec}) 137 | { 138 | kill(15, $xnec2c_pid); 139 | } 140 | 141 | exit 0; 142 | 143 | ## functions 144 | 145 | sub usage 146 | { 147 | print "usage: $0 [options] config-file.conf [saved-state-file.save]\n" 148 | . "\n" 149 | . " --no-exec|-n Do not auto-start/stop xnec2c\n" 150 | . "\n" 151 | . "Fuzzing options are useful for optimizing an antenna that is less sensitive to\n" 152 | . "error. It randomly changes optimization variables by a percentage and returns\n" 153 | . "the worst value as a result. The optimizer will then work to find the place\n" 154 | . "where dimensional error affects the design the least:\n" 155 | . "\n" 156 | . " --fuzz-count|-z N Try N random values\n" 157 | . " --fuzz-range-pct|-Z percent Randomize +/-% of the specified value\n" 158 | . " --fuzz-ignore var1,... Do not randomize the specified variables\n" 159 | . " example: wire_rad,wire_segments\n" 160 | . "\n" 161 | ; 162 | exit 1; 163 | } 164 | 165 | sub start_xnec2c 166 | { 167 | my $ncpus = `grep -c processor /proc/cpuinfo`; chomp $ncpus; 168 | my @xnec2c = (qw/xnec2c -j /, $ncpus, qw/--optimize --write-csv/, $filename_nec_csv, $filename_nec); 169 | print "Opening xnec2c: " . join(" ", @xnec2c) . "\n"; 170 | 171 | if ($opts{no_exec}) 172 | { 173 | print "Auto-starting xnec2c is disabled with \`-n\`. Run the command above if necessary.\n"; 174 | return; 175 | } 176 | 177 | $xnec2c_pid = fork(); 178 | 179 | $SIG{CHLD} = sub { 180 | my $kid; 181 | do { 182 | $kid = waitpid(-1, WNOHANG); 183 | my $rc = $? >> 8; 184 | my $sig = ($? & 0xff); 185 | 186 | if ($kid == $xnec2c_pid) 187 | { 188 | if (($rc != 0 && $rc != 2) || $sig) 189 | { 190 | warn "\n\n==== xnec2c exited (rc=$rc sig=$sig)? Restarting..."; 191 | sleep 1; 192 | start_xnec2c(); 193 | } 194 | else 195 | { 196 | warn "\n\n==== xnec2c closed gracefully (ie, you closed it), aborting optimization."; 197 | exit 0; 198 | } 199 | } 200 | } while $kid > 0; 201 | }; 202 | 203 | if (!$xnec2c_pid) 204 | { 205 | exec(@xnec2c) or die "Unable to start xnec2c: $!"; 206 | exit(1); 207 | } 208 | 209 | sleep 1; 210 | if (!kill(0, $xnec2c_pid)) 211 | { 212 | die "xnec2c does not appear to have loaded."; 213 | } 214 | } 215 | 216 | sub _build_stack 217 | { 218 | my $i = 0; 219 | my @msg; 220 | while (my @c = caller($i++)) { 221 | my @c0 = caller($i); 222 | my $caller = ''; 223 | $caller = " ($c0[3])" if (@c0); 224 | push @msg, " $i. $c[1]:$c[2]:$caller while calling $c[3]"; 225 | } 226 | 227 | return \@msg; 228 | } 229 | --------------------------------------------------------------------------------