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