# How should the ISS telemetry for attitude be converted to roll, pitch, and yaw?

NASA, via Lightstreamer SRL, provides four "quaternion components" for the International Space Station's current attitude. These are labelled USLAB000018–21 and described as "US Current Local Vertical Local Horizontal (LVLH) Attitude Quaternion Component [0-3]" respectively. For example, at roughly 15:01:30 2017-07-26 UTC we saw:

TIME_000001:   17938885000
USLAB000018:   0.999387085437775    "[...] Component 0"
USLAB000019:   0.00414502713829279  "[...] Component 1"
USLAB000020:  -0.00237915641628206  "[...] Component 2"
USLAB000021:  -0.0346784666180611   "[...] Component 3"


Lightstreamer has a demo website that displays the raw values as received here: http://demos.lightstreamer.com/ISSLive/ - to get to those attitude values, click ADCO on the left, then click on the four USLAB000018–21 subheadings.

When we built http://telemetry.space, I converted those to roll, pitch, and yaw using the threejs library by guessing components 0, 1, 2, and 3 correspond to x, y, z, w, like so:

const quaternion = new THREE.Quaternion();
quaternion.x = USLAB000018;
quaternion.y = USLAB000019;
quaternion.z = USLAB000020;
quaternion.w = USLAB000021;
const euler = new THREE.Euler();
euler.setFromQuaternion(quaternion);



Producing this output via JSFiddle:

Roll   -176.0264711321606
Pitch    -0.28893712359948726
Yaw      -0.46525059186533985


Short of getting a HAM license, how can I verify this is right or wrong, and if wrong—as was reasonably suspected by others and myself in another query—how should I convert those USLAB values to roll, pitch, yaw for telemetry.space?

I've used Javascript and threejs in the examples above only as it's what I originally used and any help in any programming language, plain old English, or just the maths will be very much appreciated.

It's not clear from the documentation what the four parameters of quaternion represent explicitly -- specifically, whether w is the real part. From my experience, NASA likes to specify quaternions with the real part first, so you may move quaternion.w to USLAB000018 and shift the remaining three accordingly. ISS also reports its Tait-Bryan angles in Yaw-Pitch-Roll order, so you would need to set the euler.order option to 'ZYX'.

Under nominal operation, all three angles (YPR) should be within a few degrees of zero. 180 degrees roll is definitely not correct.

See my modifications in action here.

• Thanks Tristan, this was very useful and very informative. I've made those changes to telemetry.space as, at the least they produce much saner looking results than what I had! Still need to find a way to test the approach, e.g., a set of validated quaternion components with corresponding YPRs. Jul 27 '17 at 0:37
• The results I'm seeing on there look more or less correct. I would suggest recording those values tomorrow during the Soyuz docking (though they might not maneuver much for where they're going to dock). Jul 27 '17 at 15:55
• Thanks Tristan, great idea. We store all the raw telemetry in a relational database, so we'll have the USLAB000018-21 and all the rest too. Jul 27 '17 at 16:05
• Looking at this older question: space.stackexchange.com/questions/20358/… -- it looks like your values match up nicely now. Jul 27 '17 at 21:48
• Oh, great, they got ISSLive! working again. I've checking that for a long while hoping to at least have that to compare to someday and its always been broken--but good in a way as I finally gave up and asked here! Jul 27 '17 at 22:55

Check out the code in the ISS Mimic github which has a calculation for roll/pitch/yaw using the ISS telemetry via the lightstreamer api

roll = math.degrees(math.atan2(2.0 * (quaternion0 * quaternion1 + quaternion2 * quaternion3), 1.0 - 2.0 * (quaternion1 * quaternion1 + quaternion2 * quaternion2))) + rollerror
pitch = math.degrees(math.asin(max(-1.0, min(1.0, 2.0 * (quaternion0 * quaternion2 - quaternion3 * quaternion1))))) + pitcherror
yaw = math.degrees(math.atan2(2.0 * (quaternion0 * quaternion3 + quaternion1 * quaternion2), 1.0 - 2.0 * (quaternion2 * quaternion2 + quaternion3 * quaternion3))) + yawerror


This is the mapping between the ISS Telemetry and those values used in the equation above (derived from the code here)

USLAB000028 = quaternion0
USLAB000029 = quaternion1
USLAB000030 = quaternion2
USLAB000031 = quaternion3

These are the Commanded attitude values rather than the Current values shown in the original question. I don't know why ISS Mimic is using those - but hopefully, these will nevertheless provide an additional way to verify the correct equation for roll/pitch/yaw

• FYI, I'm on the mimic team (at least in small part). I asked one of my colleagues why we used Commanded vs Current. We couldn't recall why that choice was made (speculating that at some point in the past, the current quaternion may have had some ratty data in it, or maybe we were approaching a gimbal lock singularity, but not sure). Will probably be switching to current in a future commit. That said, Commanded + error as the code in your answer lists should give the current data. Apr 5 at 19:38
• Hi @Tristan - love what you have done with ISS Mimic. Thanks for clarifying the use of Commanded vs Current attitude in that code. I notice you do use the Current values in your Javascript code [here](view-source:iss-mimic.github.io/Mimic) I'm using this in another python project and will be sure to reference the source in that code. I did notice you use the current values in the Javascript code here. I want to be able to get a notice when the ISS is heading into a big attitude maneuver (one of those lovely 180's or 90 deg flips). Apr 6 at 0:27
• Clarifying that link which got messed up.. view source here and search for USLAB000YAW Apr 6 at 0:34
• So the page that you link (and the one-screen dashboard at iss-mimic.github.io/Mimic/dashboard.html) have both the commanded and the current quaternions. Both of those are actually just a straight dump of all of the public telemetry channels, including some that never made it onto ISS Live. The first one was something I threw together in probably 45 minutes, but it and the dashboard one have proven surprisingly useful. Apr 6 at 0:53