Update SIM_balloon with Wind. #1213

This commit is contained in:
John M. Penn 2022-01-12 13:41:04 -06:00
parent 4418fe034c
commit 62355d54f4
10 changed files with 144 additions and 18 deletions

Binary file not shown.

Before

Width:  |  Height:  |  Size: 352 KiB

After

Width:  |  Height:  |  Size: 264 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 33 KiB

After

Width:  |  Height:  |  Size: 34 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 26 KiB

After

Width:  |  Height:  |  Size: 28 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 33 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 42 KiB

View File

@ -49,6 +49,7 @@ Returns atmospheric temperature (℃) at the given altitude (m).
Returns atmospheric pressure (pascals) at the given altitude (m).
## Dynamics Model
The forces acting on our balloon will be those of gravity, buoyancy and aerodynamic drag.
@ -151,20 +152,34 @@ Here, we can do that same calculation for air mass as before, using
---
### Force of Drag
### Force of Drag Due to Motion and Wind
As a balloon moves through the air, it encounters an atmospheric drag force. This force is a function of:
* the density of the surrounding air (𝝆),
* the balloons velocity with respect to the surrounding air (v<sub>TAS</sub>),
* the balloons coefficient of drag (C<sub>d</sub>) perpendicular to v<sub>TAS</sub>, and
* the cross-sectional area (A) perpendicular to v<sub>TAS</sub>,
As the balloon moves upward, or downward, it will be subjected to an atmospheric drag force. Drag is a function of the balloons shape [ represented by the coefficient of drag (C<sub>d</sub>) ], the density of the air (𝝆), and the cross-sectional area (A) perpendicular to the velocity (v). The drag force points in the opposite direction as the velocity.
<a id=Equation-10></a>
![Equation 10](Images/Equation10.png)
For our balloon, well assume C<sub>d</sub> = 0.5, the coefficient of drag for a sphere.
Since the balloon will be moving through our “standard atmosphere” 𝝆 = ```US_STD_density(altitude)```.
Well calculate the cross-sectional area (A) from the radius of the spherical portion of our balloon.
The velocity of the balloon through the surrounding air (v<sub>TAS</sub>) is equal to its velocity with respect to the ground (v<sub>balloon</sub>) minus the wind velocity (v<sub>wind</sub>). **TAS** stands for True air speed.
<a id=Equation-11></a>
![Equation 11](Images/Equation11.png)
![Equation 10](Images/Equation11.png)
The default value of C<sub>d</sub> = 0.5 (the coefficient of drag for a sphere) for both the horizontal and vertical axes of motion.
We can calculate the cross-sectional area (A) from the radius of the spherical portion of our balloon.
For vertical motion:
![Equation 12](Images/Equation12.png)
For horizontal motion:
![Equation 13](Images/Equation13.png)
## Graphics

View File

@ -5,6 +5,7 @@ dyn.balloon.pos[0] = 0
dyn.balloon.vel[0] = 0.0
dyn.balloon.vel[1] = 0.0
dyn.balloon.envelope_air_temperature = 92.0
dyn.balloon.wind_speed = 0.0
# ==========================================
# Start the Satellite Graphics Client

View File

@ -18,7 +18,8 @@ class Balloon {
double envelope_air_temperature;
double envelope_radius;
double envelope_theta;
double Cd;
double wind_speed;
double Cd[2];
// Calculated Variables
double acc[2];
@ -27,6 +28,7 @@ class Balloon {
// Control Variable
int temperature_change_command;
int wind_change_command;
// Methods
int default_data();
@ -42,7 +44,7 @@ class Balloon {
double calc_heated_air_density();
double calc_buoyancy_force();
double calc_displaced_air_mass();
double calc_drag_force();
void calc_drag_force(double * F);
double volume_of_a_spherical_dome( double r, double h);
double volume_of_a_cone( double r, double h);
};

View File

@ -27,7 +27,8 @@ int Balloon::default_data() {
envelope_radius = 8.5;
envelope_theta = 45 * (M_PI/180.0);
Cd = 0.5;
Cd[0] = 0.5;
Cd[1] = 0.5;
return (0);
}
@ -42,9 +43,13 @@ int Balloon::state_deriv() {
double total_mass = calc_total_mass();
double F_gravity = total_mass * (-US_STD_gravity( pos[1])); /* Equation #2 */
double F_buoyancy = calc_buoyancy_force();
double F_drag = calc_drag_force();
double F_drag[2];
calc_drag_force(F_drag);
acc[0] = 0.0;
acc[1] = (F_gravity + F_buoyancy + F_drag) / total_mass; /* Equation #1 */
acc[0] = (F_drag[0]) / total_mass; /* Equation #1 */
acc[1] = (F_gravity + F_buoyancy + F_drag[1]) / total_mass; /* Equation #1 */
return(0);
}
@ -105,15 +110,29 @@ double Balloon::calc_fixed_mass() {
return (envelope_mass + basket_mass + burner_system_mass + payload_mass);
}
double Balloon::calc_drag_force() {
void Balloon::calc_drag_force(double *F_drag) {
double altitude = pos[1];
double A = M_PI * envelope_radius * envelope_radius; /* Equation 11 */
return (- Cd * 0.5 * US_STD_density(pos[1]) * abs(vel[1]) * vel[1] * A); /* Equation 10 */
/* Equation 11 */
double v_tas[2];
v_tas[0] = vel[0] - wind_speed;
v_tas[1] = vel[1];
double A[2];
A[0] = (0.75 * M_PI + 1.0) * envelope_radius * envelope_radius; /* Equation 13 */
A[1] = M_PI * envelope_radius * envelope_radius; /* Equation 12 */
/* Equation 10 */
F_drag[0] = -( 0.5 * US_STD_density(altitude) * abs(v_tas[0]) * v_tas[0] * Cd[0] * A[0] );
F_drag[1] = -( 0.5 * US_STD_density(altitude) * abs(v_tas[1]) * v_tas[1] * Cd[1] * A[1] );
return;
}
int Balloon::control() {
if ( temperature_change_command != 0) {
envelope_air_temperature += temperature_change_command;
temperature_change_command = 0;
}
if (envelope_air_temperature > 120.0) {
envelope_air_temperature = 120.0;
@ -121,6 +140,16 @@ int Balloon::control() {
if (envelope_air_temperature < 80.0) {
envelope_air_temperature = 80.0;
}
if ( wind_change_command != 0) {
wind_speed += wind_change_command;
wind_change_command = 0;
}
if (wind_speed > 10.0) {
wind_speed = 10.0;
}
if (wind_speed < -10.0) {
wind_speed = -10.0;
}
return(0);
}

View File

@ -67,6 +67,7 @@ class RangeView extends JPanel {
private double[] balloonPos;
private double[] balloonVel;
private int envelopeAirTemp; /* degrees C */
private int windSpeed;
private double envelope_radius;
private double envelope_theta;
@ -78,6 +79,7 @@ class RangeView extends JPanel {
// Controls
private int deltaTemp ;
private int deltaWind ;
/**
* Class constructor.
@ -87,6 +89,7 @@ class RangeView extends JPanel {
setScale(mapScale);
deltaTemp = 0;
deltaWind = 0;
skyColor = new Color(184,202,231);
groundColor = new Color(100,140, 60);
@ -105,6 +108,7 @@ class RangeView extends JPanel {
envelope_theta = Math.toRadians(45.0);
envelopeAirTemp = 0;
windSpeed = 0;
cone = new ScenePoly();
cone.color = envelope_color_1;
@ -152,6 +156,11 @@ class RangeView extends JPanel {
public void resetDeltaTemp() { deltaTemp = 0; }
public int getDeltaTemp() { return deltaTemp; }
public void incWind() { deltaWind = 1; }
public void decWind() { deltaWind = -1; }
public void resetDeltaWind() { deltaWind = 0; }
public int getDeltaWind() { return deltaWind; }
public void setballoonPos(double x, double y) {
balloonPos[0] = x;
balloonPos[1] = y;
@ -163,6 +172,9 @@ class RangeView extends JPanel {
public void setAirTemp(int temperature) {
envelopeAirTemp = temperature;
}
public void setWindSpeed(int speed) {
windSpeed = speed;
}
public void setScale (int mapScale) {
if (mapScale < 2) {
@ -300,10 +312,12 @@ class RangeView extends JPanel {
// ===============================================================================
// Draw Information
// ===============================================================================
g2d.setPaint(Color.BLACK);
g2d.drawString ( String.format("SCALE: %d pixels/meter",scale), 20,20);
g2d.drawString ( String.format("Envelope Air-Temp (°C) : [%d]", envelopeAirTemp), 20,40);
g2d.drawString ( String.format("Balloon Pos: [%.2f, %.2f]", balloonPos[0], balloonPos[1]), 20,60);
g2d.drawString ( String.format("Balloon Vel: [%.2f, %.2f]", balloonVel[0], balloonVel[1]), 20,80);
g2d.drawString ( String.format("Wind Speed (m/s) : [%d]", windSpeed), 20,60);
g2d.drawString ( String.format("Balloon Pos: [%.2f, %.2f]", balloonPos[0], balloonPos[1]), 20,80);
g2d.drawString ( String.format("Balloon Vel: [%.2f, %.2f]", balloonVel[0], balloonVel[1]), 20,100);
}
@ -360,12 +374,55 @@ class TemperatureCtrlPanel extends JPanel implements ActionListener {
}
}
class WindCtrlPanel extends JPanel implements ActionListener {
private RangeView rangeView;
private JButton increaseWindButton, decreaseWindButton;
public WindCtrlPanel(RangeView view) {
rangeView = view;
setLayout(new BoxLayout(this, BoxLayout.X_AXIS));
setBorder( BorderFactory.createEtchedBorder(EtchedBorder.RAISED));
increaseWindButton = new JButton("\u25b6");
increaseWindButton.addActionListener(this);
increaseWindButton.setActionCommand("increaseWind");
increaseWindButton.setToolTipText("Increase Wind");
decreaseWindButton = new JButton("\u25c0");
decreaseWindButton.addActionListener(this);
decreaseWindButton.setActionCommand("decreaseWind");
decreaseWindButton.setToolTipText("Decrease Wind");
add(decreaseWindButton);
add(increaseWindButton);
}
public void actionPerformed(ActionEvent e) {
String s = e.getActionCommand();
switch (s) {
case "increaseWind":
rangeView.incWind();
break;
case "decreaseWind":
rangeView.decWind();
break;
default:
System.out.println("Unknown Action Command:" + s);
break;
}
}
}
class ControlPanel extends JPanel implements ActionListener {
private RangeView rangeView;
private JButton zoomOutButton, zoomInButton;
private JButton shutDownButton;
private TemperatureCtrlPanel temperatureCtrlPanel;
private WindCtrlPanel windCtrlPanel;
public ControlPanel(RangeView view) {
@ -381,6 +438,15 @@ class ControlPanel extends JPanel implements ActionListener {
labeledTemperatureCtrlPanel.add( temperatureCtrlPanel );
add(labeledTemperatureCtrlPanel);
JPanel labeledWindCtrlPanel = new JPanel();
labeledWindCtrlPanel.setLayout(new BoxLayout(labeledWindCtrlPanel, BoxLayout.Y_AXIS));
JLabel windControlLabel = new JLabel("Wind Control");
windControlLabel.setAlignmentX(Component.CENTER_ALIGNMENT);
labeledWindCtrlPanel.add(windControlLabel);
windCtrlPanel = new WindCtrlPanel(rangeView);
labeledWindCtrlPanel.add( windCtrlPanel );
add(labeledWindCtrlPanel);
zoomOutButton = new JButton("Zoom Out");
zoomOutButton.addActionListener(this);
zoomOutButton.setActionCommand("zoomout");
@ -499,9 +565,11 @@ public class BalloonDisplay extends JFrame {
double velx = 0.0;
double vely = 0.0;
int airtemp = 0;
int windspeed = 0;
// Outbound command variables
int temperature_change_command;
int wind_change_command;
int simMode = 0;
boolean standalone = false;
@ -552,6 +620,7 @@ public class BalloonDisplay extends JFrame {
"trick.var_add(\"dyn.balloon.vel[0]\")\n" +
"trick.var_add(\"dyn.balloon.vel[1]\")\n" +
"trick.var_add(\"dyn.balloon.envelope_air_temperature\")\n" +
"trick.var_add(\"dyn.balloon.wind_speed\")\n" +
"trick.var_add(\"trick_sys.sched.mode\")\n" +
// 2) We want the responses in ASCII:
"trick.var_ascii() \n" +
@ -574,7 +643,8 @@ public class BalloonDisplay extends JFrame {
velx = Double.parseDouble( field[3]);
vely = Double.parseDouble( field[4]);
airtemp = Integer.parseInt( field[5]);
simMode = Integer.parseInt( field[6]);
windspeed = Integer.parseInt( field[6]);
simMode = Integer.parseInt( field[7]);
} catch (IOException | NullPointerException e ) {
go = false;
}
@ -583,10 +653,19 @@ public class BalloonDisplay extends JFrame {
rangeView.setballoonPos(posx, posy);
rangeView.setBalloonVel(velx, vely);
rangeView.setAirTemp(airtemp);
rangeView.setWindSpeed(windspeed);
temperature_change_command = rangeView.getDeltaTemp();
balloonDisplay.out.writeBytes( String.format("dyn.balloon.temperature_change_command = %d ;\n", temperature_change_command ));
rangeView.resetDeltaTemp();
if (temperature_change_command != 0) {
balloonDisplay.out.writeBytes( String.format("dyn.balloon.temperature_change_command = %d ;\n", temperature_change_command ));
rangeView.resetDeltaTemp();
}
wind_change_command = rangeView.getDeltaWind();
if (wind_change_command != 0) {
balloonDisplay.out.writeBytes( String.format("dyn.balloon.wind_change_command = %d ;\n", wind_change_command ));
rangeView.resetDeltaWind();
}
balloonDisplay.out.flush();