The grand idea is to have an accelerometer in a vehicle and use it to detect when the vehicle is level. The problem is that the accelerometer may not be perfectly aligned with the vehicle. As a result when the accelerometer is level, the vehicle will not be.
The solution is calibration, and the assumption is that only simple tools are available to do this, typically an old fashioned bubble in a glass tube spirit level. This can be used to determine when the vehicle is level, but won't supply N decimal place numbers for inclination.
The maths below shows that calibration can be accomplished with two arrangements, first the vehicle completely level and second with the vehicle level across its width but sloping along its length.
Assume we have an accelerometer whose axes are not aligned with those of the vehicle. To get from the acceleration vector in the accelerometer coordinates to the acceleration vector in the vehicle coordinates there is a rotation matrix.
` A=R A' `
` ((x),(y),(z)) = ((a11,a12,a13),(a21,a22,a23),(a33,a32,a31)) ((x'),(y'),(z')) `
` A=R_z R_y R_x A' `
` R=((c_z c_y,c_z s_y s_x-c_x s_z, s_z s_x + c_z c_x s_y),(c_y s_z, c_z c_x + s_z s_y s_x, c_x s_z s_y - c_z s_x),(-s_y, c_y s_x, c_y c_x)) `
Where c, s and t stand for cosine, sine and tangent and the subscripts x,y and z for the angle of rotation about the respective axis. The matrix is from  "Tait-Bryan angles", ` Z_1 Y_2 X_3 `.
In the special case where the vehicle is level, the x and y components of acceleration are zero and rotations about Z have no effect.
` R_(xy)=((c_y,s_y s_x, c_x s_y),(0, c_x , - s_x),(-s_y, c_y s_x, c_y c_x)) `
` ((0),(0),(z)) = R_(xy) ((x'),(y'),(z')) `
` 0 = c_x y' - s_x z' `
` theta_x= arctan((y')/(z')) `
` 0 = c_y x' + s_y s_x y' + c_x s_y z'`
` c_y^2 ((x')/(z'))^2 = s_y^2 s_x^2 t_x^2 + 2 s_y s_x t_x c_x s_y + c_x^2 s_y ^2 `
` (c_y/s_y)^2 ((x')/(z'))^2 = s_x^2 t_x^2 + 2 s_x t_x c_x + c_x^2 `
` (c_y/s_y)^2 ((x')/(z'))^2 = s_x^2 t_x^2 + s_x^2 + 1 `
` (c_y/s_y)^2 ((x')/(z'))^2 = s_x^2 (s_x^2 + c_x^2)/c_x^2 + 1 `
` (c_y/s_y)^2 ((x')/(z'))^2 = s_x^2 /c_x^2 + 1 `
` (c_y/s_y)^2 ((x')/(z'))^2 = (s_x^2 +c_x^2) / c_x^2 `
` (c_y/s_y)^2 ((x')/(z'))^2 = 1 / c_x^2 `
` c_x^2 ((x')/(z'))^2 = t_y^2 `
` theta_y = - arctan(c_x ((x')/(z'))) `
The other special case is when the vehicle is level across its width but sloping along its length.
` ((0),(y),(z)) = R_z R_(xy) ((x''),(y''),(z'')) `
` R_z=((c_z, -s_z, 0),(s_z, c_z, 0),(0, 0, 1)) `
` 0 = c_z x'' - s_z y'' `
` theta_z=arctan((x'')/(y'')) `
From the two special cases it is possible to determine the rotation matrix that converts from accelerometer coordinates to vehicle coordinates. It is expected the two coordinate systems will be close to one another and the rotation angles will be small.
- Euler angles (Wikipedia )
- Rotation matrix (Wikipedia)
- A simple way of calculating Euler Angles from Rotation Matrix — help! (Physics StackExchange)