001/*
002*   NACAFactory -- Factory class for creating NACA analytical airfoils.
003*
004*   Copyright (C) 2000-2025, by Joseph A. Huwaldt. All rights reserved.
005*   
006*   This library is free software; you can redistribute it and/or
007*   modify it under the terms of the GNU Lesser General Public
008*   License as published by the Free Software Foundation; either
009*   version 2.1 of the License, or (at your option) any later version.
010*   
011*   This library is distributed in the hope that it will be useful,
012*   but WITHOUT ANY WARRANTY; without even the implied warranty of
013*   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
014*   Lesser General Public License for more details.
015*
016*   You should have received a copy of the GNU Lesser General Public License
017*   along with this program; if not, write to the Free Software
018*   Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
019*   Or visit:  http://www.gnu.org/licenses/lgpl.html
020*/
021package jahuwaldt.aero.airfoils;
022
023
024/**
025*  This class contains a method for parsing a NACA airfoil
026*  designation string (such as "NACA 2312") and returning
027*  a corresponding Airfoil object.
028*
029*  <p>  Modified by:  Joseph A. Huwaldt  </p>
030*
031*  @author  Joseph A. Huwaldt   Date:  October 20, 2000
032*  @version February 23, 2025
033*/
034public class NACAFactory {
035
036        /**
037        *  Returns an Airfoil object corresponding to the
038        *  airfoil designation sting passed in.  For example,
039        *  if "NACA 0012" or "0012" are passed in a
040        *  NACA4Uncambered object is returned with a thickness
041        *  of 12% toc.
042        *
043        *  @param  designation  The designation of the airfoil that is desired.
044        *  @param  chord        The chord length for the airfoil.
045        *  @return An airfoil corresponding to the designation passed in.
046        *  @throws IllegalArgumentException if the designation is for an airfoil
047        *          type that is unknown to this factory class.
048        */
049        public static Airfoil create(String designation, double chord) throws IllegalArgumentException {
050                Airfoil af = null;
051                        
052                designation = designation.toUpperCase();
053
054                //      Strip off the "NACA" designator if it is there.
055                int pos = designation.indexOf("NACA");
056                if (pos >= 0)
057                        designation = designation.substring(pos+4);
058                
059                designation = designation.trim();
060                
061                int length = designation.length();
062                
063                if (length >= 5 && designation.startsWith("6"))
064                        //      We have a 6 or 6*A series airfoil.
065                        af = parse6Series(designation, chord);
066                        
067                else if (length == 4)
068                        //      We have a 4 digit airfoil.  Parse out the pieces.
069                        af = parse4Digit(designation, chord);
070                        
071                else if (length == 5)
072                        //      We have a 5 digit airfoil.  Parse out the pieces.
073                        af = parse5Digit(designation, chord);
074                
075                else if (length == 6) {
076                        int digits = Integer.parseInt(designation.substring(0,2));
077                        
078                        if (digits == 16)
079                                //      We have a 16 series section (really a modified 4 digit section).
080                                af = parse16Series(designation, chord);
081                
082                } else if (designation.indexOf("-") == 4) {
083                        //      We have a modified 4 digit airfoil with camber.  Parse out the pieces.
084                        af = parseMod4Digit(designation, chord);
085                        
086                }
087                
088                if (af == null)
089                        throw new IllegalArgumentException("Unknown airfoil type: " + designation);
090                
091                return af;
092        }
093        
094        /**
095        *  Parse the designation string for a NACA 4 digit airfoil.
096        */
097        private static Airfoil parse4Digit(String designation, double chord) {
098                Airfoil af;
099                
100                //      We have a 4 digit airfoil.  Parse out the pieces.
101                double camber = Integer.parseInt(designation.substring(0,1));
102                camber /= 100;
103                double xcamber = Integer.parseInt(designation.substring(1,2));
104                xcamber /= 10;
105                double thickness = Integer.parseInt(designation.substring(2));
106                thickness /= 100;
107                
108                if (camber == 0)
109                        af = new NACA4Uncambered(thickness, chord);
110                else
111                        af = new NACA4Cambered(thickness, camber, xcamber, chord);
112                
113                return af;
114        }
115        
116        
117        /**
118        *  Parse the designation string for a NACA 5 digit airfoil.
119        */
120        private static Airfoil parse5Digit(String designation, double chord) {
121                Airfoil af;
122                
123                //      We have a 5 digit airfoil.  Parse out the pieces.
124                int code = Integer.parseInt(designation.substring(0,2));
125                int reflex = Integer.parseInt(designation.substring(2,3));
126                double thickness = Integer.parseInt(designation.substring(3));
127                thickness /= 100;
128                        
129                if (reflex == 0)
130                        af = new NACA5Cambered(thickness, code, chord);
131                else
132                        af = new NACA5Reflexed(thickness, code, chord);
133                
134                return af;
135        }
136        
137        /**
138        *  Parse the designation string for a NACA 16 series airfoil
139        *  (actually a modified 4 digit in disguise).
140        */
141        private static Airfoil parse16Series(String designation, double chord) {
142                Airfoil af;
143                
144                //      16 series section (really a modified 4 digit section).
145                double thickness = Integer.parseInt(designation.substring(4));
146                thickness /= 100;
147                int LEindex = 4;
148                double xThickness = 0.5;
149                
150                af = new NACA4ModUncambered(thickness, LEindex, xThickness, chord);
151                
152                return af;
153        }
154        
155        /**
156        *  Parse the designation string for a NACA modified 4 digit airfoil.
157        */
158        private static Airfoil parseMod4Digit(String designation, double chord) {
159                Airfoil af;
160                
161                //      We have a modified 4 digit airfoil with camber.  Parse out the pieces.
162                double camber = Integer.parseInt(designation.substring(0,1));
163                camber /= 100;
164                double xcamber = Integer.parseInt(designation.substring(1,2));
165                xcamber /= 10;
166                double thickness = Integer.parseInt(designation.substring(2,4));
167                thickness /= 100;
168                int LEindex = Integer.parseInt(designation.substring(5,6));
169                double xThickness = Integer.parseInt(designation.substring(6));
170                xThickness /= 10;
171                
172                if (camber == 0)
173                        af = new NACA4ModUncambered(thickness, LEindex, xThickness, chord);
174                else
175                        af = new NACA4ModCambered(thickness, camber, xcamber, LEindex, xThickness, chord);
176                
177                return af;
178        }
179        
180        /**
181        *  Parse the designation string for a NACA 6 or 6*A series airfoil.
182        */
183        private static Airfoil parse6Series(String designation, double chord) {
184                Airfoil af = null;
185                
186                //      Get the profile index.
187                int profile = Integer.parseInt(designation.substring(1,2));
188                
189                int dashOffset = designation.indexOf('-');
190                if (dashOffset > 0)     dashOffset = 1;
191                else dashOffset = 0;
192                
193                if ("A".equals(designation.substring(2,3))) {
194                        //      We have a 6*A series airfoil.
195                        
196                        //      Get the design lift coefficient.
197                        double CLi = Integer.parseInt(designation.substring(3+dashOffset,4+dashOffset));
198                        CLi /= 10.;
199                        
200                        //      Get the t/c.
201                        double thickness = Integer.parseInt(designation.substring(4+dashOffset));
202                        thickness /= 100.;
203                        
204                        switch (profile) {
205                                case 3:
206                                        af = new NACA63ASeries(CLi, thickness, chord);
207                                        break;
208                                        
209                                case 4:
210                                        af = new NACA64ASeries(CLi, thickness, chord);
211                                        break;
212
213                                case 5:
214                                        af = new NACA65ASeries(CLi, thickness, chord);
215                                        break;
216                                        
217                                default:
218                                        break;
219                        }
220                        
221                } else {
222                        //      We have a 6 series airfoil.
223                        
224                        //      Get the design lift coefficient.
225                        double CLi = Integer.parseInt(designation.substring(2+dashOffset,3+dashOffset));
226                        CLi /= 10.;
227                        
228                        //      Get the t/c.
229                        double thickness = Integer.parseInt(designation.substring(3+dashOffset));
230                        thickness /= 100.;
231                        
232                        switch (profile) {
233                                case 3:
234                                        af = new NACA63Series(CLi, thickness, chord);
235                                        break;
236                                        
237                                case 4:
238                                        af = new NACA64Series(CLi, thickness, chord);
239                                        break;
240
241                                case 5:
242                                        af = new NACA65Series(CLi, thickness, chord);
243                                        break;
244                                
245                                case 6:
246                                        af = new NACA66Series(CLi, thickness, chord);
247                                        break;
248                                
249                                case 7:
250                                        af = new NACA67Series(CLi, thickness, chord);
251                                        break;
252
253                                default:
254                                        break;
255                        }
256                        
257                }
258
259                return af;
260        }
261        
262        /**
263        *  A simple method to test this class.
264        *
265        * @param args command-line arguments.
266        */
267        public static void main(String[] args) {
268        
269                System.out.println("Testing NACAFactory...");
270                
271                Airfoil af = NACAFactory.create("NACA 0012", 1);
272                System.out.println("    NACA 4 Uncambered = " + af);
273                
274                af = NACAFactory.create(" 6409 ", 1);
275                System.out.println("    NACA 4 Cambered = " + af);
276                
277                af = NACAFactory.create("NACA23012", 1);
278                System.out.println("    NACA 5 digit unreflexed = " + af);
279                
280                af = NACAFactory.create("NACA23112", 1);
281                System.out.println("    NACA 5 digit reflexed = " + af);
282                
283                af = NACAFactory.create("0009-34", 1);
284                System.out.println("    Modified 4 digit uncambered = " + af);
285                
286                af = NACAFactory.create("2410-34", 1);
287                System.out.println("    Modified 4 digit cambered = " + af);
288                
289                af = NACAFactory.create("16-021", 1);
290                System.out.println("    16 series = " + af);
291                
292                af = NACAFactory.create("63-206", 1);
293                System.out.println("    6 series = " + af);
294                
295                af = NACAFactory.create("63A212", 1);
296                System.out.println("    6*A series = " + af);
297                
298                System.out.println("Done!");
299        }
300        
301}
302