diff --git a/.gitignore b/.gitignore
index cdba2306fb0549315f9cb347d37452f713a71b85..70a9e9cc919d84f9422b05b697a507627e6ee260 100644
--- a/.gitignore
+++ b/.gitignore
@@ -25,3 +25,5 @@
 /specifications/RPR*.xml
 /specifications/SISO*.xml
 /specifications/archive
+!/specifications/2019-SIW-Presentation-039_CompressedDis.pdf
+!/specifications/IeeeDisPduColorFigures.pdf
\ No newline at end of file
diff --git a/assignments/src/MV3500Cohort2019JulySeptember/homework4/Schutt/Pdusave2.dislog b/assignments/src/MV3500Cohort2019JulySeptember/homework4/Schutt/Pdusave2.dislog
new file mode 100644
index 0000000000000000000000000000000000000000..85e8b4b40c1717e16cab6e58d52587a007bfc8f1
--- /dev/null
+++ b/assignments/src/MV3500Cohort2019JulySeptember/homework4/Schutt/Pdusave2.dislog
@@ -0,0 +1,7 @@
+!Begin!Beginning of DIS capture file, Pdusave2.dislog.
+AAAAAAAAAAA=,BwEBAewOIGsAAAAAAAEAAgADAAABAQDhAQEDAAAAAAAAAAAAQ0gAAD+peNXAFcKPwUSoEC8TT/vBUJugrw4+cEFM2aUo/skhAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAADvaJpU=,BwEBAewglf8AAAAAAAEAAgADAAABAQDhAQEDAAAAAAAAAAAAQ0gAAD+peNXAFcKPwUSoEnaldoTBUJuf+ZkOXEFM2aUo/skhAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAHemvso=,BwEBAewy2FEAAAAAAAEAAgADAAABAQDhAQEDAAAAAAAAAAAAQ0gAAD+peNXAFcKPwUSoEC8TT/vBUJugrw4+cEFM2aUo/skhAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAALNyst8=,BwEBAexFH0sAAAAAAAEAAgADAAABAQDhAQEDAAAAAAAAAAAAQ0gAAD+peNXAFcKPwUSoEnaldoTBUJuf+ZkOXEFM2aUo/skhAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAO9xjwc=,BwEBAexXb5kAAAAAAAEAAgADAAABAQDhAQEDAAAAAAAAAAAAQ0gAAD+peNXAFcKPwUSoEC8TT/vBUJugrw4+cEFM2aUo/skhAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+!End!End of DIS capture file, Pdusave2.dislog.
diff --git a/assignments/src/MV3500Cohort2019JulySeptember/homework4/Schutt/SchuttESPDUSender.java b/assignments/src/MV3500Cohort2019JulySeptember/homework4/Schutt/SchuttESPDUSender.java
new file mode 100644
index 0000000000000000000000000000000000000000..7654f887144617eadde7f432252c00702cd9414f
--- /dev/null
+++ b/assignments/src/MV3500Cohort2019JulySeptember/homework4/Schutt/SchuttESPDUSender.java
@@ -0,0 +1,347 @@
+/*
+ * To change this license header, choose License Headers in Project Properties.
+ * To change this template file, choose Tools | Templates
+ * and open the template in the editor.
+ */
+package MV3500Cohort2019JulySeptember.homework4.Schutt;
+
+import java.io.*;
+import java.net.*;
+import java.util.*;
+
+import edu.nps.moves.dis7.*;
+import edu.nps.moves.dis7.util.*;
+import edu.nps.moves.dis7.enumerations.Country;
+import edu.nps.moves.dis7.enumerations.EntityKind;
+import edu.nps.moves.dis7.enumerations.PlatformDomain;
+import java.nio.ByteBuffer;
+
+/**
+ * Creates and sends ESPDUs in IEEE binary format. Adapted from OpenDIS library
+ * example package edu.nps.moves.examples
+ *
+ * @author Don Brutzman
+ * @author Don Mcgregor
+ * Edited By Thomas Schutt
+ */
+public class SchuttESPDUSender {
+
+    public static final int NUMBER_TO_SEND = 5; // 5000
+
+    /**
+     * Default multicast group address we send on.
+     */
+    public static final String DEFAULT_MULTICAST_ADDRESS = "239.1.2.3";
+
+    /**
+     * Default multicast port used, matches Wireshark DIS capture default
+     */
+    public static final int DEFAULT_MULTICAST_PORT = 3002;
+
+    public enum NetworkMode {
+        UNICAST, MULTICAST, BROADCAST
+    };
+
+    /**
+     * Possible system properties, passed in via -Dattr=val networkMode:
+     * unicast, broadcast, multicast destinationIp: where to send the packet. If
+     * in multicast mode, this can be multicast. To determine broadcast
+     * destination IP, use an online broadcast address calculator, for example
+     * http://www.remotemonitoringsystems.ca/broadcast.php If in multicast mode,
+     * a join() will be done on the multicast address. port: port used for both
+     * source and destination.
+     *
+     * @param args
+     */
+    public static void main(String args[]) {
+        System.out.println("SchuttESPDUSender started...");
+
+        // Default settings. These are used if no system properties are set. 
+        // If system properties are passed in, these are overridden later.
+        NetworkMode networkMode = NetworkMode.BROADCAST;
+        InetAddress address = null; // must be initialized, even if null
+        int port = DEFAULT_MULTICAST_PORT;
+        MulticastSocket socket = null; // must be initialized to avoid later error, even if null;
+        EntityStatePdu espdu = new EntityStatePdu();
+        DisTime disTime = new DisTime();
+
+        // Entity Location
+        double latitude = 36.595517;
+        double longitude = -121.877000;
+        
+        //Target Location
+        double tarLat = 36.435345;
+        double tarLong = -121.54325;
+        
+        try {
+            address = InetAddress.getByName(DEFAULT_MULTICAST_ADDRESS);
+        } catch (UnknownHostException e) {
+            System.out.println(e + " Cannot create multicast address");
+            System.exit(0);
+        }
+
+        // All system properties, passed in on the command line via -Dattribute=value
+        Properties systemProperties = System.getProperties();
+
+        // IP address we send to
+        String destinationIpString = systemProperties.getProperty("destinationIp");
+
+        // Port we send to, and local port we open the socket on
+        String portString = systemProperties.getProperty("port");
+
+        // Network mode: unicast, multicast, broadcast
+        String networkModeString = systemProperties.getProperty("networkMode"); // unicast or multicast or broadcast
+
+        // Set up socket to send information
+        try {
+            if (portString != null) // Update port we send to, if provided
+            {
+                port = Integer.parseInt(portString);
+            }
+            socket = new MulticastSocket(port);
+
+            // Where we send packets to, the destination IP address
+            if (destinationIpString != null) {
+                address = InetAddress.getByName(destinationIpString);
+            }
+
+            // Type of transport: unicast, broadcast, or multicast
+            if (networkModeString != null) {
+                if (networkModeString.equalsIgnoreCase("unicast")) {
+                    networkMode = NetworkMode.UNICAST;
+                } else if (networkModeString.equalsIgnoreCase("broadcast")) {
+                    networkMode = NetworkMode.BROADCAST;
+                } else if (networkModeString.equalsIgnoreCase("multicast")) {
+                    networkMode = NetworkMode.MULTICAST;
+                    if (!address.isMulticastAddress()) {
+                        throw new RuntimeException("*** Error: sending to multicast address, but destination address " + address.toString() + "is not multicast");
+                    }
+                    socket.joinGroup(address);
+                }
+            } // end networkModeString
+        } catch (IOException | RuntimeException e) {
+            System.out.println("Unable to initialize network correctly, exiting.");
+            System.out.println(e);
+            System.exit(-1); // outta here
+        }
+
+        // Initialize values in the Entity State PDU object. The exercise ID is 
+        // a way to differentiate between different virtual worlds on one network.
+        // Note that some values (such as the PDU type and PDU family) are set
+        // automatically when you create the ESPDU.
+        espdu.setExerciseID((byte) 1); //(short) 1);
+
+        // The EID is the unique identifier for objects in the world. This 
+        // EID should match up with the ID for the object specified in the 
+        // VMRL/x3d/virtual world.
+        EntityID entityID = espdu.getEntityID(); // initialize, reset, override
+        // TODO check: 0 is apparently not a valid site number, per DIS specification
+        entityID.setSiteID((short) 1); // TODO utility method to allow int values
+        entityID.setApplicationID((short) 2);
+        entityID.setEntityID((short) 3);
+        espdu.setEntityID(entityID);
+
+        // Set the entity type. SISO has a big list of enumerations, so that by
+        // specifying various numbers we can say this is an M1A2 American tank,
+        // the USS Enterprise, and so on. We'll make this a tank. There is a 
+        // separate project elsehwhere in this project that implements DIS 
+        // enumerations in C++ and Java, but to keep things simple we just use
+        // numbers here.
+        // New way using entity jar(s)
+        espdu.setEntityType(new edu.nps.moves.dis7.entities.usa.platform.land.M1A2());
+
+        // Manual way:
+        /*
+		EntityType entityType = espdu.getEntityType();
+		entityType.setEntityKind(EntityKind.PLATFORM); //(short) 1);      // Platform (vs lifeform, munition, sensor, etc.)
+		entityType.setCountry(Country.UNITED_STATES_OF_AMERICA_USA); //225);               // USA
+		entityType.setDomain(Domain.inst(PlatformDomain.LAND));          // Land (vs air, surface, subsurface, space)
+		entityType.setCategory((byte) 1);        // Tank
+		entityType.setSubCategory((byte) 1);     // M1 Abrams
+		entityType.setSpecific((byte) 3);        // M1A2 Abrams
+         */
+        Set<InetAddress> broadcastAddresses;
+
+        try // Loop through sending N ESPDUs
+        {
+            System.out.println("Sending " + NUMBER_TO_SEND + " sets of packets:"); // + address.toString()
+
+            for (int index = 0; index < NUMBER_TO_SEND; index++) {
+                // DIS time is a pain in the uh, neck. DIS time units are 2^31-1 units per
+                // hour, and time is set to DIS time units from the top of the hour. 
+                // This means that if you start sending just before the top of the hour
+                // the time units can roll over to zero as you are sending. The receivers
+                // (escpecially homegrown ones) are often not able to detect rollover
+                // and may start discarding packets as dupes or out of order. We use
+                // an NPS timestamp here, hundredths of a second since the start of the
+                // year. The DIS standard for time is often ignored in the wild; I've seen
+                // people use Unix time (seconds since 1970) and more. Or you can
+                // just stuff idx into the timestamp field to get something that is monotonically
+                // increasing.
+
+                // Note that timestamp is used to detect duplicate and out of order packets. 
+                // That means if you DON'T change the timestamp, many implementations will simply
+                // discard subsequent packets that have an identical timestamp. Also, if they
+                // receive a PDU with an timestamp lower than the last one they received, they
+                // may discard it as an earlier, out-of-order PDU. So it is a good idea to
+                // update the timestamp on ALL packets sent.
+                // An alterative approach: actually follow the standard. It's a crazy concept,
+                // but it might just work.
+                int timestamp = disTime.getDisAbsoluteTimestamp();
+                espdu.setTimestamp(timestamp);
+
+                // Set the position of the entity in the world. DIS uses a cartesian 
+                // coordinate system with the origin at the center of the earth, the x
+                // axis out at the equator and prime meridian, y out at the equator and
+                // 90 deg east, and z up and out the north pole. To place an object on
+                // the earth's surface you also need a model for the shape of the earth
+                // (it's not a sphere.) All the fancy math necessary to do this is in
+                // the SEDRIS SRM package. There are also some one-off formulas for 
+                // doing conversions from, for example, lat/lon/altitude to DIS coordinates.
+                // Here we use those one-off formulas.
+                // Modify the position of the object. This will send the object a little
+                // due east by adding some to the longitude every iteration. Since we
+                // are on the Pacific coast, this sends the object east. Assume we are
+                // at zero altitude. In other worlds you'd use DTED to determine the
+                // local ground altitude at that lat/lon, or you'd just use ground clamping.
+                // The x and y values will change, but the z value should not.
+                //lon = lon + (double)((double)idx / 100000.0);
+                //System.out.println("lla=" + lat + "," + lon + ", 0.0");
+                double direction = Math.pow((double) (-1.0), (double) (index));
+                longitude = longitude + (direction * 0.00006);
+
+                double disCoordinates[] = CoordinateConversions.getXYZfromLatLonDegrees(latitude, longitude, 1.0);
+                Vector3Double location = espdu.getEntityLocation();
+                location.setX(disCoordinates[0]);
+                location.setY(disCoordinates[1]);
+                location.setZ(disCoordinates[2]);
+                System.out.println("===============");
+                System.out.println("Create new PDUs");
+                System.out.println("  latitude, longitude:   [" + latitude + ", " + longitude + "]");
+                System.out.println("  coordinate conversion: [" + disCoordinates[0] + ", " + disCoordinates[1] + ", " + disCoordinates[2] + "]");
+                //Schutt Edit
+                Vector3Float espduVelocity = new Vector3Float();
+                espduVelocity.setX((float) 200.0);
+                espduVelocity.setY((float) 1.324);
+                espduVelocity.setZ((float) -2.34);
+                espdu.setEntityLinearVelocity(espduVelocity);
+                // End Schutt Edit
+                
+
+                location = espdu.getEntityLocation();
+
+                System.out.println("Espdu #" + index + " entityID=[" + entityID.getSiteID() + "," + entityID.getApplicationID() + "," + entityID.getEntityID() + "]");
+                double c[] = {location.getX(), location.getY(), location.getZ()};
+                double lla[] = CoordinateConversions.xyzToLatLonDegrees(c);
+//              System.out.println("  DIS entityLocation: [" + location.getX() + "," + location.getY() + "," + location.getZ() + "]");
+                String debugString = " Location (latitude/longitude/altitude): [" + lla[0] + ", " + lla[1] + ", " + lla[2] + "]";
+//      		System.out.println(debugString);
+
+                // Optionally, we can do some rotation of the entity
+                /*
+            Orientation orientation = espdu.getEntityOrientation();
+            float psi = orientation.getPsi();
+            psi = psi + idx;
+            orientation.setPsi(psi);
+            orientation.setTheta((float)(orientation.getTheta() + idx /2.0));
+                 */
+                // You can set other ESPDU values here, such as the velocity, acceleration,
+                // and so on.
+                // Marshal out the espdu object to a byte array, then send a datagram
+                // packet with that data in it.
+                ByteArrayOutputStream baos = new ByteArrayOutputStream();
+                DataOutputStream dos = new DataOutputStream(baos);
+                DatagramPacket packet;
+
+                // The byte array here is the packet in DIS format. We put that into a 
+                // datagram and send it.
+                espdu.marshal(dos);
+                byte[] espduArray = baos.toByteArray();
+                //Schutt Edit
+                tarLong = tarLong + (direction * 0.00006);
+                double tarDisCoordinates[] = CoordinateConversions.getXYZfromLatLonDegrees(tarLat, tarLong, 1.0);
+                Vector3Double tarLocation = new Vector3Double();
+                tarLocation.setX(tarDisCoordinates[0]);
+                tarLocation.setY(tarDisCoordinates[1]);
+                tarLocation.setZ(tarDisCoordinates[2]);
+
+                FirePdu firePdu = new FirePdu();
+                
+                firePdu.setLocationInWorldCoordinates(tarLocation);
+                firePdu.setRange((float) 4000.0);
+                byte[] fireArray = firePdu.marshal();
+
+                broadcastAddresses = getBroadcastAddresses();
+                Iterator iterator = broadcastAddresses.iterator();
+                while (iterator.hasNext()) {
+                    InetAddress broadcast = (InetAddress) iterator.next();
+                    if (espduArray.length > 0) {
+                        System.out.println("Sending espdu datagram packet to " + broadcast);
+                        packet = new DatagramPacket(espduArray, espduArray.length, broadcast, port);
+                        socket.send(packet);
+                    }
+                    // TODO experiment with these!  8)
+                    if (fireArray.length > 0) {
+                        System.out.println("Sending  fire datagram packet to " + broadcast);
+                        packet = new DatagramPacket(fireArray, fireArray.length, broadcast, port); // alternate
+                        socket.send(packet);
+                    }
+                }
+
+                // Send every 1 second within loop. Otherwise all this will be all over in a fraction of a second.
+                Thread.sleep(1000); // msec
+            }
+        } catch (Exception e) {
+            System.out.println("Problem with DisExamplesOpenDis7.EspduSender, see exception trace:");
+            System.out.println(e);
+        }
+        System.out.println("===============");
+        System.out.println("DisExamplesOpenDis7.EspduSender complete.");
+    }
+
+    /**
+     * A number of sites get all snippy about using 255.255.255.255 for a
+     * broadcast address; it trips their security software and they kick you off
+     * their network. (Comcast, NPS, etc.) This determines the broadcast address
+     * for all connected interfaces, based on the IP and subnet mask. If you
+     * have a dual-homed host it will return a broadcast address for both. If
+     * you have some VMs running on your host this will pick up the addresses
+     * for those as well--e.g. running VMWare on your laptop with a local IP
+     * this will also pick up a 192.168 address assigned to the VM by the host
+     * OS.
+     *
+     * @return set of all broadcast addresses
+     */
+    public static Set<InetAddress> getBroadcastAddresses() {
+        Set<InetAddress> broadcastAddresses = new HashSet<>();
+        Enumeration interfaces;
+
+        try {
+            interfaces = NetworkInterface.getNetworkInterfaces();
+
+            while (interfaces.hasMoreElements()) {
+                NetworkInterface anInterface = (NetworkInterface) interfaces.nextElement();
+
+                if (anInterface.isUp()) {
+                    Iterator iterator = anInterface.getInterfaceAddresses().iterator();
+                    while (iterator.hasNext()) {
+                        InterfaceAddress anAddress = (InterfaceAddress) iterator.next();
+                        if ((anAddress == null || anAddress.getAddress().isLinkLocalAddress())) {
+                            continue;
+                        }
+
+                        //System.out.println("Getting broadcast address for " + anAddress);
+                        InetAddress broadcastAddress = anAddress.getBroadcast();
+                        if (broadcastAddress != null) {
+                            broadcastAddresses.add(broadcastAddress);
+                        }
+                    }
+                }
+            }
+        } catch (SocketException e) {
+            System.out.println("Problem with DisExamplesOpenDis7.EspduSender.getBroadcastAddresses(), see exception trace:");
+            System.out.println(e);
+        }
+        return broadcastAddresses;
+    }
+}
diff --git a/assignments/src/MV3500Cohort2019JulySeptember/homework4/Schutt/SchuttEspduReceiver.java b/assignments/src/MV3500Cohort2019JulySeptember/homework4/Schutt/SchuttEspduReceiver.java
new file mode 100644
index 0000000000000000000000000000000000000000..5beafd0129efaadf905f12d8a7bbab61fa304c7e
--- /dev/null
+++ b/assignments/src/MV3500Cohort2019JulySeptember/homework4/Schutt/SchuttEspduReceiver.java
@@ -0,0 +1,99 @@
+package MV3500Cohort2019JulySeptember.homework4.Schutt; 
+
+import java.io.*;
+import java.net.*;
+import java.util.*;
+
+import edu.nps.moves.dis7.*;
+import edu.nps.moves.dis7.util.*;
+
+/**
+ * Receives PDUs from the network in IEEE DIS format.
+ * Adapted from OpenDIS library example package edu.nps.moves.examples
+ *
+ * @author DMcG
+ * @version $Id:$
+ * Edited by Thomas Schutt
+ */
+public class SchuttEspduReceiver
+{
+    /** Max size of a PDU in binary format that we can receive. This is actually
+     * somewhat outdated--PDUs can be larger--but this is a reasonable starting point.
+     */
+    public static final int MAX_PDU_SIZE = 8192;
+
+    /** Default multicast group address we send on. */
+    public static final String DEFAULT_MULTICAST_ADDRESS = SchuttESPDUSender.DEFAULT_MULTICAST_ADDRESS;
+	
+    /** Default multicast port used, matches Wireshark DIS capture default */
+    public static final int    DEFAULT_MULTICAST_PORT    = SchuttESPDUSender.DEFAULT_MULTICAST_PORT;
+
+    public static void main(String args[])
+	{
+		System.out.println("DisExamplesOpenDis7.EspduReceiver started...");
+		
+        MulticastSocket socket;
+        DatagramPacket packet;
+        InetAddress address;
+        PduFactory pduFactory = new PduFactory();
+
+        try {
+            // Specify the socket to receive data
+            socket = new MulticastSocket(DEFAULT_MULTICAST_PORT);
+//          socket.setBroadcast(true);
+       
+            address = InetAddress.getByName(DEFAULT_MULTICAST_ADDRESS);
+            socket.joinGroup(address);
+
+            while (true) // Loop infinitely, receiving datagrams
+			{
+                byte buffer[] = new byte[MAX_PDU_SIZE];
+                packet = new DatagramPacket(buffer, buffer.length);
+
+                socket.receive(packet);
+
+                List<Pdu> pduBundle = pduFactory.getPdusFromBundle(packet.getData(),packet.getLength());
+                if (pduBundle.size() > 1)
+                    System.out.println("Bundle size is " + pduBundle.size());
+                Iterator iterator = pduBundle.iterator();
+
+                while (iterator.hasNext())
+                {
+                    Pdu aPdu = (Pdu)iterator.next();
+                    String receiptMessage = "received PDU type " + aPdu.getPduType().getValue() + "=" + aPdu.getPduType().name() + " " + aPdu.getClass().getName();
+                    if (aPdu instanceof EntityStatePdu)
+                    {
+                        System.out.println("===============");
+                        System.out.println(receiptMessage);
+                        EntityID      entityID = ((EntityStatePdu)aPdu).getEntityID();
+                        Vector3Double position = ((EntityStatePdu)aPdu).getEntityLocation();
+                        Vector3Float velocity = ((EntityStatePdu)aPdu).getEntityLinearVelocity();
+                        System.out.println("  entityID triplet: [" + entityID.getSiteID()+ ", " + entityID.getApplicationID()+ ", " + entityID.getEntityID()+ "] ");
+                        System.out.println("  Location in DIS coordinates: [" + position.getX() + ", " + position.getY() + ", " + position.getZ() + "]");
+                        //Schutt Edit
+                        System.out.println("  Velocity: [" + velocity.getX() + ", " + velocity.getY() + ", " + velocity.getZ() + "]");
+                    }
+                    else if (aPdu instanceof FirePdu)
+                    {    
+                        System.out.println(receiptMessage);
+                        Vector3Double position = ((FirePdu)aPdu).getLocationInWorldCoordinates();
+                        float range = ((FirePdu)aPdu).getRange();
+                        System.out.println("  FirePdu locationInWorldCoordinates: [" + position.getX() + ", " + position.getY() + ", " + position.getZ() + "]");
+                        //Schutt Edit
+                        System.out.println("  FirePdu Range: [" + range + "]");
+                    }
+                    else
+                    {    
+                        System.out.println(receiptMessage);
+                    }
+                } // end iterator loop through PDU bundle
+            } // end while
+        } // end try block
+        catch (IOException e)
+		{
+            System.out.println("Problem with DisExamplesOpenDis7.EspduReceiver, see exception trace:");
+            System.out.println(e);
+        }
+		System.out.println("DisExamplesOpenDis7.EspduReceiver complete.");
+    } // end main
+} // end class
diff --git a/assignments/src/MV3500Cohort2019JulySeptember/homework4/Schutt/SchuttPDUSaverListener.java b/assignments/src/MV3500Cohort2019JulySeptember/homework4/Schutt/SchuttPDUSaverListener.java
new file mode 100644
index 0000000000000000000000000000000000000000..8b1471b700f24988f3f36168147d97001f34f53f
--- /dev/null
+++ b/assignments/src/MV3500Cohort2019JulySeptember/homework4/Schutt/SchuttPDUSaverListener.java
@@ -0,0 +1,89 @@
+/*
+ * To change this license header, choose License Headers in Project Properties.
+ * To change this template file, choose Tools | Templates
+ * and open the template in the editor.
+ */
+package MV3500Cohort2019JulySeptember.homework4.Schutt;
+import edu.nps.moves.dis7.util.playerrecorder.Recorder;
+import java.io.IOException;
+import java.util.Scanner;
+
+/**
+ * PduSaver.java created on Aug 21, 2019
+ * MOVES Institute Naval Postgraduate School, Monterey, CA, USA www.nps.edu
+ *
+ * @author Mike Bailey, jmbailey@nps.edu
+ * @version $Id$
+ */
+public class SchuttPDUSaverListener
+{
+  private final static String DEFAULT_OUTPUTDIR = "pduLog";
+  private final static String MCAST_ADDR = "239.1.2.3";
+  private final static int DIS_PORT = 3002;
+
+  private enum mystate
+  {
+    RUNNING,
+    PAUSED;
+  }
+
+  public static void main(String[] args)
+  {
+    String outDir = DEFAULT_OUTPUTDIR;
+    String mcast = MCAST_ADDR;
+    int port = DIS_PORT;
+    
+    System.out.println("DisExamplesOpenDis7.PduListenerSaver started...");
+
+    switch (args.length) {
+      case 0:
+        break;
+      case 1:
+        outDir = args[0];
+        break;
+      case 3:
+        outDir = args[0];
+        mcast = args[1];
+        port = Integer.parseInt(args[2]);
+        break;
+      default:
+        System.err.println("Usage: PduListener() or PduListener(\"outputdir\") or PduListener(\"outputdir\",\"multicast address\", ipPort");
+        System.exit(1);
+    }
+
+    System.out.println("Beginning pdu save to directory " + outDir);
+    try {
+      Recorder recorder = new Recorder(outDir, mcast, port);
+
+      recorder.startResume();
+      mystate state = mystate.RUNNING;
+      System.out.println("* recorder.startResume(), state=RUNNING, recording in progress...");
+      Scanner scan = new Scanner(System.in);
+
+      while (true) {
+        System.out.println("Warning: you must quit when complete, otherwise recorded PDUs are lost!");
+        System.out.println("Type p/enter to pause, r/enter to resume, q/enter to stop recording, save and quit");
+        String line = scan.nextLine();
+        if (line.equalsIgnoreCase("p") && state == mystate.RUNNING) {
+          recorder.stopPause();
+          state = mystate.PAUSED;
+          System.out.println("* recorder.stopPause(), state=PAUSED, recording paused...");
+        }
+        else if (line.equalsIgnoreCase("r") && state == mystate.PAUSED) {
+          recorder.startResume();
+          state = mystate.RUNNING;
+          System.out.println("* recorder.startResume(), state=RUNNING, recording in progress...");
+        }
+        else if (line.equalsIgnoreCase("q")) {
+          recorder.end();
+          System.out.println("* recorder.end(), recording complete.");
+          break;
+        }
+      }
+      System.out.println("Ending pdu save to "+recorder.getLogFile());
+    }
+    catch (IOException ex) {
+      System.err.println("*** Exception: " + ex.getClass().getSimpleName() + ": " + ex.getLocalizedMessage());
+    }
+  }
+}
diff --git a/assignments/src/MV3500Cohort2019JulySeptember/projects/Boron_Yurkovich - UDPs for MAJ Furr/FinalProject_YurkBoron.pptx b/assignments/src/MV3500Cohort2019JulySeptember/projects/Boron_Yurkovich - UDPs for MAJ Furr/FinalProject_YurkBoron__002_.pptx
similarity index 93%
rename from assignments/src/MV3500Cohort2019JulySeptember/projects/Boron_Yurkovich - UDPs for MAJ Furr/FinalProject_YurkBoron.pptx
rename to assignments/src/MV3500Cohort2019JulySeptember/projects/Boron_Yurkovich - UDPs for MAJ Furr/FinalProject_YurkBoron__002_.pptx
index cbc403ab45723a60bf2dfb63fe8d61805eeba02a..dfaed57e27d49473b5b3ed1a86ef6c36483a66a6 100644
Binary files a/assignments/src/MV3500Cohort2019JulySeptember/projects/Boron_Yurkovich - UDPs for MAJ Furr/FinalProject_YurkBoron.pptx and b/assignments/src/MV3500Cohort2019JulySeptember/projects/Boron_Yurkovich - UDPs for MAJ Furr/FinalProject_YurkBoron__002_.pptx differ
diff --git a/assignments/src/MV3500Cohort2019JulySeptember/projects/SchuttFetterolf/PduListenerSaver.java b/assignments/src/MV3500Cohort2019JulySeptember/projects/SchuttFetterolf/PduListenerSaver.java
new file mode 100644
index 0000000000000000000000000000000000000000..63ff97e155704d705c99f4c52efb819bcb36e183
--- /dev/null
+++ b/assignments/src/MV3500Cohort2019JulySeptember/projects/SchuttFetterolf/PduListenerSaver.java
@@ -0,0 +1,89 @@
+/**
+ * Copyright (c) 2008-2019, MOVES Institute, Naval Postgraduate School. All rights reserved.
+ * This work is licensed under the BSD open source license, available at https://www.movesinstitute.org/licenses/bsd.html
+ */
+package MV3500Cohort2019JulySeptember.projects.SchuttFetterolf;
+
+import edu.nps.moves.dis7.util.playerrecorder.Recorder;
+import java.io.IOException;
+import java.util.Scanner;
+
+/**
+ * PduSaver.java created on Aug 21, 2019
+ * MOVES Institute Naval Postgraduate School, Monterey, CA, USA www.nps.edu
+ *
+ * @author Mike Bailey, jmbailey@nps.edu
+ * @version $Id$
+ */
+public class PduListenerSaver
+{
+  private final static String DEFAULT_OUTPUTDIR = "pduLog";
+  private final static String MCAST_ADDR = "239.1.2.3";
+  private final static int DIS_PORT = 3000;
+
+  private enum mystate
+  {
+    RUNNING,
+    PAUSED;
+  }
+
+  public static void main(String[] args)
+  {
+    String outDir = DEFAULT_OUTPUTDIR;
+    String mcast = MCAST_ADDR;
+    int port = DIS_PORT;
+    
+    System.out.println("DisExamplesOpenDis7.PduListenerSaver started...");
+
+    switch (args.length) {
+      case 0:
+        break;
+      case 1:
+        outDir = args[0];
+        break;
+      case 3:
+        outDir = args[0];
+        mcast = args[1];
+        port = Integer.parseInt(args[2]);
+        break;
+      default:
+        System.err.println("Usage: PduListener() or PduListener(\"outputdir\") or PduListener(\"outputdir\",\"multicast address\", ipPort");
+        System.exit(1);
+    }
+
+    System.out.println("Beginning pdu save to directory " + outDir);
+    try {
+      Recorder recorder = new Recorder(outDir, mcast, port);
+
+      recorder.startResume();
+      mystate state = mystate.RUNNING;
+      System.out.println("* recorder.startResume(), state=RUNNING, recording in progress...");
+      Scanner scan = new Scanner(System.in);
+
+      while (true) {
+        System.out.println("Warning: you must quit when complete, otherwise recorded PDUs are lost!");
+        System.out.println("Type p/enter to pause, r/enter to resume, q/enter to stop recording, save and quit");
+        String line = scan.nextLine();
+        if (line.equalsIgnoreCase("p") && state == mystate.RUNNING) {
+          recorder.stopPause();
+          state = mystate.PAUSED;
+          System.out.println("* recorder.stopPause(), state=PAUSED, recording paused...");
+        }
+        else if (line.equalsIgnoreCase("r") && state == mystate.PAUSED) {
+          recorder.startResume();
+          state = mystate.RUNNING;
+          System.out.println("* recorder.startResume(), state=RUNNING, recording in progress...");
+        }
+        else if (line.equalsIgnoreCase("q")) {
+          recorder.end();
+          System.out.println("* recorder.end(), recording complete.");
+          break;
+        }
+      }
+      System.out.println("Ending pdu save to "+recorder.getLogFile());
+    }
+    catch (IOException ex) {
+      System.err.println("*** Exception: " + ex.getClass().getSimpleName() + ": " + ex.getLocalizedMessage());
+    }
+  }
+}
diff --git a/assignments/src/MV3500Cohort2019JulySeptember/projects/SchuttFetterolf/S_F_FiringUnit.java b/assignments/src/MV3500Cohort2019JulySeptember/projects/SchuttFetterolf/S_F_FiringUnit.java
new file mode 100644
index 0000000000000000000000000000000000000000..95bb2b15ca7171d42362cd7d5a99a175c45da8a5
--- /dev/null
+++ b/assignments/src/MV3500Cohort2019JulySeptember/projects/SchuttFetterolf/S_F_FiringUnit.java
@@ -0,0 +1,317 @@
+package MV3500Cohort2019JulySeptember.projects.SchuttFetterolf;
+
+import java.io.*;
+import java.net.*;
+import java.util.*;
+
+import edu.nps.moves.dis7.*;
+import edu.nps.moves.dis7.enumerations.*;
+import edu.nps.moves.dis7.util.CoordinateConversions;
+
+/**
+ * This is an example that sends many/most types of PDUs. Useful for testing standards
+ * compliance or getting a full set of PDUs. It also writes the generated PDUs to an XML file.
+ * Adapted from OpenDIS library example package edu.nps.moves.examples
+ *
+ * @author DMcG
+ * @version $Id:$
+ */
+public class S_F_FiringUnit
+{
+   public static final int NUMBER_TO_SEND = 5; // 5000
+
+    /**
+     * Default multicast group address we send on.
+     */
+    public static final String DEFAULT_MULTICAST_ADDRESS = "239.1.2.3";
+
+    /**
+     * Default multicast port used, matches Wireshark DIS capture default
+     */
+    public static final int DEFAULT_MULTICAST_PORT = 3000;
+
+    public enum NetworkMode {
+        UNICAST, MULTICAST, BROADCAST
+    };
+
+    /**
+     * Possible system properties, passed in via -Dattr=val networkMode:
+     * unicast, broadcast, multicast destinationIp: where to send the packet. If
+     * in multicast mode, this can be multicast. To determine broadcast
+     * destination IP, use an online broadcast address calculator, for example
+     * http://www.remotemonitoringsystems.ca/broadcast.php If in multicast mode,
+     * a join() will be done on the multicast address. port: port used for both
+     * source and destination.
+     *
+     * @param args
+     */
+    public static void main(String args[]) {
+        System.out.println("Firing Unit started...");
+
+        // Default settings. These are used if no system properties are set. 
+        // If system properties are passed in, these are overridden later.
+        NetworkMode networkMode = NetworkMode.BROADCAST;
+        InetAddress address = null; // must be initialized, even if null
+        int port = DEFAULT_MULTICAST_PORT;
+        MulticastSocket socket = null; // must be initialized to avoid later error, even if null;
+        EntityStatePdu espdu = new EntityStatePdu();
+        DisTime disTime = new DisTime();
+
+        // Firing Unit Location
+        double latitude = 36.350100;
+        double longitude = -121.451000;
+        try {
+            address = InetAddress.getByName(DEFAULT_MULTICAST_ADDRESS);
+        } catch (UnknownHostException e) {
+            System.out.println(e + " Cannot create multicast address");
+            System.exit(0);
+        }
+
+        // All system properties, passed in on the command line via -Dattribute=value
+        Properties systemProperties = System.getProperties();
+
+        // IP address we send to
+        String destinationIpString = systemProperties.getProperty("destinationIp");
+
+        // Port we send to, and local port we open the socket on
+        String portString = systemProperties.getProperty("port");
+
+        // Network mode: unicast, multicast, broadcast
+        String networkModeString = systemProperties.getProperty("networkMode"); // unicast or multicast or broadcast
+
+        // Set up socket to send information
+        try {
+            if (portString != null) // Update port we send to, if provided
+            {
+                port = Integer.parseInt(portString);
+            }
+            socket = new MulticastSocket(port);
+
+            // Where we send packets to, the destination IP address
+            if (destinationIpString != null) {
+                address = InetAddress.getByName(destinationIpString);
+            }
+
+            // Type of transport: unicast, broadcast, or multicast
+            if (networkModeString != null) {
+                if (networkModeString.equalsIgnoreCase("unicast")) {
+                    networkMode = NetworkMode.UNICAST;
+                } else if (networkModeString.equalsIgnoreCase("broadcast")) {
+                    networkMode = NetworkMode.BROADCAST;
+                } else if (networkModeString.equalsIgnoreCase("multicast")) {
+                    networkMode = NetworkMode.MULTICAST;
+                    if (!address.isMulticastAddress()) {
+                        throw new RuntimeException("*** Error: sending to multicast address, but destination address " + address.toString() + "is not multicast");
+                    }
+                    socket.joinGroup(address);
+                }
+            } // end networkModeString
+        } catch (IOException | RuntimeException e) {
+            System.out.println("Unable to initialize network correctly, exiting.");
+            System.out.println(e);
+            System.exit(-1); // outta here
+        }
+
+        // Initialize values in the Entity State PDU object. The exercise ID is 
+        // a way to differentiate between different virtual worlds on one network.
+        // Note that some values (such as the PDU type and PDU family) are set
+        // automatically when you create the ESPDU.
+        espdu.setExerciseID((byte) 1); //(short) 1);
+
+        // The EID is the unique identifier for objects in the world. This 
+        // EID should match up with the ID for the object specified in the 
+        // VMRL/x3d/virtual world.
+        EntityID entityID = espdu.getEntityID(); // initialize, reset, override
+        // TODO check: 0 is apparently not a valid site number, per DIS specification
+        entityID.setSiteID((short) 1); // TODO utility method to allow int values
+        entityID.setApplicationID((short) 2);
+        entityID.setEntityID((short) 3);
+        espdu.setEntityID(entityID);
+
+        // Set the entity type. SISO has a big list of enumerations, so that by
+        // specifying various numbers we can say this is an M1A2 American tank,
+        // the USS Enterprise, and so on. We'll make this a tank. There is a 
+        // separate project elsehwhere in this project that implements DIS 
+        // enumerations in C++ and Java, but to keep things simple we just use
+        // numbers here.
+        // New way using entity jar(s)
+        espdu.setEntityType(new edu.nps.moves.dis7.entities.usa.platform.land.M119105_mmlightHowitzer());
+
+        // Manual way:
+        /*
+		EntityType entityType = espdu.getEntityType();
+		entityType.setEntityKind(EntityKind.PLATFORM); //(short) 1);      // Platform (vs lifeform, munition, sensor, etc.)
+		entityType.setCountry(Country.UNITED_STATES_OF_AMERICA_USA); //225);               // USA
+		entityType.setDomain(Domain.inst(PlatformDomain.LAND));          // Land (vs air, surface, subsurface, space)
+		entityType.setCategory((byte) 1);        // Tank
+		entityType.setSubCategory((byte) 1);     // M1 Abrams
+		entityType.setSpecific((byte) 3);        // M1A2 Abrams
+         */
+        Set<InetAddress> broadcastAddresses;
+
+        try // Loop through sending N ESPDUs
+        {
+            System.out.println("Sending " + NUMBER_TO_SEND + " sets of packets:"); // + address.toString()
+
+            for (int index = 0; index < NUMBER_TO_SEND; index++) {
+                // DIS time is a pain in the uh, neck. DIS time units are 2^31-1 units per
+                // hour, and time is set to DIS time units from the top of the hour. 
+                // This means that if you start sending just before the top of the hour
+                // the time units can roll over to zero as you are sending. The receivers
+                // (escpecially homegrown ones) are often not able to detect rollover
+                // and may start discarding packets as dupes or out of order. We use
+                // an NPS timestamp here, hundredths of a second since the start of the
+                // year. The DIS standard for time is often ignored in the wild; I've seen
+                // people use Unix time (seconds since 1970) and more. Or you can
+                // just stuff idx into the timestamp field to get something that is monotonically
+                // increasing.
+
+                // Note that timestamp is used to detect duplicate and out of order packets. 
+                // That means if you DON'T change the timestamp, many implementations will simply
+                // discard subsequent packets that have an identical timestamp. Also, if they
+                // receive a PDU with an timestamp lower than the last one they received, they
+                // may discard it as an earlier, out-of-order PDU. So it is a good idea to
+                // update the timestamp on ALL packets sent.
+                // An alterative approach: actually follow the standard. It's a crazy concept,
+                // but it might just work.
+                int timestamp = disTime.getDisAbsoluteTimestamp();
+                espdu.setTimestamp(timestamp);
+
+                // Set the position of the entity in the world. DIS uses a cartesian 
+                // coordinate system with the origin at the center of the earth, the x
+                // axis out at the equator and prime meridian, y out at the equator and
+                // 90 deg east, and z up and out the north pole. To place an object on
+                // the earth's surface you also need a model for the shape of the earth
+                // (it's not a sphere.) All the fancy math necessary to do this is in
+                // the SEDRIS SRM package. There are also some one-off formulas for 
+                // doing conversions from, for example, lat/lon/altitude to DIS coordinates.
+                // Here we use those one-off formulas.
+                // Modify the position of the object. This will send the object a little
+                // due east by adding some to the longitude every iteration. Since we
+                // are on the Pacific coast, this sends the object east. Assume we are
+                // at zero altitude. In other worlds you'd use DTED to determine the
+                // local ground altitude at that lat/lon, or you'd just use ground clamping.
+                // The x and y values will change, but the z value should not.
+                //lon = lon + (double)((double)idx / 100000.0);
+                //System.out.println("lla=" + lat + "," + lon + ", 0.0");
+                double direction = Math.pow((double) (-1.0), (double) (index));
+                longitude = longitude + (direction * 0.00006);
+
+                double disCoordinates[] = CoordinateConversions.getXYZfromLatLonDegrees(latitude, longitude, 1.0);
+                Vector3Double location = espdu.getEntityLocation();
+                location.setX(disCoordinates[0]);
+                location.setY(disCoordinates[1]);
+                location.setZ(disCoordinates[2]);
+                System.out.println("===============");
+                System.out.println("Create new PDUs");
+                System.out.println("  latitude, longitude:   [" + latitude + ", " + longitude + "]");
+                System.out.println("  coordinate conversion: [" + disCoordinates[0] + ", " + disCoordinates[1] + ", " + disCoordinates[2] + "]");
+
+                location = espdu.getEntityLocation();
+
+                System.out.println("Espdu #" + index + " entityID=[" + entityID.getSiteID() + "," + entityID.getApplicationID() + "," + entityID.getEntityID() + "]");
+                double c[] = {location.getX(), location.getY(), location.getZ()};
+                double lla[] = CoordinateConversions.xyzToLatLonDegrees(c);
+//              System.out.println("  DIS entityLocation: [" + location.getX() + "," + location.getY() + "," + location.getZ() + "]");
+                String debugString = " Location (latitude/longitude/altitude): [" + lla[0] + ", " + lla[1] + ", " + lla[2] + "]";
+//      		System.out.println(debugString);
+
+                // Optionally, we can do some rotation of the entity
+                /*
+            Orientation orientation = espdu.getEntityOrientation();
+            float psi = orientation.getPsi();
+            psi = psi + idx;
+            orientation.setPsi(psi);
+            orientation.setTheta((float)(orientation.getTheta() + idx /2.0));
+                 */
+                // You can set other ESPDU values here, such as the velocity, acceleration,
+                // and so on.
+                // Marshal out the espdu object to a byte array, then send a datagram
+                // packet with that data in it.
+                ByteArrayOutputStream baos = new ByteArrayOutputStream();
+                DataOutputStream dos = new DataOutputStream(baos);
+                DatagramPacket packet;
+
+                // The byte array here is the packet in DIS format. We put that into a 
+                // datagram and send it.
+                espdu.marshal(dos);
+                byte[] espduArray = baos.toByteArray();
+
+                FirePdu firePdu = new FirePdu();
+                firePdu.setLocationInWorldCoordinates(espdu.getEntityLocation());
+                byte[] fireArray = firePdu.marshal();
+
+                broadcastAddresses = getBroadcastAddresses();
+                Iterator iterator = broadcastAddresses.iterator();
+                while (iterator.hasNext()) {
+                    InetAddress broadcast = (InetAddress) iterator.next();
+                    if (espduArray.length > 0) {
+                        System.out.println("Sending espdu datagram packet to " + broadcast);
+                        packet = new DatagramPacket(espduArray, espduArray.length, broadcast, port);
+                        socket.send(packet);
+                    }
+                    // TODO experiment with these!  8)
+                    if (fireArray.length > 0) {
+                        System.out.println("Sending  fire datagram packet to " + broadcast);
+                        packet = new DatagramPacket(fireArray, fireArray.length, broadcast, port); // alternate
+                        socket.send(packet);
+                    }
+                }
+
+                // Send every 1 second within loop. Otherwise all this will be all over in a fraction of a second.
+                Thread.sleep(1000); // msec
+            }
+        } catch (Exception e) {
+            System.out.println("Problem with DisExamplesOpenDis7.EspduSender, see exception trace:");
+            System.out.println(e);
+        }
+        System.out.println("===============");
+        System.out.println("DisExamplesOpenDis7.EspduSender complete.");
+    }
+
+    /**
+     * A number of sites get all snippy about using 255.255.255.255 for a
+     * broadcast address; it trips their security software and they kick you off
+     * their network. (Comcast, NPS, etc.) This determines the broadcast address
+     * for all connected interfaces, based on the IP and subnet mask. If you
+     * have a dual-homed host it will return a broadcast address for both. If
+     * you have some VMs running on your host this will pick up the addresses
+     * for those as well--e.g. running VMWare on your laptop with a local IP
+     * this will also pick up a 192.168 address assigned to the VM by the host
+     * OS.
+     *
+     * @return set of all broadcast addresses
+     */
+    public static Set<InetAddress> getBroadcastAddresses() {
+        Set<InetAddress> broadcastAddresses = new HashSet<>();
+        Enumeration interfaces;
+
+        try {
+            interfaces = NetworkInterface.getNetworkInterfaces();
+
+            while (interfaces.hasMoreElements()) {
+                NetworkInterface anInterface = (NetworkInterface) interfaces.nextElement();
+
+                if (anInterface.isUp()) {
+                    Iterator iterator = anInterface.getInterfaceAddresses().iterator();
+                    while (iterator.hasNext()) {
+                        InterfaceAddress anAddress = (InterfaceAddress) iterator.next();
+                        if ((anAddress == null || anAddress.getAddress().isLinkLocalAddress())) {
+                            continue;
+                        }
+
+                        //System.out.println("Getting broadcast address for " + anAddress);
+                        InetAddress broadcastAddress = anAddress.getBroadcast();
+                        if (broadcastAddress != null) {
+                            broadcastAddresses.add(broadcastAddress);
+                        }
+                    }
+                }
+            }
+        } catch (SocketException e) {
+            System.out.println("Problem with DisExamplesOpenDis7.EspduSender.getBroadcastAddresses(), see exception trace:");
+            System.out.println(e);
+        }
+        return broadcastAddresses;
+    }
+}
diff --git a/assignments/src/MV3500Cohort2019JulySeptember/projects/SchuttFetterolf/S_F_RequestingUnit.java b/assignments/src/MV3500Cohort2019JulySeptember/projects/SchuttFetterolf/S_F_RequestingUnit.java
new file mode 100644
index 0000000000000000000000000000000000000000..2fd0ee8f748917773e58e77d6ae233e123b58342
--- /dev/null
+++ b/assignments/src/MV3500Cohort2019JulySeptember/projects/SchuttFetterolf/S_F_RequestingUnit.java
@@ -0,0 +1,325 @@
+package MV3500Cohort2019JulySeptember.projects.SchuttFetterolf;
+
+import java.io.*;
+import java.net.*;
+import java.util.*;
+
+import edu.nps.moves.dis7.*;
+import edu.nps.moves.dis7.enumerations.*;
+import edu.nps.moves.dis7.util.CoordinateConversions;
+
+/**
+ * This is an example that sends many/most types of PDUs. Useful for testing standards
+ * compliance or getting a full set of PDUs. It also writes the generated PDUs to an XML file.
+ * Adapted from OpenDIS library example package edu.nps.moves.examples
+ *
+ * @author DMcG
+ * @version $Id:$
+ */
+public class S_F_RequestingUnit
+{
+    public static final int NUMBER_TO_SEND = 5; // 5000
+
+    /**
+     * Default multicast group address we send on.
+     */
+    public static final String DEFAULT_MULTICAST_ADDRESS = "239.1.2.3";
+
+    /**
+     * Default multicast port used, matches Wireshark DIS capture default
+     */
+    public static final int DEFAULT_MULTICAST_PORT = 3000;
+
+    public enum NetworkMode {
+        UNICAST, MULTICAST, BROADCAST
+    };
+
+    /**
+     * Possible system properties, passed in via -Dattr=val networkMode:
+     * unicast, broadcast, multicast destinationIp: where to send the packet. If
+     * in multicast mode, this can be multicast. To determine broadcast
+     * destination IP, use an online broadcast address calculator, for example
+     * http://www.remotemonitoringsystems.ca/broadcast.php If in multicast mode,
+     * a join() will be done on the multicast address. port: port used for both
+     * source and destination.
+     *
+     * @param args
+     */
+    public static void main(String args[]) {
+        System.out.println("Requesting Unit started...");
+
+        // Default settings. These are used if no system properties are set. 
+        // If system properties are passed in, these are overridden later.
+        NetworkMode networkMode = NetworkMode.BROADCAST;
+        InetAddress address = null; // must be initialized, even if null
+        int port = DEFAULT_MULTICAST_PORT;
+        MulticastSocket socket = null; // must be initialized to avoid later error, even if null;
+        EntityStatePdu espdu = new EntityStatePdu();
+        DisTime disTime = new DisTime();
+
+        // Requesting Unit Location
+        double latitude = 36.372000;
+        double longitude = -121.503200;
+        
+        // Target Location
+        double tarLat = 36.371600;
+        double tarLon = -121.150300;
+        
+        EventReportPdu actionReq = new edu.nps.moves.dis7.EventReportPdu();
+        actionReq.setEventType(EventReportEventType.REQUEST_FOR_INDIRECT_FIRE_OR_CAS_MISSION);
+        
+        try {
+            address = InetAddress.getByName(DEFAULT_MULTICAST_ADDRESS);
+        } catch (UnknownHostException e) {
+            System.out.println(e + " Cannot create multicast address");
+            System.exit(0);
+        }
+
+        // All system properties, passed in on the command line via -Dattribute=value
+        Properties systemProperties = System.getProperties();
+
+        // IP address we send to
+        String destinationIpString = systemProperties.getProperty("destinationIp");
+
+        // Port we send to, and local port we open the socket on
+        String portString = systemProperties.getProperty("port");
+
+        // Network mode: unicast, multicast, broadcast
+        String networkModeString = systemProperties.getProperty("networkMode"); // unicast or multicast or broadcast
+
+        // Set up socket to send information
+        try {
+            if (portString != null) // Update port we send to, if provided
+            {
+                port = Integer.parseInt(portString);
+            }
+            socket = new MulticastSocket(port);
+
+            // Where we send packets to, the destination IP address
+            if (destinationIpString != null) {
+                address = InetAddress.getByName(destinationIpString);
+            }
+
+            // Type of transport: unicast, broadcast, or multicast
+            if (networkModeString != null) {
+                if (networkModeString.equalsIgnoreCase("unicast")) {
+                    networkMode = NetworkMode.UNICAST;
+                } else if (networkModeString.equalsIgnoreCase("broadcast")) {
+                    networkMode = NetworkMode.BROADCAST;
+                } else if (networkModeString.equalsIgnoreCase("multicast")) {
+                    networkMode = NetworkMode.MULTICAST;
+                    if (!address.isMulticastAddress()) {
+                        throw new RuntimeException("*** Error: sending to multicast address, but destination address " + address.toString() + "is not multicast");
+                    }
+                    socket.joinGroup(address);
+                }
+            } // end networkModeString
+        } catch (IOException | RuntimeException e) {
+            System.out.println("Unable to initialize network correctly, exiting.");
+            System.out.println(e);
+            System.exit(-1); // outta here
+        }
+
+        // Initialize values in the Entity State PDU object. The exercise ID is 
+        // a way to differentiate between different virtual worlds on one network.
+        // Note that some values (such as the PDU type and PDU family) are set
+        // automatically when you create the ESPDU.
+        espdu.setExerciseID((byte) 1); //(short) 1);
+
+        // The EID is the unique identifier for objects in the world. This 
+        // EID should match up with the ID for the object specified in the 
+        // VMRL/x3d/virtual world.
+        EntityID entityID = espdu.getEntityID(); // initialize, reset, override
+        // TODO check: 0 is apparently not a valid site number, per DIS specification
+        entityID.setSiteID((short) 1); // TODO utility method to allow int values
+        entityID.setApplicationID((short) 2);
+        entityID.setEntityID((short) 3);
+        espdu.setEntityID(entityID);
+
+        // Set the entity type. SISO has a big list of enumerations, so that by
+        // specifying various numbers we can say this is an M1A2 American tank,
+        // the USS Enterprise, and so on. We'll make this a tank. There is a 
+        // separate project elsehwhere in this project that implements DIS 
+        // enumerations in C++ and Java, but to keep things simple we just use
+        // numbers here.
+        // New way using entity jar(s)
+        espdu.setEntityType(new edu.nps.moves.dis7.entities.usa.platform.land.M1A2());
+
+        // Manual way:
+        /*
+		EntityType entityType = espdu.getEntityType();
+		entityType.setEntityKind(EntityKind.PLATFORM); //(short) 1);      // Platform (vs lifeform, munition, sensor, etc.)
+		entityType.setCountry(Country.UNITED_STATES_OF_AMERICA_USA); //225);               // USA
+		entityType.setDomain(Domain.inst(PlatformDomain.LAND));          // Land (vs air, surface, subsurface, space)
+		entityType.setCategory((byte) 1);        // Tank
+		entityType.setSubCategory((byte) 1);     // M1 Abrams
+		entityType.setSpecific((byte) 3);        // M1A2 Abrams
+         */
+        Set<InetAddress> broadcastAddresses;
+
+        try // Loop through sending N ESPDUs
+        {
+            System.out.println("Sending " + NUMBER_TO_SEND + " sets of packets:"); // + address.toString()
+
+            for (int index = 0; index < NUMBER_TO_SEND; index++) {
+                // DIS time is a pain in the uh, neck. DIS time units are 2^31-1 units per
+                // hour, and time is set to DIS time units from the top of the hour. 
+                // This means that if you start sending just before the top of the hour
+                // the time units can roll over to zero as you are sending. The receivers
+                // (escpecially homegrown ones) are often not able to detect rollover
+                // and may start discarding packets as dupes or out of order. We use
+                // an NPS timestamp here, hundredths of a second since the start of the
+                // year. The DIS standard for time is often ignored in the wild; I've seen
+                // people use Unix time (seconds since 1970) and more. Or you can
+                // just stuff idx into the timestamp field to get something that is monotonically
+                // increasing.
+
+                // Note that timestamp is used to detect duplicate and out of order packets. 
+                // That means if you DON'T change the timestamp, many implementations will simply
+                // discard subsequent packets that have an identical timestamp. Also, if they
+                // receive a PDU with an timestamp lower than the last one they received, they
+                // may discard it as an earlier, out-of-order PDU. So it is a good idea to
+                // update the timestamp on ALL packets sent.
+                // An alterative approach: actually follow the standard. It's a crazy concept,
+                // but it might just work.
+                int timestamp = disTime.getDisAbsoluteTimestamp();
+                espdu.setTimestamp(timestamp);
+
+                // Set the position of the entity in the world. DIS uses a cartesian 
+                // coordinate system with the origin at the center of the earth, the x
+                // axis out at the equator and prime meridian, y out at the equator and
+                // 90 deg east, and z up and out the north pole. To place an object on
+                // the earth's surface you also need a model for the shape of the earth
+                // (it's not a sphere.) All the fancy math necessary to do this is in
+                // the SEDRIS SRM package. There are also some one-off formulas for 
+                // doing conversions from, for example, lat/lon/altitude to DIS coordinates.
+                // Here we use those one-off formulas.
+                // Modify the position of the object. This will send the object a little
+                // due east by adding some to the longitude every iteration. Since we
+                // are on the Pacific coast, this sends the object east. Assume we are
+                // at zero altitude. In other worlds you'd use DTED to determine the
+                // local ground altitude at that lat/lon, or you'd just use ground clamping.
+                // The x and y values will change, but the z value should not.
+                //lon = lon + (double)((double)idx / 100000.0);
+                //System.out.println("lla=" + lat + "," + lon + ", 0.0");
+                double direction = Math.pow((double) (-1.0), (double) (index));
+                longitude = longitude + (direction * 0.00006);
+
+                double disCoordinates[] = CoordinateConversions.getXYZfromLatLonDegrees(latitude, longitude, 1.0);
+                Vector3Double location = espdu.getEntityLocation();
+                location.setX(disCoordinates[0]);
+                location.setY(disCoordinates[1]);
+                location.setZ(disCoordinates[2]);
+                System.out.println("===============");
+                System.out.println("Create new PDUs");
+                System.out.println("  latitude, longitude:   [" + latitude + ", " + longitude + "]");
+                System.out.println("  coordinate conversion: [" + disCoordinates[0] + ", " + disCoordinates[1] + ", " + disCoordinates[2] + "]");
+
+                location = espdu.getEntityLocation();
+
+                System.out.println("Espdu #" + index + " entityID=[" + entityID.getSiteID() + "," + entityID.getApplicationID() + "," + entityID.getEntityID() + "]");
+                double c[] = {location.getX(), location.getY(), location.getZ()};
+                double lla[] = CoordinateConversions.xyzToLatLonDegrees(c);
+//              System.out.println("  DIS entityLocation: [" + location.getX() + "," + location.getY() + "," + location.getZ() + "]");
+                String debugString = " Location (latitude/longitude/altitude): [" + lla[0] + ", " + lla[1] + ", " + lla[2] + "]";
+//      		System.out.println(debugString);
+
+                // Optionally, we can do some rotation of the entity
+                /*
+            Orientation orientation = espdu.getEntityOrientation();
+            float psi = orientation.getPsi();
+            psi = psi + idx;
+            orientation.setPsi(psi);
+            orientation.setTheta((float)(orientation.getTheta() + idx /2.0));
+                 */
+                // You can set other ESPDU values here, such as the velocity, acceleration,
+                // and so on.
+                // Marshal out the espdu object to a byte array, then send a datagram
+                // packet with that data in it.
+                ByteArrayOutputStream baos = new ByteArrayOutputStream();
+                DataOutputStream dos = new DataOutputStream(baos);
+                DatagramPacket packet;
+
+                // The byte array here is the packet in DIS format. We put that into a 
+                // datagram and send it.
+                espdu.marshal(dos);
+                byte[] espduArray = baos.toByteArray();
+
+                FirePdu firePdu = new FirePdu();
+                firePdu.setLocationInWorldCoordinates(espdu.getEntityLocation());
+                byte[] fireArray = firePdu.marshal();
+
+                broadcastAddresses = getBroadcastAddresses();
+                Iterator iterator = broadcastAddresses.iterator();
+                while (iterator.hasNext()) {
+                    InetAddress broadcast = (InetAddress) iterator.next();
+                    if (espduArray.length > 0) {
+                        System.out.println("Sending espdu datagram packet to " + broadcast);
+                        packet = new DatagramPacket(espduArray, espduArray.length, broadcast, port);
+                        socket.send(packet);
+                    }
+                    // TODO experiment with these!  8)
+                    if (fireArray.length > 0) {
+                        System.out.println("Sending  fire datagram packet to " + broadcast);
+                        packet = new DatagramPacket(fireArray, fireArray.length, broadcast, port); // alternate
+                        socket.send(packet);
+                    }
+                }
+
+                // Send every 1 second within loop. Otherwise all this will be all over in a fraction of a second.
+                Thread.sleep(1000); // msec
+            }
+        } catch (Exception e) {
+            System.out.println("Problem with DisExamplesOpenDis7.EspduSender, see exception trace:");
+            System.out.println(e);
+        }
+        System.out.println("===============");
+        System.out.println("DisExamplesOpenDis7.EspduSender complete.");
+    }
+
+    /**
+     * A number of sites get all snippy about using 255.255.255.255 for a
+     * broadcast address; it trips their security software and they kick you off
+     * their network. (Comcast, NPS, etc.) This determines the broadcast address
+     * for all connected interfaces, based on the IP and subnet mask. If you
+     * have a dual-homed host it will return a broadcast address for both. If
+     * you have some VMs running on your host this will pick up the addresses
+     * for those as well--e.g. running VMWare on your laptop with a local IP
+     * this will also pick up a 192.168 address assigned to the VM by the host
+     * OS.
+     *
+     * @return set of all broadcast addresses
+     */
+    public static Set<InetAddress> getBroadcastAddresses() {
+        Set<InetAddress> broadcastAddresses = new HashSet<>();
+        Enumeration interfaces;
+
+        try {
+            interfaces = NetworkInterface.getNetworkInterfaces();
+
+            while (interfaces.hasMoreElements()) {
+                NetworkInterface anInterface = (NetworkInterface) interfaces.nextElement();
+
+                if (anInterface.isUp()) {
+                    Iterator iterator = anInterface.getInterfaceAddresses().iterator();
+                    while (iterator.hasNext()) {
+                        InterfaceAddress anAddress = (InterfaceAddress) iterator.next();
+                        if ((anAddress == null || anAddress.getAddress().isLinkLocalAddress())) {
+                            continue;
+                        }
+
+                        //System.out.println("Getting broadcast address for " + anAddress);
+                        InetAddress broadcastAddress = anAddress.getBroadcast();
+                        if (broadcastAddress != null) {
+                            broadcastAddresses.add(broadcastAddress);
+                        }
+                    }
+                }
+            }
+        } catch (SocketException e) {
+            System.out.println("Problem with DisExamplesOpenDis7.EspduSender.getBroadcastAddresses(), see exception trace:");
+            System.out.println(e);
+        }
+        return broadcastAddresses;
+    }
+}
diff --git a/examples/build.xml b/examples/build.xml
index 27c9ffaf4d641e7f6032b93fe69f6f422292abda..c7c0fe5a6787667e6232c4d13a0650b2cde7edaa 100644
--- a/examples/build.xml
+++ b/examples/build.xml
@@ -11,6 +11,15 @@
 <project name="Networked_Graphics_MV3500_examples" default="default" basedir=".">
     <description>Builds, tests, and runs the project Networked Graphics MV3500 examples.</description>
     <import file="nbproject/build-impl.xml"/>
+    
+    <target name="clean.pduLogAdditions">
+        <delete verbose="true">
+            <fileset dir="pduLog">
+                <include name="Pdusave*.dislog"/>
+                <exclude name="Pdusave.dislog"/><!-- version control default example -->
+            </fileset>
+        </delete>
+    </target>
     <!--
 
     There exist several targets which are by default empty and which can be 
@@ -71,11 +80,4 @@
     nbproject/build-impl.xml file. 
 
     -->
-    
-    <target name="view.gitlab.examples" description="view gitlab MV3500 examples in web browser (Netbeans only)">
-        <echo message="https://gitlab.nps.edu/Savage/NetworkedGraphicsMV3500/tree/master/examples"/>
-        <nbbrowse url="https://gitlab.nps.edu/Savage/NetworkedGraphicsMV3500/tree/master/examples"/>
-        <!-- TODO implementation-independent approach if possible.  other Ant approachs have to be customized for each OS -->
-    </target>
-    
 </project>
diff --git a/examples/nbproject/configs/7e.PduListenerSaver.properties b/examples/nbproject/configs/7e.PduListenerSaver.properties
new file mode 100644
index 0000000000000000000000000000000000000000..769047adbaff3f8e94b5c75b0430da146ecc5034
--- /dev/null
+++ b/examples/nbproject/configs/7e.PduListenerSaver.properties
@@ -0,0 +1 @@
+main.class=OpenDis7Examples.PduListenerSaver
diff --git a/examples/nbproject/configs/7f.PduReaderPlayer.properties b/examples/nbproject/configs/7f.PduReaderPlayer.properties
new file mode 100644
index 0000000000000000000000000000000000000000..1d36802d6f732e46863c7e2ab98a2b364d81d51b
--- /dev/null
+++ b/examples/nbproject/configs/7f.PduReaderPlayer.properties
@@ -0,0 +1 @@
+main.class=OpenDis7Examples.PduReaderPlayer
diff --git a/specifications/README.md b/specifications/README.md
index 2b98ae694d1ebdb304990684492672fd6860c136..9a8b913fbcca1e6e037f61172e171b9ac231d3ec 100644
--- a/specifications/README.md
+++ b/specifications/README.md
@@ -8,11 +8,19 @@ space exploration and medicine.*
 * [Simulation Interoperabilty Standards Organization (SISO)](https://www.sisostds.org): "Simulation Interoperability and Reuse through Standards"
 * SISO Product Support Group (PSG): [DIS / RPR FOM PSG - Distributed Interactive Simulation / Real-time Platform Reference Federation Object Model](https://www.sisostds.org/StandardsActivities/SupportGroups/DISRPRFOMPSG.aspx)
 
-Resources from the working group:
-* [DIS PDU data structures](IeeeDisPduColorFigures.pdf)
-* [Compressed DIS](2019-SIW-Presentation-039_CompressedDis.pdf)
+## Working-Group Resources
 
-Manual download links follow.  IEEE standards must be downloaded manually while within NPS campus or firewall.
+**DIS/RPR FOM Product Support Group**
+* "The Distributed Interactive Simulation / Real-time Platform Reference Federation Object Model (DIS / RPR FOM) Product Support Group (PSG) is a permanent support group chartered by the SISO Standards Activity Committee to support multiple DIS-related products."
+* [Distributed Interactive Simulation / Real-time Platform Reference Federation Object Model (DIS / RPR FOM) Product Support Group (PSG)](https://www.sisostds.org/StandardsActivities/SupportGroups/DISRPRFOMPSG.aspx)
+
+* [SISO Digital Library](https://www.sisostds.org/Default.aspx?tabid=105&EntryId=31596)
+** [DIS Introduction and Briefings](https://www.sisostds.org/Default.aspx?tabid=105&EntryId=31596)
+** [IEEE 1278 Bibliography Material](https://www.sisostds.org/Default.aspx?tabid=105&EntryId=31596)
+** [DIS PDU data structures](IeeeDisPduColorFigures.pdf)
+** [Compressed DIS](2019-SIW-Presentation-039_CompressedDis.pdf) overview, Lance Call, Simulation Interoperability Workshop (SIW), Orlando Florida, February 2019.
+
+Specification download links follow.  For free access, IEEE standards must be downloaded manually while within NPS campus or firewall.
 
 ## Standards Documents
 
@@ -64,12 +72,4 @@ Guidelines are established for the verification, validation, and accreditation (
 * *Abstract.* 
 SISO-REF-010 specifies numerical values and associated definitions for fields that are identified as enumerations in SISO Standards Products and SISO-sponsored standards published by IEEE for High Level Architecture (HLA) and Distributed Interactive Simulation (DIS). Enumerations for simulations may be applied in other architectures, such as the Test and Training Enabling Architecture (TENA).
 
-## Working Groups
-
-**DIS/RPR FOM Product Support Group**
-* "The Distributed Interactive Simulation / Real-time Platform Reference Federation Object Model (DIS / RPR FOM) Product Support Group (PSG) is a permanent support group chartered by the SISO Standards Activity Committee to support multiple DIS-related products."
-* [Distributed Interactive Simulation / Real-time Platform Reference Federation Object Model (DIS / RPR FOM) Product Support Group (PSG)](https://www.sisostds.org/StandardsActivities/SupportGroups/DISRPRFOMPSG.aspx)
-
-* [SISO Digital Library](https://www.sisostds.org/Default.aspx?tabid=105&EntryId=31596) including DIS RPRFOM Product Support Group (PSG),
-** [DIS Introduction and Briefings](https://www.sisostds.org/Default.aspx?tabid=105&EntryId=31596)
-** [IEEE 1278 Bibliography Material](https://www.sisostds.org/Default.aspx?tabid=105&EntryId=31596)
+---