-
Notifications
You must be signed in to change notification settings - Fork 0
/
mescal_6.py
237 lines (198 loc) · 8.86 KB
/
mescal_6.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
"""Prebuilt wing models."""
from __future__ import annotations
import numpy as np
import pfh.glidersim as gsim
from pfh.glidersim.extras import plots
def skywalk_mescal_6(
*,
size: str,
num_control_points: int = 31,
verbose: bool = True,
) -> gsim.paraglider_wing.ParagliderWing:
"""
Build an approximate Skywalk Mescal 6.
Parameters
----------
size : {XS}
The size of the wing.
num_control_points : int
The number of aerodynamic control points.
verbose : bool, default: True
Whether to print a description of the resulting model.
References
----------
https://skywalk.info/project/mescal/
"""
# Specifications from the Skywalk Mescal 6 Pro Guide, p3, "Technical Data"
technical_specs: dict[str, dict] = {}
technical_specs['XS'] = {
"chord_tip": 0.66, # Min. profile depth
"chord_root": 2.80, # Max. profile depth
"chord_mean": 2.06, # Not specified
"S_flat": 24.29,
"b_flat": 10.80,
"AR_flat": 4.80,
"S": 20.48,
"b": 8.40,
"AR": 3.47,
"m_s": 4.6, # Glider mass [kg]
"kappa_z": 6.91, # Taken from 'middle line length w.o. risers"
"total_line_length": 278, # Line consumption
# Helper variables
"number_of_cells": 38
}
if size not in technical_specs:
raise ValueError(
f"Invalid canopy size {size}, must be in {tuple(technical_specs.keys())}",
)
# Select the spec
specs = technical_specs[size]
if verbose:
print(f"Building an (approximate) Skywalk Mescal 6 {size}\n")
if verbose:
print("Airfoil: braking_NACA24018_Xtr0.25\n")
airfoils = gsim.extras.airfoils.load_datfile_set("braking_NACA24018_Xtr0.25")
airfoil_geo = gsim.airfoil.AirfoilGeometryInterpolator(airfoils)
airfoil_coefs = gsim.extras.airfoils.load_polar("braking_NACA24018_Xtr0.25")
delta_d_max = 0.20273 # FIXME: magic number from the set of coefficients
c = gsim.foil_layout.EllipticalChord(
root=specs["chord_root"] / (specs["b_flat"] / 2),
tip=specs["chord_tip"] / (specs["b_flat"] / 2),
)
# Geometric torsion
#
# The distribution is uncertain. In Sec. 11.4, pg 17 of the manual ("Line
# Plan") it appears to have a roughly square-root spanwise distribution
# with a maximum value of 6 or so, but without better data I'm sticking to
# a linear distribution with a smaller peak (easier for the aerodynamics).
# theta = gsim.foil_layout.PolynomialTorsion(start=0.0, peak=6, exponent=0.5)
theta = gsim.foil_layout.PolynomialTorsion(start=0.05, peak=4, exponent=1)
# Using `tip_anhedral = 75` is probably more accurate, but it also
# increases the chances of stalling the wing tips during hard turns.
layout = gsim.foil_layout.FoilLayout(
r_x=0.70,
x=0,
r_yz=0.25,
yz=gsim.foil_layout.EllipticalArc(mean_anhedral=32, tip_anhedral=75),
c=c,
theta=theta,
)
sections = gsim.foil_sections.FoilSections(
profiles=airfoil_geo,
coefficients=airfoil_coefs,
intakes=gsim.foil_sections.SimpleIntakes(0.85, -0.04, -0.09), # FIXME: guess
Cd_intakes=0.07, # ref: babinsky1999AerodynamicPerformanceParagliders
Cd_surface=0.004, # ref: ware1969WindtunnelInvestigationRamair
)
s_nodes = np.linspace(-1, 1, num_control_points + 1)
canopy = gsim.foil.SimpleFoil(
layout=layout,
sections=sections,
# b=b, # Option 1: Scale the using the projected span
b_flat=specs["b_flat"], # Option 2: Scale the using the flattened span
aerodynamics_method=gsim.foil_aerodynamics.Phillips,
aerodynamics_config={
"v_ref_mag": 10,
"alpha_ref": 5,
"s_nodes": s_nodes,
"s_clamp": s_nodes[-2], # Mitigate fictitious stalls at wing tips
},
)
# Most of these values are based on data, but `kappa_x` is simply the
# choice that produces a nice polar (max and min speeds are reasonable) and
# the best glide ratio occurs at zero control inputs (some wings produce
# better glide ratios with small amounts of accelerator, but without
# evidence this a reasonable assumption).
riser_position_parameters = {
"kappa_x": 0.45 * specs["chord_root"],
# Pro guide - 3. Technical Data
"kappa_z": specs["kappa_z"],
"kappa_a": 0.13, # Maximum speed bar travel
# Pro guide - 11. Line Schematic
"kappa_A": 0.11 * specs["chord_root"],
"kappa_C": 0.70 * specs["chord_root"],
}
# TODO / Estimated from https://www.youtube.com/watch?v=D-OyGZbOmS0
# The `0` parameters are easy to estimate directly from small brake inputs.
# The "max brake" parameters are more difficult since they depend on the
# value of `kappa_b` (the maximum deflection supported by the model). An
# initial guess shows that the set of NACA 24018 coefficients is going to
# limit `kappa_b` to roughly 45cm; using the video to observe deflections
# at ~45cm (the risers are 47cm) produce `start1` and `stop1`.
brake_parameters = {
"kappa_b": 0, # Set later with `maximize_kappa_b`
"s_delta_start0": 0.30,
"s_delta_start1": 0.08,
"s_delta_stop0": 0.70,
"s_delta_stop1": 1.05,
}
# Crude guesses to account for the bulk of the line drag.
# TODO: needs a serious review
line_drag_parameters = {
"total_line_length": specs["total_line_length"],
"average_line_diameter": 1e-3, # Blind guess
"r_L2LE": np.array(
[
[-0.5 * specs["chord_root"], -1.75, 1.75],
[-0.5 * specs["chord_root"], 1.75, 1.75],
],
),
"Cd_lines": 1, # ref: Kulhánek, 2019; page 5
}
lines = gsim.paraglider_wing.SimpleLineGeometry(
**riser_position_parameters,
**brake_parameters,
**line_drag_parameters, # type: ignore [arg-type]
)
lines.maximize_kappa_b(delta_d_max, canopy.chord_length)
# Construct the wing object
wing = gsim.paraglider_wing.ParagliderWing(
lines=lines,
canopy=canopy,
rho_upper=38 / 1000, # [kg/m^2] Porcher Skytex 38g / Skytex Easyfly
rho_lower=40 / 1000, # [kg/m^2] Porcher Skytex Easyfly
rho_ribs=40 / 1000, # [kg/m^2] Porcher Skytex 40g hard
N_cells=specs["number_of_cells"]
)
# -----------------------------------------------------------------------
# Plots
print("Drawing the canopy")
# Split the canopy into cells
number_of_sections = specs["number_of_cells"]
plots.plot_foil(canopy, number_of_sections, surface="airfoil", flatten=False)
# plots.plot_foil(canopy, 131, surface="chord", flatten=False)
# plots.plot_foil(canopy, 131, surface="camber", flatten=False)
# plots.plot_foil(canopy, number_of_sections, surface="airfoil", flatten=True)
# plots.plot_foil(canopy, 71, surface="chord", flatten=True)
# plots.plot_foil(canopy, 71, surface="camber", flatten=True)
# plots.plot_foil_topdown(canopy, number_of_sections)
plots.plot_foil_topdown(canopy, number_of_sections + 1, flatten=True) # The number of internal sections need to match number of sections
# Plot a braking wing
s = np.linspace(-1, 1, 131) # Inflated plots
ai = wing.lines.delta_d(s, 0.5, 1) / wing.canopy.chord_length(s)
# plots.plot_foil(canopy, s, ai=ai, surface="airfoil", flatten=False)
# Compare to the Mescal 6 manual, sec 11.4 "Line Plan", page 17
# plots.plot_foil_topdown(canopy, number_of_sections)
# -----------------------------------------------------------------------
# Model diagnostics
if verbose:
print("Canopy geometry: [Target]")
print(f" flattened span: {canopy.b_flat:>6.3f} [{specs['b_flat']:>6.3f}]")
print(f" flattened area: {canopy.S_flat:>6.3f} [{specs['S_flat']:>6.3f}]")
print(f" flattened AR: {canopy.AR_flat:>6.3f} [{specs['AR_flat']:>6.3f}]")
# print(f" planform flat SMC {canopy.SMC:>6.3f}")
# print(f" planform flat MAC: {canopy.MAC:>6.3f}")
print(f" projected span: {canopy.b:>6.3f} [{specs['b']:>6.3f}]")
print(f" projected area: {canopy.S:>6.3f} [{specs['S']:>6.3f}]")
print(f" projected AR: {canopy.AR:>6.3f} [{specs['AR']:>6.3f}]")
print()
# Reminder that I'm not accounting for mass from things like the lines,
# internal vribs, internal horizontal straps, caribiners, etc.
r_RM2LE = wing.r_RM2LE(delta_a=0)
wmp = wing.mass_properties(rho_air=1.225, r_R2LE=r_RM2LE)
print("Wing inertia: [Target]")
print(f" solid mass: {wmp['m_s']:>6.3f} [{specs['m_s']:>6.3f}]")
print()
print(f"Maximum brake length (kappa_b): {wing.lines.kappa_b}")
print("Finished building the glider.\n")
return wing