Satellite 3D model appears off by 90 degrees

Hello!

I’ve been struggling quite a bit with getting correct orientation of our satellite 3D model. Prior to posting here, I’ve read every blog and issue on Stack Overflow I can find on the subject! I’m using the same model we use in STK connect (a .dae file), and have the file converted to .glb both by a coworker who used Blender, and yesterday loaded the model into the Cesium iOn cloud but still have the same issue:


To explain what we’re looking at, I’ve added the cesium inspector widget to get the green, blue, and red lines for the body axes. The darker orange line is a sun vector…greenish yellow line is the tail of the orbit. We are looking at the satellite’s solar panels from the top-side…which are pointed 90 degrees off from the sun vector. I am looking for a solution using the client-side API to update the position of the model…ideally one that works when it is initially rendered so I do not need to apply some kind of offset every time new data comes in (typically every second but could be faster).

From the opposite side…


What I am hoping to achieve is that the ‘blue’ part of the solar panels would be perpendicular to the orange sun vector, such that they are pointing at the sun!

Note that there are many similar posts to this one…most involve stationary objects like a building that is upside down or turned on it’s side etc. I’ve also seen one about a rocket etc…I feel that I am experiencing the same/similar issue in telling the 3D model which direction is ‘up’.etc…currently using Cesium 1.85.

I’ve also experimented with both loading the file locally using the primitive API, and got this working with the Entity API yesterday, but only by loading the 3D asset from the iOn cloud? Separate question, but why can’t I load the same .glb file locally for an Entity? Do Entities need to be loaded asynchronously, or is there a scaling issue (I don’t get any errors…I can still see the orbit path but no satellite model? Maybe it’s reeeeeeally small?)

Here is a code snippet using the Entity API. Note that for brevity, we are inside the .then callback after the model is loaded, and inside another callback where json data is read-in and parsed every second during a live simulation:

//the variables below are previously declared with a 'let' so we can update them with new data
position = new Cesium.Cartesian3(parseFloat(telemetry.PositionX), parseFloat(telemetry.PositionY), parseFloat(telemetry.PositionZ))

 //Our Data is scalar first, so to meet Cesium's x, y, z, w formatting, index_0 or Q_0 is passed in last
orientation = new Cesium.Quaternion(parseFloat(telemetry.Q_1), parseFloat(telemetry.Q_2), parseFloat(telemetry.Q_3), parseFloat(telemetry.Q_0))

mySatelliteEntity.position = position
mySatelliteEntity.orientation = orientation

Another question…the tail of the orbit works with a sampledPositionProperty to interpolate between data points - does this not work for a 3D model? rendering of the satellite 3D model is choppy as it is redrawn with its new position and orientation every second. I can speed up how often we fetch data but this is expensive!

let time = new Cesium.JulianDate
let positionProperty = new Cesium.SampledPositionProperty(); 
let orientationProperty = new Cesium.SampledProperty(Cesium.Quaternion)

//then, in the .then callback...note that the time variable is also updated by adding seconds to the start time variable
time = Cesium.JulianDate.addSeconds(start, parseFloat(telemetry.SimTime), new Cesium.JulianDate)
positionProperty.addSample(time, position)
orientationProperty.addSample(time, orientation)

mySatelliteEntity.position = positionProperty //no errors, no 3D model is rendered
mySatelliteEntity.orientation = orientationProperty //no errors, no 3D model is rendered

myOrbitPathPolylineEntity.position = positionProperty//works great!

Lastly, here is my attempt to update the orientation by 90 degrees each time I parse new JSON data. Still hoping there is a way to do this just once when the model is loaded? Of course open to suggestions, including any recommendations on other ways to convert the .dae file to .glb. Optimally this could be loaded locally as simulations may be run offline in the future once we have a working prototype.

//From the same code once we've updated the position and orientation variables above...
//convert the orientation Quaternion into a Heading Pitch Roll object
let hpRoll =  new Cesium.HeadingPitchRoll.fromQuaternion(orientation);
hpRoll.heading = hpRoll.heading + Cesium.Math.toRadians(-90);
let attitude = Cesium.Transforms.headingPitchRollQuaternion(position, hpRoll);
mySatelliteEntity.orientation = attitude;

//Alternatively I also tried...
let hpRoll =  new Cesium.HeadingPitchRoll.fromQuaternion(orientation);
const fixedFrameTransform = Cesium.Transforms.localFrameToFixedFrameGenerator(
       "north",
        "west"
  );
let attitude = new Cesium.Quaternion
Cesium.Transforms.headingPitchRollQuaternion(
        position, 
         hpRoll,
         Cesium.Ellipsoid.WGS84,
         fixedFrameTransform, 
         attitude
);
satelliteEntity.orientation = attitude

So the code above…adapted from a stationary building turned on its side, does work to update the orientation of the satellite model, but I’m not getting the results I’m hoping for? Neither approaches are giving me the exact 90 degree offset I am looking for. Am I doing something wrong, and is there another approach I can try that does not involve updating the orientation twice each time I ingest data.

Note that this issue is also apparent when using CZML to playback a satellite simulation run. I can create a CZML file using Python and our output data, and load the .glb file locally and it works but the same attitude correction is also needed here. Please help! My apologies I am not able to create a SandCastle that will ingest live data from our simulation software, but happy to provide some more code etc as needed!