```
In [1]:
```import numpy as np
import matplotlib.pyplot as plt
import matplotlib.pyplot as plt
from matplotlib import rcParams
rcParams['legend.fontsize'] = 14
rcParams['axes.labelsize'] = 14
rcParams['axes.titlesize'] = 14
%matplotlib inline

```
In [2]:
```from pyins import sim

Because we are not going to use IMU readings we set high sampling period.

```
In [3]:
```dt = 1

```
In [4]:
```traj, gyro, accel = sim.from_position(dt, [30, 30], [0, 90], t=[0, 48 * 3600])

On the plots we see that the velocity as well as the heading are constant.

```
In [5]:
```plt.figure(figsize=(13, 4))
plt.subplot(131)
plt.plot(traj.lon, traj.lat)
plt.ylabel("lat, deg", fontsize=16)
plt.xlabel("lon, deg", fontsize=16)
plt.subplot(132)
plt.plot(traj.index * dt / 3600, traj.VE, label='VE')
plt.plot(traj.index * dt / 3600, traj.VN, label='VN')
plt.xlabel("time, h")
plt.ylabel("m/s")
plt.legend(fontsize=16, loc='center right')
plt.subplot(133)
plt.plot(traj.index * dt / 3600, traj.h, label='heading')
plt.xlabel("time, h")
plt.ylabel("deg")
plt.legend(loc='best')
plt.tight_layout()

```
```

```
In [6]:
```from pyins.filt import _error_model_matrices
from scipy.linalg import eigvals

```
In [7]:
```F = _error_model_matrices(traj.iloc[:1])[0][0]

```
In [8]:
```eigvals(F)

```
Out[8]:
```

Here we illustrates the previous findings by computing the errors solution for some initial values.

```
In [9]:
```from pyins.filt import propagate_errors
from pyins import earth

```
In [10]:
```def plot_errors(dt, err, step=1000):
plt.figure(figsize=(15, 10))
plt.subplot(331)
err = err.iloc[::step]
t = err.index * dt / 3600
plt.plot(t, err.lat, label='lat')
plt.xlabel("time, h")
plt.ylabel("m")
plt.legend(loc='best')
plt.subplot(334)
plt.plot(t, err.lon, label='lon')
plt.xlabel("time, h")
plt.ylabel("m")
plt.legend(loc='best')
plt.subplot(332)
plt.plot(t, err.VE, label='VE')
plt.xlabel("time, h")
plt.ylabel("m/s")
plt.legend(loc='best')
plt.subplot(335)
plt.plot(t, err.VN, label='VN')
plt.xlabel("time, h")
plt.ylabel("m/s")
plt.legend(loc='best')
plt.subplot(333)
plt.plot(t, err.h, label='heading')
plt.xlabel("time, h")
plt.ylabel("deg")
plt.legend(loc='best')
plt.subplot(336)
plt.plot(t, err.p, label='pitch')
plt.xlabel("time, h")
plt.ylabel("deg")
plt.legend(loc='best')
plt.subplot(339)
plt.plot(t, err.r, label='roll')
plt.xlabel("time, h")
plt.ylabel("deg")
plt.legend(loc='best')
plt.tight_layout()

```
In [11]:
```err = propagate_errors(dt, traj, d_lat=500, d_lon=-300, d_VE=2, d_VN=-1, d_h=0.05, d_p=0.01, d_r=-0.02)

```
In [12]:
```plot_errors(dt, err)

```
```

It's easy to compute responses to individual error sources and thus compute the full transition matrix. But it will be hard and cumbersome to illustrate such result.

A simpler way is to repeat computations with some initial errors disabled and compare the results with the full picture. By such approach the following conclusions can be easily verified:

- An initial longitude error doesn't excite other errors and doesn't evolve itself. It means that it's impossible to distinguish between different values of longitude and the navigation can be started from any value.
- An initial latitude error doesn't influence other errors significantly.
- Velocity, pitch and roll errors form a subsystem weakly influenced by other errors.
- Heading error is mostly determined by initial heading, pitch and roll errors.

Note that 2, 3, 4 are true as long as $\Delta r / R$ ($\Delta r$ is the position error and $R$ is Earth radius) is significantly less than attitude errors.

Now we investigate errors propagation when the inertial sensors containing constant biases.

```
In [13]:
```err = propagate_errors(dt, traj, d_accel=[0, 1e-2, 0])

We see that accelerometer biases also creates oscillatory errors. The things worth noting are that longitude, pitch and heading errors are biased relative to zero by their oscillation amplitudes.

Errors due to the North bias are conceptually the same, with East and North axes switched. The heading error won't have a bias.

Errors due to the vertical accelerometer bias is irrelevant for horizontal errors components.

```
In [14]:
```plot_errors(dt, err)

```
```

First consider errors due to the East gyro bias.

```
In [15]:
```err = propagate_errors(dt, traj, d_gyro=[0, 1e-7, 0])

```
In [16]:
```plot_errors(dt, err)

```
```

Now consider errors due to the North bias.

```
In [17]:
```err = propagate_errors(dt, traj, d_gyro=[-1e-7, 0, 0]) # North bias is +1e-7.

This is the first case when the error in longitude growth approximately linearly to an unbounded value. The rate of the longitude error growth is $R \epsilon_N \cos^2 \varphi$ , where $\varphi$ is the latitude and $\epsilon$ is the North gyro bias.

In our case the growth in kilometers per hour is equal to:

```
In [18]:
```1e-7 * earth.R0 * 3.6 * np.cos(np.deg2rad(traj.lat[0])) ** 2

```
Out[18]:
```

So the expected final error is 1.72 * 48 = 82.5 km, which matches well with the simulation results.

```
In [19]:
```plot_errors(dt, err)

```
```

Now consider errors due to the vertical bias.

```
In [20]:
```err = propagate_errors(dt, traj, d_gyro=[0, 0, 1e-7])

```
In [21]:
```1e-7 * earth.R0 * 3.6 * np.sin(np.deg2rad(traj.lat[0])) * np.cos(np.deg2rad(traj.lat[0]))

```
Out[21]:
```

```
In [22]:
```plot_errors(dt, err)

```
```

When the coefficients of the error equations are constant, then:

- The horizontal INS errors contain only oscillatory modes. The two periods are 24 hours (corrected by a vehicle East velocity) and the 84 minutes (slightly perturbed depending on a latitude and a vehicle's velocity).
- The response to constant accelerometer biases is also bounded and oscillatory. The same holds true for a constant East gyro bias.
- In a presence of North and vertical gyro biases the longitude error growths unbounded, with the rate of growth proportional to $R \varepsilon$. And this is the only unbounded error. It proves the fact that the pure inertial performance of an INS is determined by gyroscopes accuracy.

Generally the behaviour of INS errors can be analyzed at any level of detail and for arbitrary trajectories using pyins.

```
In [ ]:
```