Skip to content

Feature: Added Dead Reckoning (DR) Module#83

Merged
leif81 merged 4 commits intoopen-dis:masterfrom
JayshKhan:master
Nov 29, 2025
Merged

Feature: Added Dead Reckoning (DR) Module#83
leif81 merged 4 commits intoopen-dis:masterfrom
JayshKhan:master

Conversation

@JayshKhan
Copy link
Copy Markdown
Contributor

Feature: Add Dead Reckoning (DR) Module

This PR implements the standard IEEE 1278.1 Dead Reckoning (DR) algorithms for open-dis-python.

The Problem — Why This Is Needed

The DIS protocol requires Dead Reckoning so simulators can move entities smoothly between network updates (typically every 5 seconds for non-maneuvering entities).

Without Dead Reckoning:

  • Developers must implement complex 3D vector math and rotation matrices themselves.
  • Applications will show "teleporting" or jerky movement between updates.
  • The library cannot be used for compliant or real-time visualization.

The Solution — What This PR Adds

A new module:

opendis/DeadReckoning.py

This module provides a clean, static API implementing all standard IEEE 1278.1 DR algorithms:

  • Static
  • FPW, RPW, RVW (common)
  • FVW
  • FPB, RPB, RVB, FVB

Key Attributes

  • High performance: uses NumPy for fast vectorized 3D math
  • Accurate: handles world/body coordinate transforms and rotation projections
  • Robust: includes safeguards against gimbal lock and numerical instability
  • No new dependencies: relies only on NumPy, already in the project

Code Comparison

Before

Users manually computed physics:

# User must implement their own physics update logic
dt = current_time - last_update_time

# "Is this world or body coordinates? How do I rotate velocity?"
# ... 50+ lines of error-prone math ...

current_x = pdu.location.x + ...

After

Standard DR function with correct behavior:

from opendis.DeadReckoning import DeadReckoning

pos, ori = DeadReckoning.drm_rvw(
    pdu.location,
    pdu.velocity,
    pdu.acceleration,
    dt,
    pdu.orientation,
    pdu.angular_velocity,
)

Verification & Tests

Included in this PR:

  • Unit tests: tests/test_dead_reckoning.py

    • Linear extrapolation
    • Quadratic motion under acceleration
    • Orientation and rotation updates
  • Example script: examples/dis_dead_reckoning.py

    • Demonstrates a vehicle accelerating over 5 seconds to verify quadratic position updates

All tests pass.


Dependencies

  • No new dependencies
  • Uses NumPy (already required by the project)

Impact

This PR upgrades open-dis-python from a packet-level parser into a viable simulation engine component.
It enables:

  • Smooth, realistic entity motion
  • Standards-compliant simulations
  • Cross-language consistency with existing DIS implementations
  • Real-time visualization and exercise playback in Python-based tools

Implemented standard DIS Dead Reckoning algorithms in
opendis/DeadReckoning.py
.
Added unit tests in
tests/test_dead_reckoning.py
.
Added usage example in
examples/dis_dead_reckoning.py
.
Algorithms support Static, FPW, RPW, RVW, FVW, FPB, RPB, RVB, FVB modes.
Implemented standard DIS Dead Reckoning algorithms in
opendis/DeadReckoning.py
.
Added unit tests in
tests/test_dead_reckoning.py
.
Added usage example in
examples/dis_dead_reckoning.py
.
Algorithms support Static, FPW, RPW, RVW, FVW, FPB, RPB, RVB, FVB modes.
@JayshKhan JayshKhan changed the title Feature: Add Dead Reckoning (DR) Module Feature: Added Dead Reckoning (DR) Module Nov 22, 2025
@leif81 leif81 self-requested a review November 25, 2025 02:17
@leif81
Copy link
Copy Markdown
Member

leif81 commented Nov 25, 2025

@JayshKhan thank-you!

@ngjunsiang would you be interested in helping to review?

@ngjunsiang
Copy link
Copy Markdown
Contributor

@leif81 @JayshKhan this is a challenging PR to review for a few reasons:

Review challenges

Unclear use case for open-dis-python

Although I'm a contributor to this codebase, I am not using it in any practical application yet (I'm primarily doing so while doing some research for a military simulation project I have not started on yet). From the reported issues and PRs so far I am thus not sure how this package is intended to be used: as a plugin to a working simulator for emitting PDUs, as a deserializer for viewing logged PDUs, as a teaching aid, ...

Uncertain API design

I think this matters because the optimal API very much depends on the use case and I am hesitant to make a judgement on how to lock in the API until the use cases become clearer. For example, if open-dis-python is primarily used for introspection of PDUs, the implementation of DRM might not matter so much since the simulation will likely have its own implementation. But if used as a simulation plugin, then the implementation begins to matter.

So far I am primarily following @leif81's guideline on following the IEEE1278.1 specification faithfully when it comes to class and attribute naming. But the Dead Reckoning Model (DRM) describes algorithms for which the input and output type/format become critical to performance.

Potential performance optimizations

We want to avoid as much overhead as possible for algorithms which will be invoked often, and which are likely to be further optimized as the codebase develops further. Ideally we want to get as much of the API "correct" from the start as possible, to minimize disruption to package users (and the need to bump major version numbers).

While Python is not primarily known for speed, numpy does get used to perform calculations in parallel in many Python packages. There is usually a boundary at which Python data types are packed into a numpy data structure, the heavy calculation offloaded to numpy's optimized algorithms, and then the results unpacked back into Python data structures.

I don't have a good feel for where this boundary is for the users of open-dis-python and the DRM algorithms, which further adds to the hesitation to make major decisions so early in the package's implementation


(Proposed) Guidelines for new features

Despite the uncertainty we are operating in, I think this is a useful place to check in with other contributors on some principles.

Maintain optionality until use cases are clearer

We should avoid locking in API designs until we know how a class or function will be used. Prefer private interfaces and avoid committing to public interfaces until a use case is demonstrated. Avoid huge classes/functions with opaque functionality, preferring small classes/functions that are easy to compose.

Minimise implementation until discussion progresses

While discussion is underway for API design and use cases, we should keep implementation light (avoid unnecessary assertions and validation), focusing on the core functionality and keeping optionality open for other enhancements.

That said, I think this PR mostly fits these requirements and would be a good fit for the package in its current state with some changes.


Review

DRM Purpose and use

IEEE1278.1 states the use of the Dead Reckoning Model as follows:

Each simulation application shall maintain an internal model of itself (representing its actual position and
orientation) and a dead reckoned model of itself. Thresholds for position and orientation shall be established
as criteria for determining whether the entity’s actual position/orientation has varied from the dead reckoned
model. When the entity’s actual position and orientation have varied from the dead reckoned position and
orientation by more than the threshold value
, the entity shall issue an Entity State PDU to communicate to
the other simulation applications the actual position and orientation. The entity shall also use this same
information communicated to other simulation applications to update the dead reckoned model of itself.

Each simulation application shall also maintain a dead reckoned model of the position of entities that are of
interest (within sight or range). An entity shall also dead reckon the orientation of other entities when
specified by the dead reckoning model in use. The dead reckoned position/orientation of other entities shall
be used to display their position/orientation in an entity’s visual or sensor displays. When an entity receives
a new update from one of the entities that it is dead reckoning, then it shall correct its dead reckoning model
and base its estimation on the most recent position, velocity, and acceleration information. Smoothing
techniques may be used to eliminate jumps that may occur in a visual display when the dead reckoned
position/ orientation of an entity is corrected to the most recently communicated position/orientation.

It is thus expected that helper functions will be needed that can take a threshold, an entity, and a dead-reckoned model, and figure out if the DRM threshold has been exceeded.

Generic Input, Specific Output

Since it is unclear how entity state (position, velocity, acceleration) will ultimately be represented in use cases, the principle of Generic Input, Specific Output suggests flexibility in the input types (i.e. typing.Sequence instead of np.ndarray), while maintaining specificity of output types (np.ndarray is fine)

  • Change annotations for DRM functions (e.g. drm_rpw()) to accept Sequence types for input.
    It may be helpful to define a type alias (e.g. Vector = typing.Sequence) to make this easier; if the Vector type definition needs to change in future then only a one-line change is needed.

De-abstract implementation

There are 9 DRM algorithms in total. Each algorithm takes in a single entity's state, and returns a new state: position, velocity, acceleration, orientation, angular velocity. This makes it seem ripe for a Strategy design pattern. I am thus hesitant to OK the current pattern of grouping all 9 algorithms under a namespace class (DeadReckoning).

May I suggest that for this PR we keep the decision lock-in small and just implement them as separate functions in DeadReckoning.py: drm_static(), drm_fpw(), etc. They would be easier to wrap in classes later without adding too much to the namespace.

The import them becomes from opendis import DeadReckoning, or import opendis.DeadReckoning as DeadReckoning; the functions could be left as part of the public interface with classes then added later.

  • Unpack the algorithms into individual functions, without the DeadReckoning namespace class

Nitpicks

In the algorithms the vectors are generally unpacked into variables, e.g.

        psi, theta, phi = orientation
        p, q, r = angular_velocity

But in get_angular_velocity_matrix() they are not:

        return np.array([
            [0, -omega[2], omega[1]],
            [omega[2], 0, -omega[0]],
            [-omega[1], omega[0], 0]
        ])

I would recommend following the same pattern to reduce cognitive overload:

        wx, wy, wz = omega
        return np.array([
            [0, -wz, wy],
            [wz, 0, -wx],
            [-wy, wx, 0]
        ])

A detailed review of the algorithmic implementation would take more time and I probably won't be able to get to it until next week. But I also think that is not too critical at this juncture, and should not block this PR from being merged once the above are addressed. Any bugs can still be reported and fixed.

@leif81
Copy link
Copy Markdown
Member

leif81 commented Nov 26, 2025

how this package is intended to be used: as a plugin to a working simulator for emitting PDUs, as a deserializer for viewing logged PDUs, as a teaching aid

Yes, it's meant to be added as a library in a python app to send and receive PDU's.

@leif81
Copy link
Copy Markdown
Member

leif81 commented Nov 26, 2025

A detailed review of the algorithmic implementation would take more time and I probably won't be able to get to it until next week. But I also think that is not too critical at this juncture, and should not block this PR from being merged once the above are addressed. Any bugs can still be reported and fixed.

I agree.

If there are bugs or questions about the DR algorithm implementations here, I would suggest comparing it with the implementation in the open-dis-java project:

https://github.com/open-dis/open-dis-java/blob/master/src/main/java/edu/nps/moves/deadreckoning/DeadReckoner.java

And it's unit tests:

https://github.com/open-dis/open-dis-java/blob/master/src/test/java/edu/nps/moves/deadreckoning/DeadReckonerTest.java

@leif81
Copy link
Copy Markdown
Member

leif81 commented Nov 27, 2025

@ngjunsiang thank-you for providing the review. One of the reasons I ask is I'm more comfortable with Java, much less with Python. So having yourself comment on and recommend best practices for Python is very helpful. Thank-you for that

Copy link
Copy Markdown
Member

@leif81 leif81 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@JayshKhan are you able to ammend this PR with these 2 suggestions provided by @ngjunsiang in #83 (comment)

  • Change annotations for DRM functions (e.g. drm_rpw()) to accept Sequence types for input.
    It may be helpful to define a type alias (e.g. Vector = typing.Sequence) to make this easier; if the Vector type definition needs to change in future then only a one-line change is needed.
  • in the algorithms unpack the vectors into variables to reduce cognitive overload:

@JayshKhan
Copy link
Copy Markdown
Contributor Author

Hey @leif81, @ngjunsiang — thanks for the great feedback!

I’ll go ahead and update the PR with those suggestions. I’ll modify the DRM function annotations to use Sequence types and define the Vector alias to make things easier to change later on. Also, I’ll unpack the vectors into variables to reduce cognitive load as suggested.

I’ll get the changes pushed soon

JayshKhan and others added 2 commits November 27, 2025 19:07
- Update type hints to accept generic Sequence inputs (lists/tuples)
- Improve variable unpacking in matrix helper functions for readability
@JayshKhan JayshKhan requested a review from leif81 November 27, 2025 14:26
@leif81
Copy link
Copy Markdown
Member

leif81 commented Nov 28, 2025

@JayshKhan thank-you that was quick!

@ngjunsiang are you good with the changes?

@ngjunsiang
Copy link
Copy Markdown
Contributor

looks good to me 👍

@leif81 leif81 merged commit 10db1e4 into open-dis:master Nov 29, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants