added new mass-spring-damper sim and graphics client (#555)

* added new mass-spring-damper sim and graphics client

* changed math model image for README.md

* updated math model image for README.md
This commit is contained in:
Scott Fennell 2018-01-31 13:37:14 -06:00 committed by jmpenn
parent 8f76605f3b
commit aa36c31231
26 changed files with 1125 additions and 0 deletions

View File

@ -0,0 +1,7 @@
trick.frame_log_on()
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)
trick.sim_control_panel_set_enabled(True)

View File

@ -0,0 +1,84 @@
# SIM_MSD
SIM\_MSD is a simulator for a mass-spring-damper system.
![Picture of MSD](./images/MSD0.png)
The system is described by the equation _mẍ_ + _bẋ_ + _kx_ = _F_, where:
_m_ is the mass of an object.
_x_ is the position of the mass.
_b_ is the damping coefficient.
_k_ is the spring coefficient.
_F_ is an external force applied to the mass.
---
### Building the Simulation
In the SIM_MSD directory, execute **trick-CP** to build the simulation executable. When it's complete, you should see:
```
=== Simulation make complete ===
```
Now **cd** into models/Graphics/ and type **make**. This builds the display client for the simulation.
### Running the Simulation
In the SIM_MSD directory:
```
% ./S_main_*.exe RUN_test/input.py
```
The following windows should appear:
![Picture of MSD](./images/MSD2.png)
![Picture of Control](./images/MSD3.png)
### Controlling the Simulation
Click **Start** to begin the simulation.
Click **Reset** to freeze the simulation and reset the position and velocity to
their initial conditions.
Change **Position** and **Velocity** to adjust the initial conditions for those
attributes.
Change **Mass**, **Spring Coefficient**, **Damping Coefficient**, and **Applied
Force** to adjust initial conditions _**and**_ also update the sim in real-time.
---
The following inputs can be set in any of the RUN_*/input.py files to change
the inputs on startup
### Inputs
Variable | Type | Units | Default Value
---------- |-----------|-------|--------------
dyn.msd.x_0 | double | m | 5.0
dyn.msd.v_0 | double | m/s | 0.0
dyn.msd.b | double | N.s/m | 0.5
dyn.msd.k | double | N/m | 2.0
dyn.msd.F | double | N | 5.0
dyn.msd.m | double | kg | 1.0
---
Some sample input.py files have been created to demonstrate different
mass-spring-damper conditions
### Samples
Path |Type
------------------------|----------------------------------------------
RUN_underdamp/input.py |Underdamped Harmonic Oscillator
RUN_critdamp/input.py |Critically Damped Harmonic Oscillator
RUN_overdamp/input.py |Overdamped Harmonic Oscillator
RUN_nodamp/input.py | Simple Harmonic Oscillator (No Damping Force)

View File

@ -0,0 +1,23 @@
dyn_integloop.getIntegrator(trick.Runge_Kutta_4, 4)
execfile("Modified_data/realtime.py")
dyn.msd.b = 2*(2**(0.5))
#==========================================
# Start the display VarServer Client
#==========================================
varServerPort = trick.var_server_get_port();
MsdGui_path = "models/graphics/dist/MsdGui.jar"
if (os.path.isfile(MsdGui_path)) :
MsdGui_cmd = "java -jar " \
+ MsdGui_path \
+ " " + str(varServerPort) + " &" ;
print(MsdGui_cmd)
os.system( MsdGui_cmd);
else :
print('==================================================================================')
print('MsdGui needs to be built. Please \"cd\" into models/Graphics and type \"make\".')
print('==================================================================================')
trick.stop(300.0)

View File

@ -0,0 +1,23 @@
dyn_integloop.getIntegrator(trick.Runge_Kutta_4, 4)
execfile("Modified_data/realtime.py")
dyn.msd.b = 0
#==========================================
# Start the display VarServer Client
#==========================================
varServerPort = trick.var_server_get_port();
MsdGui_path = "models/graphics/dist/MsdGui.jar"
if (os.path.isfile(MsdGui_path)) :
MsdGui_cmd = "java -jar " \
+ MsdGui_path \
+ " " + str(varServerPort) + " &" ;
print(MsdGui_cmd)
os.system( MsdGui_cmd);
else :
print('==================================================================================')
print('MsdGui needs to be built. Please \"cd\" into models/Graphics and type \"make\".')
print('==================================================================================')
trick.stop(300.0)

View File

@ -0,0 +1,23 @@
dyn_integloop.getIntegrator(trick.Runge_Kutta_4, 4)
execfile("Modified_data/realtime.py")
dyn.msd.b = 4*(2**(0.5)) - 1/2
#==========================================
# Start the display VarServer Client
#==========================================
varServerPort = trick.var_server_get_port();
MsdGui_path = "models/graphics/dist/MsdGui.jar"
if (os.path.isfile(MsdGui_path)) :
MsdGui_cmd = "java -jar " \
+ MsdGui_path \
+ " " + str(varServerPort) + " &" ;
print(MsdGui_cmd)
os.system( MsdGui_cmd);
else :
print('==================================================================================')
print('MsdGui needs to be built. Please \"cd\" into models/Graphics and type \"make\".')
print('==================================================================================')
trick.stop(300.0)

View File

@ -0,0 +1,21 @@
dyn_integloop.getIntegrator(trick.Runge_Kutta_4, 4)
execfile("Modified_data/realtime.py")
#==========================================
# Start the display VarServer Client
#==========================================
varServerPort = trick.var_server_get_port();
MsdGui_path = "models/graphics/dist/MsdGui.jar"
if (os.path.isfile(MsdGui_path)) :
MsdGui_cmd = "java -jar " \
+ MsdGui_path \
+ " " + str(varServerPort) + " &" ;
print(MsdGui_cmd)
os.system( MsdGui_cmd);
else :
print('==================================================================================')
print('MsdGui needs to be built. Please \"cd\" into models/Graphics and type \"make\".')
print('==================================================================================')
trick.stop(300.0)

View File

@ -0,0 +1,23 @@
dyn_integloop.getIntegrator(trick.Runge_Kutta_4, 4)
execfile("Modified_data/realtime.py")
dyn.msd.b = 0.5
#==========================================
# Start the display VarServer Client
#==========================================
varServerPort = trick.var_server_get_port();
MsdGui_path = "models/graphics/dist/MsdGui.jar"
if (os.path.isfile(MsdGui_path)) :
MsdGui_cmd = "java -jar " \
+ MsdGui_path \
+ " " + str(varServerPort) + " &" ;
print(MsdGui_cmd)
os.system( MsdGui_cmd);
else :
print('==================================================================================')
print('MsdGui needs to be built. Please \"cd\" into models/Graphics and type \"make\".')
print('==================================================================================')
trick.stop(300.0)

View File

@ -0,0 +1,33 @@
/************************TRICK HEADER*************************
PURPOSE:
(This S_define works with the RUN_numeric input file)
LIBRARY DEPENDENCIES:
(
(msd/src/msd_init.cpp)
(msd/src/msd_numeric.cpp)
(msd/src/msd_shutdown.cpp)
(msd/src/msd.cpp)
(msd/src/msd_deriv.cpp)
(msd/src/msd_integ.cpp)
)
*************************************************************/
#include "sim_objects/default_trick_sys.sm"
##include "msd/include/msd_numeric.hh"
class MSDSimObject : public Trick::SimObject {
public:
MSD msd;
MSDSimObject() {
("default_data") msd_default_data( msd ) ;
("initialization") msd_init( msd ) ;
("derivative") msd.state_deriv();
("integration") trick_ret= msd.state_integ();
}
} ;
MSDSimObject dyn ;
IntegLoop dyn_integloop (0.01) dyn ;

View File

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

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 27 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

View File

@ -0,0 +1,200 @@
/*
* Trick
* 2018 (c) National Aeronautics and Space Administration (NASA)
* Programmers: Scott P. Fennell
*/
import javax.swing.*;
import javax.swing.JTextField;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
public class MsdController extends JFrame {
JTextField positionTextField;
JTextField velocityTextField;
JTextField springTextField;
JTextField dampingTextField;
JTextField forceTextField;
JTextField massTextField;
ButtonPanel buttonPanel;
JPanel positionPanel;
JPanel labelPanel;
public void armResetCommand() {
buttonPanel.armResetCommand();
}
public void armStartCommand() {
buttonPanel.armStartCommand();
}
public boolean getResetCommand() {
return buttonPanel.getResetCommand();
}
public boolean getStartCommand() {
return buttonPanel.getStartCommand();
}
public MsdController(MsdDisplay display){
setTitle("Mass-Spring-Damper Control");
setLayout(new GridLayout(1, 2));
buttonPanel = new ButtonPanel(display);
positionPanel = new JPanel();
labelPanel = new JPanel();
labelPanel.setAlignmentX(Component.RIGHT_ALIGNMENT);
positionPanel.setLayout(new GridLayout(13, 1));
labelPanel.setLayout(new GridLayout(13, 1));
JPanel framePanel = new JPanel();
GridLayout layout = new GridLayout(1, 3);
framePanel.setLayout(layout);
positionTextField = new JTextField(2);
velocityTextField = new JTextField(2);
massTextField = new JTextField(2);
forceTextField = new JTextField(2);
springTextField = new JTextField(2);
dampingTextField = new JTextField(2);
labelPanel.add(new JLabel());
labelPanel.add(new JLabel("Position: "));
labelPanel.add(new JLabel());
labelPanel.add(new JLabel("Velocity: "));
labelPanel.add(new JLabel());
labelPanel.add(new JLabel("Mass: "));
labelPanel.add(new JLabel());
labelPanel.add(new JLabel("Spring Coefficient: "));
labelPanel.add(new JLabel());
labelPanel.add(new JLabel("Damping Coefficient: "));
labelPanel.add(new JLabel());
labelPanel.add(new JLabel("Applied Force: "));
labelPanel.add(new JLabel());
positionPanel.add(new JLabel());
positionPanel.add(positionTextField);
positionPanel.add(new JLabel());
positionPanel.add(velocityTextField);
positionPanel.add(new JLabel());
positionPanel.add(massTextField);
positionPanel.add(new JLabel());
positionPanel.add(springTextField);
positionPanel.add(new JLabel());
positionPanel.add(dampingTextField);
positionPanel.add(new JLabel());
positionPanel.add(forceTextField);
positionPanel.add(new JLabel());
for(Component L : labelPanel.getComponents()){
if(L instanceof JLabel){
((JLabel)L).setHorizontalAlignment(JLabel.RIGHT);
}
}
framePanel.add(buttonPanel);
framePanel.add(labelPanel);
framePanel.add(positionPanel);
add(framePanel);
positionPanel.setSize(positionPanel.getLayout().preferredLayoutSize(positionPanel));
buttonPanel.setSize(buttonPanel.getPreferredSize());
setSize(getPreferredSize());
setLocationByPlatform(true);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
}
class ButtonPanel extends JPanel implements ActionListener {
private MsdDisplay msdDisplay;
private boolean startCommand;
private boolean resetCommand;
private JButton startButton, resetButton, zoomOutButton, zoomInButton;
public ButtonPanel(MsdDisplay cd) {
msdDisplay = cd;
startCommand = false;
resetCommand = false;
setLayout(new GridLayout(4,1 ));
startButton = new JButton("Start");
startButton.addActionListener(this);
startButton.setActionCommand("start");
startButton.setToolTipText("Start MSD");
add(startButton);
resetButton = new JButton("Reset");
resetButton.addActionListener(this);
resetButton.setActionCommand("reset");
resetButton.setToolTipText("Reset MSD");
add(resetButton);
zoomOutButton = new JButton("\u25b2");
zoomOutButton.addActionListener(this);
zoomOutButton.setActionCommand("zoomout");
zoomOutButton.setToolTipText("Zoom in");
add(zoomOutButton);
zoomInButton = new JButton("\u25bc");
zoomInButton.addActionListener(this);
zoomInButton.setActionCommand("zoomin");
zoomInButton.setToolTipText("Zoom Out");
add(zoomInButton);
}
public void actionPerformed(ActionEvent e) {
String s = e.getActionCommand();
switch (s) {
case "start":
startCommand = true;
break;
case "reset":
resetCommand = true;
break;
case "zoomin":
msdDisplay.setScale(msdDisplay.getScale() / 2);
break;
case "zoomout":
msdDisplay.setScale(msdDisplay.getScale() * 2);
break;
default:
System.out.println("Unknown Action Command:" + s);
break;
}
}
public void armResetCommand() {
resetCommand = false;
startButton.setForeground(Color.red);
}
public void armStartCommand() {
startCommand = false;
startButton.setForeground(Color.gray);
}
public boolean getResetCommand() {
return resetCommand;
}
public boolean getStartCommand() {
return startCommand;
}
} // class ButtonPanel
}

View File

@ -0,0 +1,50 @@
/*
* Trick
* 2018 (c) National Aeronautics and Space Administration (NASA)
* Programmers: Scott P. Fennell
*/
import javax.swing.*;
public class MsdDisplay extends JFrame {
MsdView msdView;
public void setPosition(double value) {
msdView.setPosition(value);
}
public void setVelocity(double value) {
msdView.setVelocity(value);
}
public void setScale(int value) {
msdView.setScale(value);
}
public int getScale() {
return msdView.getScale();
}
public void drawMsdView() {
msdView.repaint();
}
public MsdDisplay(MsdView arena) {
setTitle("Mass-Spring-Damper");
msdView = arena;
add(msdView);
setSize(800, 500);
setLocationByPlatform(true);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
}
}

View File

@ -0,0 +1,218 @@
/*
* Trick
* 2018 (c) National Aeronautics and Space Administration (NASA)
* Programmers: Scott P. Fennell
*/
import java.awt.*;
import java.io.*;
import java.net.Socket;
public class MsdGui {
static BufferedReader in;
static DataOutputStream out;
public static 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 static void main(String[] args) throws IOException {
String host = "localHost";
int port = 0;
// ==========================================================
// Handle program arguments.
// ==========================================================
int ii = 0;
while (ii < args.length) {
switch (args[ii]) {
case "-help":
case "--help": {
MsdUtils.printHelpText();
System.exit(0);
}
break;
default: {
port = (Integer.parseInt(args[ii]));
}
break;
}
++ii;
}
if (port == 0) {
System.out.println("No variable server port specified.");
MsdUtils.printHelpText();
System.exit(0);
}
boolean go = true;
double dt = 0.01; // Time between updates (seconds).
double pos = 0.0;
double vel = 0.0;
boolean resetCommand;
boolean startCommand;
int simMode = 0;
int mapScale = 32; // pixels per meter.
MsdView msdView = new MsdView(mapScale);
MsdDisplay display = new MsdDisplay(msdView);
MsdController controller = new MsdController(display);
display.setVisible(true);
display.drawMsdView();
controller.setVisible(true);
controller.setLocation(new Point((int)display.getLocationOnScreen().getX() + display.getWidth(), (int)display.getLocationOnScreen().getY()));
controller.setSize(controller.getWidth() + 10, display.getHeight());
// Connect to the Trick simulation's variable server
System.out.println("Connecting to: " + host + ":" + port);
connectToServer(host, port);
out.writeBytes("trick.var_set_client_tag(\"MsdGui\") \n");
out.flush();
// Have the Variable Server send us the simulation mode ONCE.
out.writeBytes("trick.var_add(\"trick_sys.sched.mode\")\n" +
"trick.var_add(\"dyn.msd.x_0\")\n" +
"trick.var_add(\"dyn.msd.v_0\")\n" +
"trick.var_add(\"dyn.msd.m\")\n" +
"trick.var_add(\"dyn.msd.b\")\n" +
"trick.var_add(\"dyn.msd.k\")\n" +
"trick.var_add(\"dyn.msd.F\")\n" +
"trick.var_send() \n" +
"trick.var_clear() \n");
out.flush();
// Read the response and extract the simulation mode. Initialize text fields with initial values.
try {
String line;
String field[];
line = in.readLine();
field = line.split("\t");
simMode = Integer.parseInt(field[1]);
controller.positionTextField.setText(String.format("%.2f", new Double(field[2])));
controller.velocityTextField.setText(String.format("%.2f", new Double(field[3])));
controller.massTextField.setText(String.format("%.2f", new Double(field[4])));
controller.dampingTextField.setText(String.format("%.2f", new Double(field[5])));
controller.springTextField.setText(String.format("%.2f", new Double(field[6])));
controller.forceTextField.setText(String.format("%.2f", new Double(field[7])));
} catch (IOException | NullPointerException e) {
go = false;
}
// If we're in FREEZE, make ready to start.
if (simMode == TrickSimMode.FREEZE) {
controller.armResetCommand();
}
// Configure the Variable Server to cyclically send us the following variables.
// Tell the variable server:
// 1) We want the values of the following variables:
out.writeBytes("trick.var_pause() \n" +
"trick.var_add(\"dyn.msd.x\")\n" +
"trick.var_add(\"dyn.msd.v\")\n" +
"trick.var_add(\"trick_sys.sched.mode\")\n" +
// 2) We want the responses in ASCII:
"trick.var_ascii() \n" +
/* 3) We want debug information:
"trick.var_debug(1) \n" +
*/
// 4) We want values to be updated at the specified rate:
String.format("trick.var_cycle(%.3f)\n", dt) +
// 5) Start sending values as specified.
"trick.var_unpause() \n");
out.flush();
while (go) {
// Receive and parse periodic data response from the variable server.
try {
String line;
String field[];
line = in.readLine();
field = line.split("\t");
pos = Double.parseDouble(field[1]);
vel = Double.parseDouble(field[2]);
simMode = Integer.parseInt(field[3]);
} catch (IOException | NullPointerException e) {
go = false;
}
// Get inputs from the GUI.
resetCommand = controller.getResetCommand();
startCommand = controller.getStartCommand();
if (simMode == TrickSimMode.FREEZE) {
controller.armResetCommand();
resetCommand = false;
if (!controller.positionTextField.getText().isEmpty() && new Double(controller.positionTextField.getText()) >= 0.0) {
display.msdView.hideWall = false;
}
if (!(controller.positionTextField.getText().isEmpty() ||
controller.velocityTextField.getText().isEmpty() ||
controller.springTextField.getText().isEmpty() ||
controller.dampingTextField.getText().isEmpty() ||
controller.massTextField.getText().isEmpty() ||
controller.forceTextField.getText().isEmpty())) {
out.writeBytes(
"dyn.msd.x_0 = " + controller.positionTextField.getText() + ";\n" +
"dyn.msd.v_0 = " + controller.velocityTextField.getText() + ";\n" +
"dyn.msd.k = " + controller.springTextField.getText() + ";\n" +
"dyn.msd.b = " + controller.dampingTextField.getText() + ";\n" +
"dyn.msd.m = " + controller.massTextField.getText() + ";\n" +
"dyn.msd.F = " + controller.forceTextField.getText() + ";\n");
out.writeBytes("dyn.msd.x = dyn.msd.x_0 ;\n");
out.writeBytes("dyn.msd.v = dyn.msd.v_0 ;\n");
}
if (startCommand) {
out.writeBytes(String.format("trick.exec_run();\n"));
out.flush();
}
if (resetCommand) {
out.writeBytes(String.format("trick.exec_freeze() ;\n"));
continue;
}
out.flush();
} else if (simMode == TrickSimMode.RUN) {
controller.armStartCommand();
startCommand = false;
if (resetCommand) {
out.writeBytes(String.format("trick.exec_freeze() ;\n"));
out.flush();
}
if (!(controller.springTextField.getText().isEmpty() ||
controller.dampingTextField.getText().isEmpty() ||
controller.massTextField.getText().isEmpty() ||
controller.forceTextField.getText().isEmpty())) {
out.writeBytes(
"dyn.msd.k = " + controller.springTextField.getText() + ";\n" +
"dyn.msd.b = " + controller.dampingTextField.getText() + ";\n" +
"dyn.msd.m = " + controller.massTextField.getText() + ";\n" +
"dyn.msd.F = " + controller.forceTextField.getText() + ";\n");
}
}
// Update the display data.
display.setPosition(pos);
display.setVelocity(vel);
// Update the scene.
display.drawMsdView();
} // while
}
}

View File

@ -0,0 +1,24 @@
/*
* Trick
* 2018 (c) National Aeronautics and Space Administration (NASA)
* Programmers: Scott P. Fennell
*/
public class MsdUtils {
public static void printHelpText() {
System.out.println(
"----------------------------------------------------------------------\n"
+ "usage: java jar CannonDisplay.jar <port-number>\n"
+ "----------------------------------------------------------------------\n"
);
}
}
class TrickSimMode {
public static final int INIT = 0;
public static final int FREEZE = 1;
public static final int RUN = 5;
}

View File

@ -0,0 +1,183 @@
/*
* Trick
* 2018 (c) National Aeronautics and Space Administration (NASA)
* Programmers: Scott P. Fennell
*/
import javax.swing.*;
import java.awt.*;
class MsdView extends JPanel {
private int scale;
private Color massColor;
private Color backgroundColor;
private Color wallColor;
private Color springColor;
private Double position;
private Double velocity;
private Double massRadius;
boolean hideWall = false;
// Origin of world coordinates in jpanel coordinates.
private int worldOriginX;
private int worldOriginY;
/**
* Class constructor.
*/
public MsdView(int mapScale) {
setScale(mapScale);
backgroundColor = new Color(200, 200, 255);
springColor = new Color(150, 150, 100);
massColor = new Color(10, 10, 10);
wallColor = new Color(120, 118, 118);
springColor = Color.BLUE;
massRadius = 0.75;
position = 0.0;
velocity = 0.0;
}
/**
* @param value angle in degrees
*/
public void setPosition(double value) {
position = value;
}
public void setVelocity(double value) {
velocity = value;
}
public void setScale(int mapScale) {
if (mapScale < 4) {
scale = 4;
} else if (mapScale > 128) {
scale = 128;
} else {
scale = mapScale;
}
repaint();
}
public int getScale() {
return scale;
}
public void drawCenteredCircle(Graphics2D g, int x, int y, int r) {
x = x - (r / 2);
y = y - (r / 2);
g.fillOval(x, y, r, r);
}
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();
worldOriginX = (width / 3);
worldOriginY = (height / 2);
while (worldOriginX + scale * position > width) {
scale = scale / 2;
}
// Draw Sky
g2d.setPaint(backgroundColor);
g2d.fillRect(0, 0, width, worldOriginY);
// Draw mass
g2d.setPaint(massColor);
int bx = (int) (worldOriginX + scale * position);
int by = (int) (worldOriginY);
drawCenteredCircle(g2d, bx, by, (int) (scale * massRadius));
// Draw spring
double springPosition;
// reflect spring visual over origin correctly
if(position < massRadius/2 && position > -massRadius/2){
springPosition = 0.0;
} else if(position < 0){
springPosition = position + massRadius/2;
} else{
springPosition = position - massRadius/2;
}
int SCALEFACTOR = 200;
int cycles = 5;
int points = SCALEFACTOR * cycles * 2;
double[] sines = new double[points];
for (int i = 0; i < points; i++) {
double radians = (Math.PI / SCALEFACTOR) * i;
sines[i] = Math.sin(radians);
}
int maxWidth = (int) (scale * (springPosition /*- massRadius / 2*/));
double hstep = (double) maxWidth / (double) points;
int maxHeight = (int) (scale);
int[] pts = new int[points];
for (int i = 0; i < points; i++) {
pts[i] = (int) (sines[i] * maxHeight / 2 * .95 + maxHeight / 2);
}
g2d.setColor(springColor);
for (int i = 1; i < points; i++) {
int x1 = (int) ((i - 1) * hstep);
int x2 = (int) (i * hstep);
int y1 = pts[i - 1];
int y2 = pts[i];
g2d.drawLine(x1 + worldOriginX, y1 + height / 2 - scale / 2, x2 + worldOriginX, y2 + height / 2 - scale / 2);
}
//Draw wall
if (!hideWall && position >= 0) {
g2d.setPaint(wallColor);
g2d.fillRect(0, 0, worldOriginX, height);
} else {
hideWall = true;
}
// 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.BLACK);
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);
}
g2d.drawString(String.format("SCALE: %d pixels/meter", scale), 20, 20);
g2d.drawString(String.format(" Position (m): %.2f", position), 20, 80);
g2d.drawString(String.format("Velocity (m/s): %.2f", velocity), 20, 120);
}
@Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
doDrawing(g);
}
}

View File

@ -0,0 +1,36 @@
SHELL = /bin/sh
PROJECT_NAME = MsdGui
SRC_DIR = MSD/src
BUILD_DIR = build
CLASSES_DIR = $(BUILD_DIR)/classes
JAR_DIR = dist
MAIN_CLASS = MsdGui
all: jar
clean:
rm -rf $(BUILD_DIR)
rm -f manifest
spotless: clean
rm -rf dist
$(CLASSES_DIR):
@ mkdir -p $(CLASSES_DIR)
compile: | $(CLASSES_DIR)
javac -sourcepath $(SRC_DIR) -d $(CLASSES_DIR) $(SRC_DIR)/MsdGui.java
manifest:
@ echo "Main-Class: $(MAIN_CLASS)" > $@
$(JAR_DIR):
@ mkdir -p $(JAR_DIR)
jar: compile manifest | $(JAR_DIR)
jar cvfm $(JAR_DIR)/$(PROJECT_NAME).jar manifest -C $(CLASSES_DIR) .
@ echo "-------------------------------------------------------------------------------"
@ echo " BUILD COMPLETE"
@ echo "The Java jar file (the Java Executable) is located at: $(JAR_DIR)/$(PROJECT_NAME).jar"
@ echo "-------------------------------------------------------------------------------"

View File

@ -0,0 +1,48 @@
/************************TRICK HEADER*************************
PURPOSE: Mass-Spring-Damper class header file .
PROGRAMMERS:
(((Scott P. Fennell) (CACI International Inc.) (January 2018) (Trick Learning Project)))
*************************************************************/
#ifndef MSD_H
#define MSD_H
class MSD {
public:
double a; /* m/s2 acceleration */
double v; /* m/s velocity */
double x; /* m position */
double m; /* kg mass */;
double v_0; /* *i m/s initial velocity of the mass (at t = 0) */
double x_0; /* *i m initial position of the mass (at t = 0) */
double b; /* N.s/m damping constant */
double k; /* N/m spring constant */
double F; /* N force constant */
MSD(void); /* not used */
MSD(double _m, double _k, double _b, double _F,
double _v_0, double _x_0);
int state_deriv(void);
int state_integ(void);
};
#ifdef __cplusplus
extern "C" {
#endif
int msd_default_data(MSD &);
int msd_init(MSD &);
int msd_shutdown(const MSD &);
#ifdef __cplusplus
}
#endif
#endif /* MSD_H */

View File

@ -0,0 +1,17 @@
/************************TRICK HEADER*************************
PURPOSE: Msd Numeric model
PROGRAMMERS:
(((Scott P. Fennell) (CACI International Inc.) (January 2018) (Trick Learning Project)))
*************************************************************/
#ifndef MSD_NUMERIC_H
#define MSD_NUMERIC_H
#include "msd.hh"
#ifdef __cplusplus
extern "C" {
#endif
#ifdef __cplusplus
}
#endif
#endif

View File

@ -0,0 +1,19 @@
/************************TRICK HEADER*************************
PURPOSE: MSD is a mass-spring-damper class for a trick sim.
PROGRAMMERS:
(((Scott P. Fennell) (CACI International Inc.) (January 2018) (Trick Learning Project)))
*************************************************************/
#include <math.h>
#include "../include/msd_numeric.hh"
#include <float.h>
MSD::MSD(double _m, double _k, double _b, double _F, double _v_0, double _x_0) {
m = _m;
k = _k;
b = _b;
F = _F;
v_0 = _v_0;
x_0 = _x_0;
}
MSD::MSD(void) {/* not used */}

View File

@ -0,0 +1,15 @@
/************************TRICK HEADER*************************
PURPOSE: ( Trick integration )
PROGRAMMERS:
(((Scott P. Fennell) (CACI International Inc.) (January 2018) (Trick Learning Project)))
*************************************************************/
#include "../include/msd_numeric.hh"
#include "../include/msd.hh"
#include <math.h>
int MSD::state_deriv(void) {
/* system equation for second derivitive */
a = (F - b*v - k*x)/m;
return(0);
}

View File

@ -0,0 +1,28 @@
/************************TRICK HEADER*************************
PURPOSE: Set the initial data values of the MSD
PROGRAMMERS:
(((Scott P. Fennell) (CACI International Inc.) (January 2018) (Trick Learning Project)))
*************************************************************/
/* Model Include files */
#include "../include/msd.hh"
/* default data job */
int msd_default_data(MSD &M) {
M = MSD(
1.0, /* m */
2.0, /* k */
0.5, /* b */
5.0, /* F */
0.0, /* v_0 */
5.0 /* x_0 */
);
return 0;
}
/* initialization job */
int msd_init(MSD &M) {
return 0;
}

View File

@ -0,0 +1,31 @@
/************************TRICK HEADER*************************
PURPOSE: ( Trick integration )
PROGRAMMERS:
(((Scott P. Fennell) (CACI International Inc.) (January 2018) (Trick Learning Project)))
*************************************************************/
#include "../include/msd_numeric.hh"
#include "msd.hh"
#include "trick/Integrator.hh"
#include "trick/integrator_c_intf.h"
#include <math.h>
int MSD::state_integ(void) {
int ipass;
load_state(
&x,
&v,
NULL);
load_deriv(
&v,
&a,
NULL);
ipass = integrate();
unload_state(
&x,
&v,
NULL);
return(ipass);
}

View File

@ -0,0 +1,17 @@
/************************TRICK HEADER*************************
PURPOSE: (Print the final msd state.)
PROGRAMMERS:
(((Scott P. Fennell) (CACI International Inc.) (January 2018) (Trick Learning Project)))
*************************************************************/
#include "../include/msd.hh"
#include "trick/exec_proto.h"
#include <stdio.h>
int msd_shutdown(const MSD &M) {
printf("========================================\n");
printf(" MSD State at Shutdown \n");
printf("pos = [%g], vel = [%g], acc = [%g]\n",
M.x, M.v, M.a);
printf("========================================\n");
return 0;
}