Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
44 commits
Select commit Hold shift + click to select a range
83b4220
coning alg
vegard-solum-4ss Dec 4, 2025
87f96d2
sculling
vegard-solum-4ss Dec 4, 2025
2da4c21
some fixes
vegard-solum-4ss Dec 4, 2025
1b7c4ea
small fix
vegard-solum-4ss Dec 5, 2025
79ab497
some changes
vegard-solum-4ss Dec 5, 2025
2e230b7
some changes
vegard-solum-4ss Dec 5, 2025
0eacaeb
use private cross
vegard-solum-4ss Dec 5, 2025
b19e73a
docstring
vegard-solum-4ss Dec 5, 2025
6586665
reference equations
vegard-solum-4ss Dec 5, 2025
32f43fd
some changes
vegard-solum-4ss Dec 5, 2025
e9116ec
some changesw
vegard-solum-4ss Dec 8, 2025
843c43d
delete commented out code
vegard-solum-4ss Dec 8, 2025
088aeae
comment out trapezoid integral
vegard-solum-4ss Dec 11, 2025
153ad48
docstrign
vegard-solum-4ss Dec 11, 2025
e3b4fc5
wnd order sculling
vegard-solum-4ss Dec 11, 2025
48ec92b
S2 version
vegard-solum-4ss Dec 11, 2025
2a649f7
2nd order S2 version
vegard-solum-4ss Dec 11, 2025
ab3e550
renaming, refactoring and deleting old code
vegard-solum-4ss Dec 12, 2025
f0dde5b
update references
vegard-solum-4ss Dec 12, 2025
df3767c
fix eq comment
vegard-solum-4ss Dec 12, 2025
94e7d87
small fix
vegard-solum-4ss Dec 12, 2025
4104deb
small fix
vegard-solum-4ss Dec 12, 2025
722a45a
docstring fix
vegard-solum-4ss Dec 12, 2025
72ff53c
docstring
vegard-solum-4ss Dec 12, 2025
20d5ff0
docstring fix
vegard-solum-4ss Dec 12, 2025
dc00189
small updates to docstring
ali-cetin-4ss Dec 18, 2025
d45ae00
small updates to docstring
ali-cetin-4ss Dec 18, 2025
b7086d5
test init
vegard-solum-4ss Dec 18, 2025
586c0ec
add flush, remove reset
ali-cetin-4ss Dec 18, 2025
eae10dd
docstring
ali-cetin-4ss Dec 18, 2025
cf634d6
Merge branch 'coning-sculling-v2' of https://github.com/4Subsea/smsfu…
ali-cetin-4ss Dec 18, 2025
40d4f59
test update
vegard-solum-4ss Dec 18, 2025
3dc365c
Merge branch 'coning-sculling-v2' of https://github.com/4Subsea/smsfu…
vegard-solum-4ss Dec 18, 2025
6df28ec
fix tests
vegard-solum-4ss Dec 18, 2025
27d6468
docstring fix
vegard-solum-4ss Dec 18, 2025
8a438e4
test puyre pitch
vegard-solum-4ss Dec 18, 2025
48a5869
test pure yaw
vegard-solum-4ss Dec 18, 2025
9cad702
test pure sway
vegard-solum-4ss Dec 18, 2025
2197fe3
test pure heave
vegard-solum-4ss Dec 18, 2025
d96cf4f
test rot and acc
vegard-solum-4ss Dec 18, 2025
fa8e3b4
all linear test
vegard-solum-4ss Dec 18, 2025
5b5a59f
test all rots
vegard-solum-4ss Dec 18, 2025
b02b514
test dtheta
vegard-solum-4ss Dec 18, 2025
455c6a5
test flush
vegard-solum-4ss Dec 18, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions src/smsfusion/__init__.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
from . import benchmark, calibrate, constants, noise
from ._coning_sculling import ConingScullingAlg
from ._ins import AHRS, VRU, AidedINS, FixedNED, StrapdownINS, gravity
from ._smoothing import FixedIntervalSmoother
from ._transforms import quaternion_from_euler
Expand All @@ -16,4 +17,5 @@
"StrapdownINS",
"VRU",
"quaternion_from_euler",
"ConingScullingAlg",
]
162 changes: 162 additions & 0 deletions src/smsfusion/_coning_sculling.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,162 @@
import numpy as np
from numpy.typing import ArrayLike

from smsfusion._vectorops import _cross


class ConingScullingAlg:
"""
Coning and sculling algorithm.

Integrates the specific force and angular rate measurements to coning and
sculling corrected velocity (dvel) and attitude (dtheta) changes.

Can be used in a strapdown algorithm as:

vel[m+1] = vel[m] + R(q[m]) @ dvel[m] + dvel_corr
q[m+1] = q[m] ⊗ dq(dtheta[m])

where,

dvel_corr = [0, 0, g * dt] (if 'NED')
dvel_corr = [0, 0, -g * dt] (if 'ENU')

and,

- dvel[m] is the sculling integral, i.e., the velocity vector change (no gravity
correction) from time step m to m+1.
- dtheta[m] is the coning integral, i.e., the rotation vector change from time
step m to m+1.
- dq(dtheta[m]) is the unit quaternion representation of the rotation increment
over the interval [m, m+1].
- R(q[m]) is the rotation matrix (body-to-nav) corresponding to the attitude
quaternion q[m].

Here, ⊗ denotes quaternion multiplication (Hamilton product).

The coning and sculling integrals are computed using a 2nd order algorithm as
described in [1]_ and [2]_.

References
----------
.. [1] Savage, Paul G., Strapdown System Algorithms, AD-P003 621, https://apps.dtic.mil/sti/tr/pdf/ADP003621.pdf
.. [2] Savage, Paul G., Strapdown Analytics, 2nd Edition, Part 1, 2007, https://strapdownassociates.com/Strapdown%20Analytics%20II%20Part%201.pdf
"""

def __init__(self, fs: float):
self._fs = fs
self._dt = 1.0 / fs

# Coning params
self._theta = np.zeros(3, dtype=float)
self._dtheta_con = np.zeros(3, dtype=float)
self._dtheta_prev = np.zeros(3, dtype=float)

# Sculling params
self._vel = np.zeros(3, dtype=float)
self._dvel_scul = np.zeros(3, dtype=float)
self._dv_prev = np.zeros(3, dtype=float)

def update(self, f: ArrayLike, w: ArrayLike, degrees: bool = False):
"""
Update the coning (dtheta) and sculling (dvel) integrals using new measurements.

Parameters
----------
f : array-like, shape (3,)
Specific force (acceleration + gravity) measurements [f_x, f_y, f_z],
where f_x, f_y and f_z are specific forces along the x-, y-, and z-axis,
respectively.
w : array-like, shape (3,)
Angular rate measurements [w_x, w_y, w_z], where w_x, w_y and w_z are
angular rates about the x-, y-, and z-axis, respectively.
degrees : bool, default False
Specifies whether the angular rates are given in degrees or radians (default).
"""
f = np.asarray(f, dtype=float)
w = np.asarray(w, dtype=float)

if degrees:
w = (np.pi / 180.0) * w

# View for readability
theta = self._theta
dtheta_con = self._dtheta_con
dtheta_prev = self._dtheta_prev
vel = self._vel
dvel_scul = self._dvel_scul
dv_prev = self._dv_prev

dv = f * self._dt # backward Euler
dtheta = w * self._dt # backward Euler

# Sculling update (2nd order)
# See Eq. (7.2.2.2.2-15) in ref [2]_ and Eq. (56) in ref [1]_
dvel_scul += 0.5 * (
_cross(theta + (1.0 / 6.0) * dtheta_prev, dv)
+ _cross(vel + (1.0 / 6.0) * dv_prev, dtheta)
)
vel += dv

# Coning update
# See Eq. (26) in ref [1]_
dtheta_con += 0.5 * _cross(theta + (1.0 / 6.0) * dtheta_prev, dtheta)
theta += dtheta

dv_prev[:] = dv
dtheta_prev[:] = dtheta

def dtheta(self, degrees=False):
"""
Peek at the accumulated 'body attitude change' vector. I.e., the rotation
vector describing the total rotation over all samples since initialization
(or last reset).

Parameters
----------
degrees : bool, default False
Specifies whether the returned rotation vector should be in degrees
or radians (default).
"""
dtheta = self._theta + self._dtheta_con
return np.degrees(dtheta) if degrees else dtheta

@property
def _dvel_rot(self):
return 0.5 * _cross(self._theta, self._vel)

def dvel(self):
"""
Peek at the accumulated specific force velocity change vector. I.e.,
the total change in velocity (no gravity correction) over all samples since
initialization (or last reset).
"""
return self._vel + self._dvel_rot + self._dvel_scul

def flush(self, degrees=False):
"""
Return dtheta (the accumulated 'body attitude change' vector) and
dvel (the accumulated specific force velocity change vector), and reset
the coning (dtheta) and sculling (dvel) integrals to zero.

Parameters
----------
degrees : bool, default False
Specifies whether the returned rotation vector should be in degrees
or radians (default).

Returns
-------
dtheta : ndarray, shape (3,)
The accumulated 'body attitude change' vector, see :meth:`dtheta`.
dvel : ndarray, shape (3,)
The accumulated specific force velocity change vector, see :meth:`dvel`.
"""
dtheta = self.dtheta(degrees=degrees)
dvel = self.dvel()

self._theta[:] = np.zeros(3, dtype=float)
self._dtheta_con[:] = np.zeros(3, dtype=float)
self._dvel_scul[:] = np.zeros(3, dtype=float)
self._vel[:] = np.zeros(3, dtype=float)
return dtheta, dvel
Loading