Hot-air balloon simulation. #1210

This commit is contained in:
John M. Penn 2021-12-17 16:03:43 -06:00
parent dbf465f217
commit b643296fd8
9 changed files with 942 additions and 0 deletions

View File

@ -0,0 +1,10 @@
trick.real_time_enable()
trick.exec_set_software_frame(0.1)
trick.itimer_enable()
trick.exec_set_enable_freeze(True)
trick.exec_set_freeze_command(True)
simControlPanel = trick.SimControlPanel()
trick.add_external_application(simControlPanel)

View File

@ -0,0 +1,24 @@
execfile("Modified_data/realtime.py")
dyn.balloon.pos[0] = 0
#dyn.balloon.pos[1] = 136.89
dyn.balloon.vel[0] = 0.0
dyn.balloon.vel[1] = 0.0
dyn.balloon.envelope_air_temperature = 92.0
# ==========================================
# Start the Satellite Graphics Client
# ==========================================
varServerPort = trick.var_server_get_port();
BalloonDisplay_path = "models/graphics/dist/BalloonDisplay.jar"
if (os.path.isfile(BalloonDisplay_path)) :
BalloonDisplay_cmd = "java -jar " \
+ BalloonDisplay_path \
+ " " + str(varServerPort) + " &" ;
print(BalloonDisplay_cmd)
os.system( BalloonDisplay_cmd);
else :
print('==================================================================================')
print('BalloonDisplay needs to be built. Please \"cd\" into ../models/graphics and type \"make\".')
print('==================================================================================')

View File

@ -0,0 +1,30 @@
/************************************************************
PURPOSE:
( Simulate a hot-air balloon. )
LIBRARY DEPENDENCIES:
((balloon/src/Balloon.cpp)
(atmosphere/src/atmosphere.c))
*************************************************************/
#include "sim_objects/default_trick_sys.sm"
##include "balloon/include/Balloon.hh"
class BalloonSimObject : public Trick::SimObject {
public:
Balloon balloon;
BalloonSimObject() {
("default_data") balloon.default_data() ;
("initialization") balloon.state_init() ;
("derivative") balloon.state_deriv() ;
(0.1, "scheduled") balloon.control() ;
("integration") trick_ret = balloon.state_integ() ;
("post_integration") balloon.check_ground_contact() ;
}
};
BalloonSimObject dyn;
IntegLoop dyn_integloop(0.1) dyn;
void create_connections() {
dyn_integloop.getIntegrator(Runge_Kutta_4, 4);
}

View File

@ -0,0 +1,2 @@
TRICK_CFLAGS += -Imodels
TRICK_CXXFLAGS += -Imodels

View File

@ -0,0 +1,24 @@
#ifndef ATMOSPHERE_H
#define ATMOSPHERE_H
#ifdef __cplusplus
extern "C" {
#endif
/* Returns atmospheric density (kg/m^3) at the given altitude (m).*/
double US_STD_density ( double alt_m );
/* Returns acceleration of gravity (m/s^2) at the given altitude (m).*/
double US_STD_gravity( double alt_m);
/* Returns atmospheric temperature (℃) at the given altitude (m).*/
double US_STD_temperature( double alt_m );
/* Returns atmospheric pressure (pascals) at the given altitude (m).*/
double US_STD_pressure( double alt_m);
#ifdef __cplusplus
}
#endif
#endif

View File

@ -0,0 +1,71 @@
#include <stdio.h>
static double interpolate( double x, const double xa[], const double fa[], int N_elems ) {
int ii;
for (ii=0 ; ((ii+2 < N_elems ) && (x > xa[ii+1])) ; ii++ ) ;
double x_lower = xa[ii];
double x_upper = xa[ii+1];
double f_lower = fa[ii];
double f_upper = fa[ii+1];
if (x < x_lower) {
fprintf(stderr, "Interpolation x is out of range low.\n");
return(f_lower);
}
if (x > x_upper) {
fprintf(stderr, "Interpolation x is out of range high.\n");
return(f_upper);
}
double f = (x_upper - x)/(x_upper - x_lower) * f_lower +
(x - x_lower)/(x_upper - x_lower) * f_upper ;
return(f);
}
#define NUM_ELEMENTS 21
// Units = meters (above sea level).
const double altitude_array[NUM_ELEMENTS] = {
-1000.0, 0.0, 1000.0, 2000.0, 3000.0, 4000.0, 5000.0, 6000.0,
7000.0, 8000.0, 9000.0, 10000.0, 15000.0, 20000.0, 25000.0, 30000.0,
40000.0, 50000.0, 60000.0, 70000.0, 80000.0 };
// Units = kilograms per cubic meter.
const double US_STD_density_array[NUM_ELEMENTS] = {
1.347, 1.225, 1.112, 1.007, 0.9093, 0.8194, 0.7364, 0.6601,
0.5900, 0.5258, 0.4671, 0.4135, 0.1948, 0.08891, 0.04008, 0.01841,
0.003996, 0.001027, 0.0003097, 0.00008283, 0.00001846 };
// Units = meters per second squared.
const double US_STD_gravity_array[NUM_ELEMENTS] = {
9.810, 9.807, 9.804, 9.801, 9.797, 9.794, 9.791, 9.788,
9.785, 9.782, 9.779, 9.776, 9.761, 9.745, 9.730, 9.715,
9.684, 9.654, 9.624, 9.594, 9.564
};
// Units = celsius
const double US_STD_temperature_array[NUM_ELEMENTS] = {
21.50, 15.00, 8.50, 2.00, -4.49, -10.98, -17.47, -23.96,
-30.45, -36.94, -43.42, -49.90, -56.50, -56.50, -51.60, -46.64,
-22.80, -2.5, -26.13, -53.57, -74.51
};
// Units = pascals
const double US_STD_pressure_array[NUM_ELEMENTS] = {
113900.0, 101325.0, 89880.0, 79500.0, 70120.0, 61660.0, 54050.0, 47220.0,
41110.0, 35650.0, 30800.0, 26500.0, 12110.0, 5529.0, 2549.0, 1197.0,
287.00, 79.78, 21.96, 5.20, 1.10
};
double US_STD_density ( double alt_m ) {
return interpolate( alt_m, altitude_array, US_STD_density_array, NUM_ELEMENTS );
}
double US_STD_gravity( double alt_m) {
return interpolate( alt_m, altitude_array, US_STD_gravity_array, NUM_ELEMENTS );
}
double US_STD_temperature( double alt_m ) {
return interpolate( alt_m, altitude_array, US_STD_temperature_array, NUM_ELEMENTS );
}
double US_STD_pressure( double alt_m) {
return interpolate( alt_m, altitude_array, US_STD_pressure_array, NUM_ELEMENTS );
}

View File

@ -0,0 +1,49 @@
/************************************************************************
PURPOSE: (Simulate a hor-air balloon.)
LIBRARY DEPENDENCIES:
((balloon/src/Balloon.o))
**************************************************************************/
#ifndef BALLOON_HH
#define BALLOON_HH
class Balloon {
public:
// State Variables (Uncalculated Variables)
double pos[2];
double vel[2];
double envelope_mass;
double basket_mass;
double burner_system_mass;
double payload_mass;
double envelope_air_temperature;
double envelope_radius;
double envelope_theta;
double Cd;
// Calculated Variables
double acc[2];
double envelope_volume;
double fixed_mass;
// Control Variable
int temperature_change_command;
// Methods
int default_data();
int state_init();
int state_deriv();
int state_integ();
int check_ground_contact();
int control();
double calc_fixed_mass();
double calc_envelope_volume();
double calc_total_mass();
double calc_envelope_air_mass();
double calc_heated_air_density();
double calc_buoyancy_force();
double calc_displaced_air_mass();
double calc_drag_force();
double volume_of_a_spherical_dome( double r, double h);
double volume_of_a_cone( double r, double h);
};
#endif

View File

@ -0,0 +1,134 @@
/********************************* TRICK HEADER *******************************
PURPOSE: ( Simulate a hot-air balloon. )
LIBRARY DEPENDENCY:
((Balloon.o))
*******************************************************************************/
#include "balloon/include/Balloon.hh"
#include "atmosphere/include/atmosphere.h"
#include "trick/integrator_c_intf.h"
#include <stdlib.h>
#include <math.h>
#include <iostream>
int Balloon::default_data() {
pos[0] = 0.0;
pos[1] = 2.0;
vel[0] = 0.0;
vel[1] = 0.0;
envelope_mass = 113.4;
basket_mass = 63.5;
burner_system_mass = 206.4;
payload_mass = 300.0;
envelope_air_temperature = 80;
envelope_radius = 8.5;
envelope_theta = 45 * (M_PI/180.0);
Cd = 0.5;
return (0);
}
int Balloon::state_init() {
envelope_volume = calc_envelope_volume();
fixed_mass = calc_fixed_mass();
return (0);
}
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();
acc[0] = 0.0;
acc[1] = (F_gravity + F_buoyancy + F_drag) / total_mass; /* Equation #1 */
return(0);
}
int Balloon::state_integ() {
int integration_step;
load_state ( &pos[0], &pos[1], &vel[0], &vel[1], (double*)0);
load_deriv ( &vel[0], &vel[1], &acc[0], &acc[1], (double*)0);
integration_step = integrate();
unload_state( &pos[0], &pos[1], &vel[0], &vel[1], (double*)0);
return(integration_step);
}
double Balloon::calc_total_mass() {
return (fixed_mass + calc_envelope_air_mass());
}
double Balloon::calc_envelope_air_mass() {
double heated_air_density = calc_heated_air_density();
return ( heated_air_density * envelope_volume ); /* Equation #4 with density of heated air. */
}
double Balloon::calc_envelope_volume() {
double h;
h = envelope_radius * (1.0 + sin( envelope_theta));
double v_dome = volume_of_a_spherical_dome(envelope_radius, h);
double r = envelope_radius * cos(envelope_theta);
h = r / tan(envelope_theta);
double v_cone = volume_of_a_cone(r, h);
return (v_dome + v_cone); /* Equation #7 */
}
double Balloon::volume_of_a_spherical_dome( double r, double h) {
return (M_PI*((r*h*h)-((h*h*h)/3.0))); /* Equation #5 */
}
double Balloon::volume_of_a_cone( double r, double h) {
return ((M_PI*r*r*h)/3.0); /* Equation #6 */
}
double Balloon::calc_heated_air_density() {
double t_k = envelope_air_temperature + 273.15;
double rho = US_STD_pressure(pos[1]) / (287.055 * t_k); /* Equation #8 */
return rho;
}
double Balloon::calc_buoyancy_force() {
return( calc_displaced_air_mass() * US_STD_gravity( pos[1])); /* Equation 9 */
}
double Balloon::calc_displaced_air_mass() {
return( US_STD_density(pos[1]) * envelope_volume); /* Equation #4 with standard air density. */
}
double Balloon::calc_fixed_mass() {
return (envelope_mass + basket_mass + burner_system_mass + payload_mass);
}
double Balloon::calc_drag_force() {
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 */
}
int Balloon::control() {
if ( temperature_change_command != 0) {
envelope_air_temperature += temperature_change_command;
}
if (envelope_air_temperature > 120.0) {
envelope_air_temperature = 120.0;
}
if (envelope_air_temperature < 80.0) {
envelope_air_temperature = 80.0;
}
return(0);
}
int Balloon::check_ground_contact() {
if (pos[1] < 2.0) {
pos[1] = 2.0;
vel[0] = 0.0;
vel[1] = 0.0;
}
return(0);
}

View File

@ -0,0 +1,598 @@
/*
* Trick
* 2021 (c) National Aeronautics and Space Administration (NASA)
*/
import java.awt.Color;
import java.awt.Graphics2D;
import java.awt.Graphics;
import java.awt.RenderingHints;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.BufferedOutputStream;
import java.io.BufferedReader;
import java.io.DataOutputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.Socket;
import java.util.*;
import javax.swing.BoxLayout;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JSlider;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
import javax.sound.sampled.*;
import java.net.URL;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.awt.geom.AffineTransform;
import javax.swing.text.NumberFormatter;
import java.text.NumberFormat;
import javax.swing.JFormattedTextField;
import java.awt.Dimension;
import javax.swing.BorderFactory;
import javax.swing.border.EtchedBorder;
import java.awt.Component;
/**
*
* @author penn
*/
class ScenePoly {
public Color color;
public int n;
public double[] x;
public double[] y;
}
class RangeView extends JPanel {
private int scale;
private Color skyColor;
private Color groundColor;
private Color envelope_color_1;
private Color envelope_color_2;
private Color basket_color;
private Color envelope_phong;
// Origin of world coordinates in jpanel coordinates.
private int worldOriginX;
private int worldOriginY;
private double[] balloonPos;
private double[] balloonVel;
private int envelopeAirTemp; /* degrees C */
private double envelope_radius;
private double envelope_theta;
private ScenePoly cone;
private ScenePoly upright; // The frame that connects the basket, burner, and balloon
private ScenePoly basket;
private int[] workPolyX, workPolyY;
// Controls
private int deltaTemp ;
/**
* Class constructor.
*/
public RangeView( int mapScale) {
setScale(mapScale);
deltaTemp = 0;
skyColor = new Color(184,202,231);
groundColor = new Color(100,140, 60);
balloonPos = new double[]
{0.0, 2.0};
balloonVel = new double[]
{0.0, 0.0};
envelope_color_1 = new Color(254,181, 36);
envelope_color_2 = new Color(251, 0, 21);
basket_color = new Color(220,180,120);
envelope_phong = new Color(220,220,220,80);
envelope_radius = 8.5;
envelope_theta = Math.toRadians(45.0);
envelopeAirTemp = 0;
cone = new ScenePoly();
cone.color = envelope_color_1;
cone.x = new double[4];
cone.y = new double[4];
cone.n = 4;
upright = new ScenePoly();
upright.color = Color.BLACK;
upright.x = new double[] {-0.40, 0.40, 0.50, 0.40, 0.30,-0.30,-0.40,-0.50};
upright.y = new double[] { 0.00, 0.00,-1.00,-1.00,-0.10,-0.10,-1.00,-1.00};
upright.n = 8;
basket = new ScenePoly();
basket.color = basket_color;
basket.x = new double[] {-0.50, 0.50, 0.50,-0.50};
basket.y = new double[] {-1.00,-1.00,-2.00,-2.00};
basket.n = 4;
workPolyX = new int[30];
workPolyY = new int[30];
}
// |jpanel_x| = |origin_x| + |scale 0 | * |cos(angle) -sin(angle)| * |world_x|
// |jpanel_y| |origin_y| | 0 -scale| |sin(angle) cos(angle)| |world_y|
public void drawScenePoly(Graphics2D g, ScenePoly p, double angle_r , double x, double y) {
for (int ii = 0; ii < p.n; ii++) {
workPolyX[ii] = (int)(worldOriginX + scale *
( Math.cos(angle_r) * p.x[ii] - Math.sin(angle_r) * p.y[ii] + x));
workPolyY[ii] = (int)(worldOriginY - scale *
( Math.sin(angle_r) * p.x[ii] + Math.cos(angle_r) * p.y[ii] + y));
}
g.setPaint(p.color);
g.fillPolygon(workPolyX, workPolyY, p.n);
}
public void drawSceneOval(Graphics2D g2d, Color color, double x, double y, double w, double h) {
g2d.setPaint(color);
g2d.fillOval( (int)(worldOriginX+scale*(x-w/2)), (int)(worldOriginY-scale*(y+h/2)), (int)(scale*w), (int)(scale*h));
}
public void incTemperature() { deltaTemp = 1; }
public void decTemperature() { deltaTemp = -1; }
public void resetDeltaTemp() { deltaTemp = 0; }
public int getDeltaTemp() { return deltaTemp; }
public void setballoonPos(double x, double y) {
balloonPos[0] = x;
balloonPos[1] = y;
}
public void setBalloonVel(double vx, double vy) {
balloonVel[0] = vx;
balloonVel[1] = vy;
}
public void setAirTemp(int temperature) {
envelopeAirTemp = temperature;
}
public void setScale (int mapScale) {
if (mapScale < 2) {
scale = 2;
} else if (mapScale > 128) {
scale = 128;
} else {
scale = mapScale;
}
repaint();
}
public int getScale() {
return scale;
}
private void doDrawing(Graphics g) {
Graphics2D g2d = (Graphics2D) g;
RenderingHints rh = new RenderingHints(
RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
rh.put(RenderingHints.KEY_RENDERING,
RenderingHints.VALUE_RENDER_QUALITY);
int ii, jj;
int width = getWidth();
int height = getHeight();
// |jpanel_x| = |origin_x| + |scale 0 | * |cos(angle) -sin(angle)| * |world_x|
// |jpanel_y| |origin_y| | 0 -scale| |sin(angle) cos(angle)| |world_y|
worldOriginX = (width/2) - (int)(scale * balloonPos[0]);
worldOriginY = (height/2) + (int)(scale * balloonPos[1]);
// ===============================================================================
// Draw Sky
// ===============================================================================
g2d.setPaint(skyColor);
g2d.fillRect(0, 0, width, worldOriginY);
// ===============================================================================
// Draw ground.
// ===============================================================================
g2d.setPaint(groundColor);
g2d.fillRect(0, worldOriginY, width, height);
// ===============================================================================
// Draw Balloon
// ===============================================================================
double r_cone_top = envelope_radius * Math.cos(envelope_theta);
double h_cone = r_cone_top / Math.tan(envelope_theta);
double h = envelope_radius * Math.sin(envelope_theta) + h_cone;
double balloon_diameter = 2.0 * envelope_radius;
// Draw Dome
drawSceneOval(g2d, envelope_color_1, balloonPos[0], balloonPos[1]+h, balloon_diameter*Math.cos(Math.toRadians( 0.0)), balloon_diameter);
drawSceneOval(g2d, envelope_color_2, balloonPos[0], balloonPos[1]+h, balloon_diameter*Math.cos(Math.toRadians(22.5)), balloon_diameter);
drawSceneOval(g2d, envelope_color_1, balloonPos[0], balloonPos[1]+h, balloon_diameter*Math.cos(Math.toRadians(37.5)), balloon_diameter);
drawSceneOval(g2d, envelope_color_2, balloonPos[0], balloonPos[1]+h, balloon_diameter*Math.cos(Math.toRadians(52.5)), balloon_diameter);
drawSceneOval(g2d, envelope_color_1, balloonPos[0], balloonPos[1]+h, balloon_diameter*Math.cos(Math.toRadians(67.5)), balloon_diameter);
drawSceneOval(g2d, envelope_color_2, balloonPos[0], balloonPos[1]+h, balloon_diameter*Math.cos(Math.toRadians(82.5)), balloon_diameter);
drawSceneOval(g2d, envelope_phong, balloonPos[0], balloonPos[1]+h+4.0, balloon_diameter*Math.cos(Math.toRadians( 40.0)), balloon_diameter*Math.cos(Math.toRadians( 60.0)));
// Draw Cone
cone.color = envelope_color_1;
double rt = r_cone_top * Math.cos( Math.toRadians( 0.0));
cone.x[0] = -rt;
cone.y[0] = h_cone;
cone.x[1] = rt;
cone.y[1] = h_cone;
cone.x[2] = 0.0;
cone.y[2] = 0.0;
cone.x[3] = 0.0;
cone.y[3] = 0.0;
drawScenePoly(g2d, cone, 0.0, balloonPos[0], balloonPos[1]);
cone.color = envelope_color_2;
rt = r_cone_top * Math.cos( Math.toRadians(22.5));
cone.x[0] = -rt;
cone.x[1] = rt;
drawScenePoly(g2d, cone, 0.0, balloonPos[0], balloonPos[1]);
cone.color = envelope_color_1;
rt = r_cone_top * Math.cos( Math.toRadians(37.5));
cone.x[0] = -rt;
cone.x[1] = rt;
drawScenePoly(g2d, cone, 0.0, balloonPos[0], balloonPos[1]);
cone.color = envelope_color_2;
rt = r_cone_top * Math.cos( Math.toRadians(52.5));
cone.x[0] = -rt;
cone.x[1] = rt;
drawScenePoly(g2d, cone, 0.0, balloonPos[0], balloonPos[1]);
cone.color = envelope_color_1;
rt = r_cone_top * Math.cos( Math.toRadians(67.5));
cone.x[0] = -rt;
cone.x[1] = rt;
drawScenePoly(g2d, cone, 0.0, balloonPos[0], balloonPos[1]);
cone.color = envelope_color_2;
rt = r_cone_top * Math.cos( Math.toRadians(82.5));
cone.x[0] = -rt;
cone.x[1] = rt;
drawScenePoly(g2d, cone, 0.0, balloonPos[0], balloonPos[1]);
// Draw Upright
drawScenePoly(g2d, upright, 0.0, balloonPos[0], balloonPos[1]);
// Draw Basket
drawScenePoly(g2d, basket, 0.0, balloonPos[0], balloonPos[1]);
// ===============================================================================
// Draw range markers.
// ===============================================================================
int tickRange = 50;
if (scale >= 8) tickRange = 20;
if (scale >= 16) tickRange = 10;
if (scale >= 32) tickRange = 5;
if (scale >= 64) tickRange = 1;
int lower = ((int)(( - worldOriginX)/(scale * tickRange)) + 1) * tickRange;
int upper = ((int)((width - worldOriginX)/(scale * tickRange)) + 1) * tickRange;
g2d.setPaint(Color.WHITE);
for (ii = lower ; ii < upper ; ii += tickRange) {
int mx = (int)(worldOriginX + scale * ii);
g2d.drawLine( mx, worldOriginY, mx, worldOriginY + 20);
g2d.drawString ( String.format("%d",ii), mx, worldOriginY + 15);
}
// ===============================================================================
// Draw Information
// ===============================================================================
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);
}
@Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
doDrawing(g);
}
}
class TrickSimMode {
public static final int INIT = 0;
public static final int FREEZE = 1;
public static final int RUN = 5;
}
class TemperatureCtrlPanel extends JPanel implements ActionListener {
private RangeView rangeView;
private JButton increaseTempButton, decreaseTempButton;
public TemperatureCtrlPanel(RangeView view) {
rangeView = view;
setLayout(new BoxLayout(this, BoxLayout.X_AXIS));
setBorder( BorderFactory.createEtchedBorder(EtchedBorder.RAISED));
increaseTempButton = new JButton("\u25b2");
increaseTempButton.addActionListener(this);
increaseTempButton.setActionCommand("increaseTemp");
increaseTempButton.setToolTipText("Increase Temperature");
decreaseTempButton = new JButton("\u25bc");
decreaseTempButton.addActionListener(this);
decreaseTempButton.setActionCommand("decreaseTemp");
decreaseTempButton.setToolTipText("Decrease Temperature");
add(increaseTempButton);
add(decreaseTempButton);
}
public void actionPerformed(ActionEvent e) {
String s = e.getActionCommand();
switch (s) {
case "increaseTemp":
rangeView.incTemperature();
break;
case "decreaseTemp":
rangeView.decTemperature();
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;
public ControlPanel(RangeView view) {
rangeView = view;
setLayout(new BoxLayout(this, BoxLayout.X_AXIS));
JPanel labeledTemperatureCtrlPanel = new JPanel();
labeledTemperatureCtrlPanel.setLayout(new BoxLayout(labeledTemperatureCtrlPanel, BoxLayout.Y_AXIS));
JLabel temperatureControlLabel = new JLabel("Temperature Control");
temperatureControlLabel.setAlignmentX(Component.CENTER_ALIGNMENT);
labeledTemperatureCtrlPanel.add(temperatureControlLabel);
temperatureCtrlPanel = new TemperatureCtrlPanel(rangeView);
labeledTemperatureCtrlPanel.add( temperatureCtrlPanel );
add(labeledTemperatureCtrlPanel);
zoomOutButton = new JButton("Zoom Out");
zoomOutButton.addActionListener(this);
zoomOutButton.setActionCommand("zoomout");
zoomOutButton.setToolTipText("Zoom Out");
add(zoomOutButton);
zoomInButton = new JButton("Zoom In");
zoomInButton.addActionListener(this);
zoomInButton.setActionCommand("zoomin");
zoomInButton.setToolTipText("Zoom In");
add(zoomInButton);
}
public void actionPerformed(ActionEvent e) {
String s = e.getActionCommand();
switch (s) {
case "zoomout":
rangeView.setScale( rangeView.getScale() / 2 );
break;
case "zoomin":
rangeView.setScale( rangeView.getScale() * 2 );
break;
default:
System.out.println("Unknown Action Command:" + s);
break;
}
}
} // class ControlPanel
public class BalloonDisplay extends JFrame {
private RangeView rangeView;
private BufferedReader in;
private DataOutputStream out;
private JPanel panelGroup0;
private JPanel panelGroup1;
private ControlPanel controlPanel;
public BalloonDisplay(RangeView arena) {
setTitle("Balloon Range");
rangeView = arena;
panelGroup1 = new JPanel();
panelGroup1.setLayout(new BoxLayout(panelGroup1, BoxLayout.X_AXIS));
panelGroup1.add(rangeView);
controlPanel = new ControlPanel(rangeView);
panelGroup0 = new JPanel();
panelGroup0.setLayout(new BoxLayout(panelGroup0, BoxLayout.Y_AXIS));
panelGroup0.add(panelGroup1);
panelGroup0.add(controlPanel);
add(panelGroup0);
rangeView.setScale(8);
rangeView.setballoonPos (0.0, 2.0);
setSize(800, 500);
setLocationRelativeTo(null);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setFocusable(true);
}
public void connectToServer(String host, int port ) throws IOException {
Socket socket = new Socket(host, port);
in = new BufferedReader( new InputStreamReader( socket.getInputStream()));
out = new DataOutputStream(new BufferedOutputStream(socket.getOutputStream()));
}
public void drawRangeView() {
rangeView.repaint();
}
private static void printHelpText() {
System.out.println(
"----------------------------------------------------------------------\n"
+ "usage: java jar BalloonDisplay.jar <port-number>\n"
+ "----------------------------------------------------------------------\n"
);
}
public enum ModelState { INACTIVE, READY, ACTIVE }
public static void main(String[] args) throws IOException, InterruptedException {
String host = "localHost";
int port = 0;
boolean boom = false;
// ==========================================================
// Handle program arguments.
// ==========================================================
int ii = 0;
while (ii < args.length) {
switch (args[ii]) {
case "-help" :
case "--help" : {
printHelpText();
System.exit(0);
} break;
default : {
port = (Integer.parseInt(args[ii]));
} break;
}
++ii;
}
boolean go = true;
double dt = 0.100; // Time between updates (seconds).
double posx = 0.0;
double posy = 0.0;
double velx = 0.0;
double vely = 0.0;
int airtemp = 0;
// Outbound command variables
int temperature_change_command;
int simMode = 0;
boolean standalone = false;
int mapScale = 32 ; // pixels per meter.
RangeView rangeView = new RangeView( mapScale);
BalloonDisplay balloonDisplay = new BalloonDisplay( rangeView);
balloonDisplay.setVisible(true);
balloonDisplay.drawRangeView();
if (port == 0) {
System.out.println("No variable server port specified.");
printHelpText();
System.exit(0);
}
// Connect to the Trick simulation's variable server
System.out.println("Connecting to: " + host + ":" + port);
balloonDisplay.connectToServer(host, port);
balloonDisplay.out.writeBytes("trick.var_set_client_tag(\"BalloonDisplay\") \n");
balloonDisplay.out.flush();
// Have the Variable Server send us the simulation mode ONCE.
balloonDisplay.out.writeBytes( "trick.var_add(\"trick_sys.sched.mode\")\n" +
"trick.var_send() \n" +
"trick.var_clear() \n");
balloonDisplay.out.flush();
// Read the response and extract the simulation mode.
try {
String line;
String field[];
line = balloonDisplay.in.readLine();
field = line.split("\t");
simMode = Integer.parseInt( field[1]);
} catch (IOException | NullPointerException e ) {
go = false;
}
// Configure the Variable Server to cyclically send us the following varibales.
// Tell the variable server:
// 1) We want the values of the following variables:
balloonDisplay.out.writeBytes( "trick.var_pause() \n" +
"trick.var_add(\"dyn.balloon.pos[0]\")\n" +
"trick.var_add(\"dyn.balloon.pos[1]\")\n" +
"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(\"trick_sys.sched.mode\")\n" +
// 2) We want the responses in ASCII:
"trick.var_ascii() \n" +
// 3) We want values to be updated at the specified rate:
String.format("trick.var_cycle(%.3f)\n", dt) +
// 4) Start sending values as specified.
"trick.var_unpause() \n" );
balloonDisplay.out.flush();
while (go) {
// Recieve and parse periodic data response from the variable server.
try {
String line;
String field[];
line = balloonDisplay.in.readLine();
field = line.split("\t");
posx = Double.parseDouble( field[1]);
posy = Double.parseDouble( field[2]);
velx = Double.parseDouble( field[3]);
vely = Double.parseDouble( field[4]);
airtemp = Integer.parseInt( field[5]);
simMode = Integer.parseInt( field[6]);
} catch (IOException | NullPointerException e ) {
go = false;
}
// Update the display data.
rangeView.setballoonPos(posx, posy);
rangeView.setBalloonVel(velx, vely);
rangeView.setAirTemp(airtemp);
temperature_change_command = rangeView.getDeltaTemp();
balloonDisplay.out.writeBytes( String.format("dyn.balloon.temperature_change_command = %d ;\n", temperature_change_command ));
rangeView.resetDeltaTemp();
balloonDisplay.out.flush();
// Update the scene.
balloonDisplay.drawRangeView();
} // while
} // main
} // class