From 25728e2f62de85489a144cb2015c9ec439d1a35a Mon Sep 17 00:00:00 2001
From: Thomas Lambert <t.lambert@uliege.be>
Date: Sat, 9 Apr 2022 22:41:13 +0200
Subject: [PATCH] feat: add save to dat for nacaairfoil

---
 +af_tools/nacaairfoil.m  | 33 ++++++++++++++++++++++++++++++---
 .gitignore               |  1 +
 CHANGELOG.md             |  4 ++++
 README.md                | 10 ++++++++--
 tests/test_nacaairfoil.m | 14 +++++++-------
 5 files changed, 50 insertions(+), 12 deletions(-)

diff --git a/+af_tools/nacaairfoil.m b/+af_tools/nacaairfoil.m
index 80791fb..674857c 100644
--- a/+af_tools/nacaairfoil.m
+++ b/+af_tools/nacaairfoil.m
@@ -40,12 +40,18 @@ function [x, y] = nacaairfoil(varargin)
 %     true   - (default) zero thickness trailing edge
 %     false  - finite trailing edge
 %
+%   [...] = NACAAIRFOIL(..., 'savedat', false) saves the output coordinates in a
+%   dat files. savedat can either be true or false (default).
+%
 % Inputs:
 %   digits  : Character vector representing the NACA airfoil (ex: '0012', '24120', '24012')
 %   nPoints : Number of output coordinates
 %
 % Output:
 %   [X, Y] : Airfoil coordinates, with 0 <= X <= 1.
+%            The output coordinates follow the most usual order. They start at
+%            the trailing edge, along the upper surface to the leading edge and
+%            back around the lower surface to trailing edge.
 %
 % Example:
 %   NACAAIRFOIL
@@ -69,11 +75,11 @@ narginchk(0,6);
 import af_tools.nacacamber;
 import af_tools.utils.parsenacainputs;
 
-OPTION_LIST = {'spacing', 'zerote'};
+OPTION_LIST = {'spacing', 'zerote', 'savedat'};
 
 % Extract and validate the inputs
 [digits, nPoints, idxOpts] = parsenacainputs(OPTION_LIST, varargin{:});
-[spacing, zerote] = parseoptioninputs(varargin{idxOpts:end});
+[spacing, zerote, savedat] = parseoptioninputs(varargin{idxOpts:end});
 
 %------------------------------------
 % Coordinates calculation
@@ -82,6 +88,10 @@ OPTION_LIST = {'spacing', 'zerote'};
 % number of points on the upper and lower surfaces.
 [xc, yc, gradY] = nacacamber(digits, ceil((nPoints+1)/2), 'spacing', spacing);
 
+% Flip camberline, so the first element is the trailing edge
+xc = fliplr(xc);
+yc = fliplr(yc);
+
 % Thickness value (checks were done in nacacamber)
 t = str2double(digits(end-1:end)) /100;
 
@@ -110,11 +120,16 @@ yl = yc - yt.*cos(theta);
 x = [xu, fliplr(xl(1:end-1))];
 y = [yu, fliplr(yl(1:end-1))];
 
+% Save coordinates to dat file
+if savedat
+    savetodat(digits, x, y, spacing);
+end
+
 end
 
 
 % ------------------------------------------------------------------------------
-function [spacing, zerote] = parseoptioninputs(varargin)
+function [spacing, zerote, savedat] = parseoptioninputs(varargin)
 % PARSEINPUTS Parses the input and checks their validity
 
 import af_tools.utils.*
@@ -122,6 +137,7 @@ import af_tools.utils.*
 % Constants and defaults
 DEFAULT_SPACING = 'halfcosine';
 DEFAULT_ZEROTE = true;
+DEFAULT_SAVEDAT = false;
 ALLOWED_SPACING = {'linear', 'cosine', 'halfcosine'};
 
 % Option validator
@@ -132,10 +148,21 @@ validSpacing = @(x) any(validatestring(x, ALLOWED_SPACING, mfilename()));
 p = inputParser;
 addParameter(p,'spacing',DEFAULT_SPACING, validSpacing);
 addParameter(p,'zerote',DEFAULT_ZEROTE, validLogical);
+addParameter(p,'savedat',DEFAULT_SAVEDAT, validLogical);
 parse(p,varargin{:});
 spacing = p.Results.spacing;
 zerote = p.Results.zerote;
+savedat = p.Results.savedat;
+
+end
 
+function savetodat(digits, x, y, spacing)
+% SAVETODAT Saves the airfoil coordinates to a dat file
 
+header = ['NACA',digits,' (',spacing,' spacing)'];
+data=num2str([x; y]');
+contents=char(header,data);
+fileName=['naca', digits, '.dat'];
+dlmwrite(fileName,contents,'delimiter','')
 
 end
diff --git a/.gitignore b/.gitignore
index 962e76b..b085236 100644
--- a/.gitignore
+++ b/.gitignore
@@ -3,3 +3,4 @@
 *.m~
 *.mat
 *.png
+*.dat
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 05ad166..ba71091 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -9,8 +9,12 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
 
 ### Added
 
+- Added save to dat file for _nacaairfoil_
+
 ### Changed
 
+- Coordinates of nacaairfoil start at TE, go along upper side to LE, then TE
+
 ### Deprecated
 
 ### Removed
diff --git a/README.md b/README.md
index bb451a6..feda6b5 100644
--- a/README.md
+++ b/README.md
@@ -35,7 +35,7 @@ Unfortunately, Matlab packages have a few limitations. Hence, you should **avoid
 changing the package name** if you do not know what you are doing.
 
 - The `+` in the folder's name is required for Matlab to treat it as a package.
-- Some functions of this package are interconnected (_e.g._, _nacairfoil_ calls
+- Some functions of this package are interconnected (_e.g._, _nacaairfoil_ calls
   _nacacamber_). In that situation, Matlab forces the main function to re-import
   explicitly all sub-functions before using them, even if they are part of the
   same package ([people have complained][matlabPackgeIssue] about that design
@@ -146,6 +146,7 @@ The following functions use the same two main input arguments.
 #### nacacamber
 
 Determines the camber line of a NACA 4 or 5 digits airfoil.
+The output is ordered from the leading edge to the trailing edge.
 
 ```matlab
 import af_tools.nacacamber
@@ -162,6 +163,9 @@ import af_tools.nacacamber
 #### nacaairfoil
 
 Generates the full coordinates of a NACA 4 or 5 digits airfoil.
+The output coordinates follow the most usual order. They start at the trailing
+edge, along the upper surface to the leading edge and back around the lower
+surface to trailing edge.
 
 ```matlab
 import af_tools.nacaairfoil
@@ -169,12 +173,14 @@ import af_tools.nacaairfoil
 [x, y] = nacaairfoil(digits)
 [x, y] = nacaairfoil(digits, nPoints)
 [x, y] = nacaairfoil(digits, nPoints, 'spacing', spacing, 'zerote', true)
+[x, y] = nacaairfoil(digits, nPoints, 'savedat', true)
 ```
 
 | Options   | Example                            | Default        |
 |---------- | ---------------------------------- | -------------- |
 | 'spacing' | `halfcosine`, `cosine`, `linear`   | `'halfcosine'` |
-| 'zerote'  | `true`, `false`                    | `true`        |
+| 'zerote'  | `true`, `false`                    | `true`         |
+| 'savedat' | `true`, `false`                    | `true`         |
 
 ### Polar manipulation
 
diff --git a/tests/test_nacaairfoil.m b/tests/test_nacaairfoil.m
index 93ead07..def97d1 100644
--- a/tests/test_nacaairfoil.m
+++ b/tests/test_nacaairfoil.m
@@ -108,7 +108,7 @@ function test_invalidOptions(testCase)
 % Error if invalid option name or missing parameter value
 
 wrongName = 'testspacing';
- 
+
 verifyError(testCase, @() af_tools.nacaairfoil('0012', 100, wrongName), 'MATLAB:InputParser:ParamMissingValue')
 verifyError(testCase, @() af_tools.nacaairfoil('0012', 100, wrongName, false), 'MATLAB:InputParser:UnmatchedParameter')
 verifyError(testCase, @() af_tools.nacaairfoil('0012', 100, 'spacing', 'cosine', wrongName), 'MATLAB:InputParser:ParamMissingValue')
@@ -191,12 +191,12 @@ cosine = 1-cos(betacos);
 [xc_half, ~] = af_tools.nacaairfoil('0012', nPoints, 'spacing', 'halfcosine');
 
 % Test the two halves of the output vector (i.e. upper and lower surfaces)
-verifyEqual(testCase, xc_lin(1:(end+1)/2), linear)
-verifyEqual(testCase, xc_cos(1:(end+1)/2), cosine)
-verifyEqual(testCase, xc_half(1:(end+1)/2), halfcos)
-verifyEqual(testCase, xc_lin(end:-1:(end+1)/2), linear)
-verifyEqual(testCase, xc_cos(end:-1:(end+1)/2), cosine)
-verifyEqual(testCase, xc_half(end:-1:(end+1)/2), halfcos)
+verifyEqual(testCase, xc_lin(1:(end+1)/2), fliplr(linear))
+verifyEqual(testCase, xc_cos(1:(end+1)/2), fliplr(cosine))
+verifyEqual(testCase, xc_half(1:(end+1)/2), fliplr(halfcos))
+verifyEqual(testCase, xc_lin(end:-1:(end+1)/2), fliplr(linear))
+verifyEqual(testCase, xc_cos(end:-1:(end+1)/2), fliplr(cosine))
+verifyEqual(testCase, xc_half(end:-1:(end+1)/2), fliplr(halfcos))
 
 end
 
-- 
GitLab