shashwatak / satellite-js

Modular set of functions for SGP4 and SDP4 propagation of TLEs.
MIT License
902 stars 145 forks source link

Dates can effect propagation drasitically #86

Closed delaneyj closed 3 years ago

delaneyj commented 3 years ago

I'm using satellite-js in a 3d view. It takes the currentTime and and a delta and creates a point in the future filling a VBO. When it works it works wonderfully.

If try to created new buffer per frame I'm getting very odd behavior

Correct: image

Bad: image image

When the trails are short it looks like "popping" but its much clearer at this scale that it's very wrong. The only thing changing in these 2 screenshots is the Date used in propagate. I'm using new Date(milliseconds). Is there something about that resolution that's cause floating point errors or do I need to clamp to some interval?

Video

https://user-images.githubusercontent.com/438252/104109619-a27a0b00-5284-11eb-9748-c94b9441950f.mp4

https://user-images.githubusercontent.com/438252/104109620-a4dc6500-5284-11eb-8f80-95d721234e8d.mp4

Thanks for your time.

delaneyj commented 3 years ago

Is #75 related?

thkruz commented 3 years ago

Can you post some of the code you are using for the calls to propagate and/or sgp4?

I don't think #75 will cause that kind of effect, but its possible. Try passing the value as a number instead of a date object. If #75 is the problem then that should eliminate it.

I am having a hard time fully understanding what your program SHOULD look like. In the good screenshot, are those trails behind the cones showing the line from the last full orbit? I assume that pattern is created by copying a satellite and changing one of its elements (inclination maybe?).

Your first bad picture looks like the same thing but with the satellites spread out with different right ascensions or maybe different epoch times. If you are supplying the same element set different propagation times you will get that effect.

I got lost on the third photo. It looks more like your graphics code is having an issue than your propagation code. How do you calculate where to draw the lines?

In the second video I see the jitter you were talking about, but it isn't moving like a millisecond rounding error. I would expect to see the line fall behind and then catch up over and over again (kind of like you see), but in certain frames it is actually jumping ahead.

If it were my code I would console log the leading position values from that line and see if it is moving in a continuous line or if it is jumping around a bit (just see if the x value is moving in a continuous direction smoothly). That will help narrow down if it is a math problem with how you are passing time to the sgp4 function.

You could also limit the number of satellites to one to see if the effect only happens as you add a large number of satellites. If you are running sgp4 every frame on a large number of satellites, it might just be too much computation and something is getting messed up there. Are you getting 60 FPS? If you are getting 10 FPS that could be part of the issue. Are you calling requestAnimationFrame() in your rendering loop or a fixed timeout?

Sorry that is more questions than answers. My project works very similarly (3D with orbit lines and mesh objects) and I don't have this issue - so I am fairly confident it is an implementation problem - but I am sure we can figure it out.

delaneyj commented 3 years ago

Sure @thkruz , thanks for your response! I am really hoping its user error.

Try passing the value as a number instead of a date object. Using it via typescript to passing as a number doesn't compile. I did try spg4 and hopefully got my minutes since epoch right but still saw the same behavior.

In the good screenshot, are those trails behind the cones showing the line from the last full orbit? I assume that pattern is created by copying a satellite and changing one of its elements (inclination maybe?).

That's my fault I should have been clear on what I was doing. I take a SatRec for the selected satellite and just run it through the propagate at a delta in the future, nothing else changed. Then with those points update a VBO's content.

I got lost on the third photo. It looks more like your graphics code is having an issue than your propagation code. How do you calculate where to draw the lines?

const times = new Array<number>()
const points = new Array<Vector3>()
for (let i = 0; i < 10; i++) {
  const p = new Vector3()
  const to = 50 * deltaTime * i
  const t = currentTime + to
  const info = propagate(satRec, new Date(t))
  const pos = info.position as EciVec3<number>
  p.set(pos.x, pos.y, pos.z).multiplyScalar(KM_NORMALIZED)
  points.push(p)
  times.push(t)
}

console.log(times[0],times[1],points[points.length - 1].distanceTo(points[0]))

Running this on a single object gives me image From the distance number you can see the "popping". If I add more lines so it wraps the globe you see the behavior in the first video where the lines there projection positions each frame. I zoomed in just to show that around the current time it looked stable.

Are you getting 60 FPS? With ~4000 sats updated per frame I'm getting 19-20ms per frame. With the single sat its 0.95ms and still seeing the error.

Do I "have" to update every frame, probably not, but more of my concern is the very different answers I'm getting between smaller changes in date inputs.

Sorry that is more questions than answers. My project works very similarly (3D with orbit lines and mesh objects) and I don't have this issue Are you updating every frame? If not the issue will be hard to see. But then again I could be doing something very wrong. Thanks again for your time.

thkruz commented 3 years ago

If you are using ECI as your reference frame, then one thing to consider is that although the earth is rotating and the position of the satellite relative to a point on the ground will continue to change throughout the day -- the satellites position in ECI is locked on a racetrack going through the same points again and again. I would recommend calculating the positions for the VBO at initialization because they won't change unless the TLE is modified. That will help with the framerate, but doesn't explain the jitter.

That's my fault I should have been clear on what I was doing. I take a SatRec for the selected satellite and just run it through the propagate at a delta in the future, nothing else changed. Then with those points update a VBO's content.

If the first screenshot is a single satellite then something else is a miss. The x,y,z (ECI Reference Frame) output from propagate (which just calls sgp4) should form a perfect circle. Even if the VBO is updated with different points, they should all be along the same circle.

Running this on a single object gives me

Awesome! So we know something that feeds into points[points.length - 1].distanceTo(points[0]) is where the issue lies versus any kind of rendering code after that.

Can you try running this inside the loop? It will clarify if the millisecond issue is the problem. We should see some variation between the time you are inputing and the time being used for jd if it is rounding off milliseconds:

console.log(`t: ${t} - t2: ${info.jdsatepoch});

I think you can sidestep the problem by doing something like this (what I mentioned at the top) and you shouldn't need to do it every frame:

const NUM_SEGS = 255;
const period = (2 * Math.PI) / satRec.no; // convert rads/min to min
const timeslice = period / NUM_SEGS;
let i = 0;
while (i < len) {
    let t = now + i * timeslice;
    let info = satellite.sgp4(satRec, t);
    const pos = info.position as EciVec3<number>
    p.set(pos.x, pos.y, pos.z).multiplyScalar(KM_NORMALIZED)
    points.push(p)
    i++;
}

Are you updating every frame? If not the issue will be hard to see. But then again I could be doing something very wrong. Thanks again for your time.

I update on 1 second intervals and then interpolate position in between those updates for my satellites - may be why I have never noticed the millisecond issue. I have never seen it with the orbits because I don't update those unless I change the TLEs.

delaneyj commented 3 years ago

I might be missing something be info.jdsatepoch is set by the TLE info. It is static in my console.logs per frame which makes sense.

let info = satellite.sgp4(satRec, t); returns info with [false,false] contents. IIRC the sgp4 needs sinceEpochMinutes. Changing to const sinceEpochMinutes = t / 1000 / 60 - satRec.jdsatepoch; const info = sgp4(satRec, sinceEpochMinutes) still gives [false,false].

Calling per second and interpolating is an optimization. My concern is if the calculations have that much error its a deeper problem in the lib. Do you agree if you can T0, T1,T2,T3 you should get roughly the sames and adding a few milliseconds. Right now that appears to be off by 100s of km, not meters. Given JS uses doubles I wouldn't expect this much error to be induced.

Thanks again for your input @thkruz , I did try your interval and the same issue is occuring. Depending on your starting frame and delta you get radically different results.

thkruz commented 3 years ago

Nope that was my mistake - I was thinking about satrec.t (which is what is input into sgp4 after you call propagate). The #75 issue is in invjday which you have to call manually. So confirming the milliseconds may not matter.

I copied that chunk of code from my library and forget the initialization for now. You need const now = Date.now() towards the top of that code. You are getting false because now is undefined and that is messing everything up.

I agree with your point that adding a few milliseconds should only cause a small adjustment (~7km/s for most LEO satellites in your picture).

One other idea we can try to narrow down the problem. Use const now = new Date(2021,1,10,0,0,0) * 1 in my above example and add a `console.log(pos) inside the loop. If you post the results of that console.log and the TLEs from the satellite you chose as your example I should be able to put that same TLE and date in on my end and get the same position values. If I am getting different results that would narrow down the issue. If we are getting the exact same results then I can rule out most of satellite.js.

delaneyj commented 3 years ago

So I built a whole new app just to test satellite.js in isolation. TL;DR... I get no significant distance errors. My current theory is the translation from world space to normalized coordinates (KM_NORMALIZED for example) is the heart of the issue and with vast scale changes causing the root issues. So I'll need to do all the math in ECEF and at the very end plot back down. or move scene to use global scale and deal with the depth buffer / z-fighting

Thanks again @thkruz for your help