Update SIM_balloon with Wind. #1213
Before Width: | Height: | Size: 352 KiB After Width: | Height: | Size: 264 KiB |
Before Width: | Height: | Size: 33 KiB After Width: | Height: | Size: 34 KiB |
Before Width: | Height: | Size: 26 KiB After Width: | Height: | Size: 28 KiB |
BIN
trick_sims/SIM_balloon/Images/Equation12.png
Normal file
After Width: | Height: | Size: 33 KiB |
BIN
trick_sims/SIM_balloon/Images/Equation13.png
Normal file
After Width: | Height: | Size: 42 KiB |
@ -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 balloon’s 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, we’ll 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)```.
|
||||
|
||||
We’ll 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
|
||||
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
};
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
@ -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();
|
||||
|
||||
|