¿Cómo calcular el ángulo de cono entre dos satélites dados sus ángulos de observación?

Tengo el acimut, la elevación y la distancia de 2 satélites en relación con una estación terrestre, definidos por latitud y longitud, como discutí en mi pregunta anterior . Estoy usando los TLE de los satélites y el método Skyfield del paquete de Python para.altaz() obtener su alt/az/el.

¿Cómo puedo calcular el ángulo de cono entre 2 satélites en relación con la estación terrestre mencionada?

ACTUALIZAR

Como respondió Brandon Rhodes aquí , no es necesario usar ángulos de mirada. Skyfield puede calcular el ángulo de separación a partir de las posiciones.

Así que resulta que este es un problema XY . La pregunta dice "... ¿ dados sus ángulos de mirada? ", pero la respuesta aceptada y mejor no usa ángulos de mirada en absoluto. Le recomiendo que elimine los "ángulos de mirada" de su título y, en su lugar, agregue "usar Skyfield", ya que su respuesta aceptada solo funciona con Skyfield y no ayuda de ninguna manera si está comenzando con ángulos de mirada.
@uhoh Tienes razón. Me quedaría con el título de la pregunta y aceptaría su respuesta. Sin embargo, editó la pregunta y mencionó la respuesta de Brandon.
Es tu elección. La mejor guía es dejar una pregunta y sus respuestas en tal estado que una persona que busca su pregunta encuentre las respuestas útiles y útiles, y así todo está bien; Las tres respuestas están aquí. Si elimina "ángulos de mirada" y agrega "Skyfield" al título (que refleja mejor lo que está haciendo y quería saber cómo hacerlo), esta pregunta será más específica. Eso es lo que recomendaría. Pero no es un gran problema de cualquier manera. El título podría ser algo así como " Ángulo de cono entre satélites con Skyfield; ¿necesito los ángulos de observación o hay una forma mejor? "

Respuestas (3)

nota: Esta respuesta aborda la pregunta directamente:

¿Cómo calcular el ángulo de cono entre dos satélites dados sus ángulos de observación?

Si necesita usar los ángulos de mirada, esta es una buena manera de hacerlo. Esta mejor respuesta le explica al OP que si está usando Skyfield, no debe usar los ángulos de mirada, sino usar las coordenadas en su forma original.

El coseno del ángulo entre dos vectores viene dado por el producto escalar de sus normas.

porque ( θ 12 ) = v ^ 1 v ^ 2 = v 1 v 2 | v 1 |   | v 2 | = v 1 v 2 v 1   v 2

θ 12 = porque 1 ( v 1 v 2 v 1   v 2 )

Pero si bajas la distancia y solo usas a z , mi yo obtienes vectores normalizados automáticamente:

v ^ i = porque ( mi yo i ) ( porque ( a z i ) X ^ + pecado ( a z i ) y ^ ) + pecado ( mi yo i ) z ^

¡Creo que esto no es diferente de lo que @AdamTrhon ya ha descrito en esta respuesta ! Puede usar la trigonometría esférica y posiblemente la ley de los cosenos, pero a veces esas expresiones conducen a errores de cálculo debido a cosas como la resta de números casi iguales y la división por casi cero, mientras que de esta manera, trabajando en cartesiano tanto como sea posible, en al menos de la manera que se muestra aquí, no hay posibilidades de que eso suceda.

En Python sería algo como:

def nvec(elaz):
    (cel, sel), (caz, saz) = [[f(q) for f in (np.cos, np.sin)] for q in elaz]   # parentheses for Py3
    return np.array([cel*caz, cel*saz, sel])

def angle(elaz1, elaz2):
    v1, v2 = [nvec(elaz) for elaz in (elaz1, elaz2)]  # parentheses for Py3
    return np.arccos((v1*v2).sum(axis=0))

Entonces, si descarga TLE para tres satélites TDRS más la ISS y calcula el ángulo del cono entre los pares de TDRS, y entre la ISS y cada TDRS, obtendrá algo como lo que se muestra a continuación.

Los objetos también tienen almacenadas sus posiciones geocéntricas, por lo que puede hacer un mapa 3D como se muestra en este ejemplo .


EDITAR: lo he usado WhiteSands.at(times).observe(sat.ICRF).apparent().altaz()y esta sería la forma recomendada de obtener la posición óptica aparente. El .apparent()método incluye una variedad de efectos, que incluyen el retraso del tiempo de luz, la aberración astronómica e incluso... espera... efectos gravitatorios de cuerpos masivos que podrían desviar el camino, así como la refracción atmosférica. Puede leer más sobre esto en la documentación en http://rhodesmill.org/skyfield/api-position.html#skyfield.positionlib.Astrometric.apparent


ingrese la descripción de la imagen aquí

TLEs = """TDRS 5                  
1 21639U 91054B   18086.36437858  .00000071  00000-0  00000-0 0  9995
2 21639  14.5306  18.4626 0026343 345.4651 144.6288  1.00281508 97593
TDRS 10                 
1 27566U 02055A   18087.12756861  .00000056  00000-0  00000+0 0  9998
2 27566   5.5204  57.1630 0011308 250.2541 109.7547  1.00278469 56111
TDRS 11                 
1 39070U 13004A   18086.87347718  .00000063  00000-0  00000-0 0  9994
2 39070   5.0128 328.6219 0008993 321.0893  38.7221  1.00272889 16583
ISS (ZARYA)             
1 25544U 98067A   18088.22902370  .00003740  00000-0  63642-4 0  9999
2 25544  51.6415  57.3234 0001506 271.5382 195.6957 15.54152785106088"""

lines           = TLEs.splitlines()
names, L1s, L2s = [[x.strip() for x in lines[i::3]] for i in range(3)]
triplets        = zip(names, L1s, L2s)

class Sat(object):
    def __init__(self, name):
        self.name = name

def nvec(elaz):
    (cel, sel), (caz, saz) = [[f(q) for f in (np.cos, np.sin)] for q in elaz]   # parentheses for Py3
    return np.array([cel*caz, cel*saz, sel])

def angle(elaz1, elaz2):
    v1, v2 = [nvec(elaz) for elaz in (elaz1, elaz2)]  # parentheses for Py3
    return np.arccos((v1*v2).sum(axis=0))

import numpy as np
import matplotlib.pyplot as plt
from skyfield.api import Loader, Topos, EarthSatellite
import itertools

halfpi, pi, twopi = [f*np.pi for f in (0.5, 1, 2)]  # parentheses for Py3
degs, rads = 180/pi, pi/180

load = Loader('~/Documents/fishing/SkyData')  # avoids multiple copies of large files
ts   = load.timescale()

data    = load('de421.bsp')
earth   = data['earth']
ts      = load.timescale()

WhiteSands  = earth + Topos(latitude_degrees   =   32.4,
                             longitude_degrees = -106.5,
                             elevation_m       = 1300.0  )

minutes = np.arange(0, 1441, 1)
times   = ts.utc(2018, 3, 29, 0, minutes)

sats = []
for name, L1, L2 in triplets:
    sat = Sat(name)
    sats.append(sat)
    sat.Geo  = EarthSatellite(L1, L2)
    sat.ICRF = earth + EarthSatellite(L1, L2)
    sat.obs  = WhiteSands.at(times).observe(sat.ICRF)
    sat.elaz = [x.radians for x in sat.obs.apparent().altaz()[:2]]
    sat.below = sat.elaz[0] <= 0.
    sat.pos   = sat.Geo.at(times).position.km

ISS   = [sat for sat in sats if 'ISS' in sat.name][0]
TDRSs = [sat for sat in sats if 'TDRS' in sat.name]
TDRSpairs = list(itertools.combinations(TDRSs, 2))

intra_TDRS_cones = []
for pair in TDRSpairs:
    name = ''.join([x.name + ' ' for x in pair])[:-1]
    elaz1, elaz2 = [s.elaz for s in pair]
    cone = angle(elaz1, elaz2)
    intra_TDRS_cones.append((name, cone))

ISS_TDRS_cones = []
for TDRS in TDRSs:
    name = ISS.name + ' ' + TDRS.name
    cone = angle(ISS.elaz, TDRS.elaz)
    ISS_TDRS_cones.append((name, cone))

if True:
    plt.figure()
    plt.subplot(2, 1, 1)
    for name, cone in intra_TDRS_cones:
        plt.plot(minutes/60., degs*cone)
    plt.title("intra-TDRS cone angles", fontsize=16)
    plt.xlim(0, 24)
    plt.subplot(2, 1, 2)
    for name, cone in ISS_TDRS_cones:
        plt.plot(minutes/60., degs*cone)
    plt.title("ISS-TDRS cone angles", fontsize=16)
    plt.xlim(0, 24)
    plt.show()
Para encontrar el ángulo de elevación relativo a la estación terrestre, solo usé sat_i = EarthSatellite(L1, L2) ground_i = Topos(g_i[0],g_i[1]) diff_i = sat_i - ground_i. Y elevation=diff_i.at(time).altaz()[0].degreesentonces
En rhodesmill.org/skyfield/earth-satellites.html se dice que observer()es inútil y demasiado caro para los satélites terrestres.
¿Y por qué lo usaste elevation_m = 1300.0como estación terrestre?
"¿Y por qué lo usaste elevation_m = 1300.0como estación terrestre?" Porque la elevación de una estación terrestre en White Sands sería de unos 1300 metros.
Compartí mi solución para verificar si es correcta en comparación con la tuya, tu respuesta resuelve el problema. Acerca de la estación terrestre: ¿es la elevación sobre el nivel del mar? Olvidé considerar eso. Curiosamente, ¿cuánto cambiaría la solución?
@Leeloo Es una elevación sobre el geoide. Es un poco complicado, pero puede pensar en él como aproximadamente "nivel del mar", aunque no creo que nadie use el nivel del mar en ciencia, ingeniería, cartografía, navegación, etc. Todos usan coordenadas GPS y se refieren a WGS84 . Si quieres saber "¿Qué pasó con el nivel del mar?" puede hacerla como una nueva pregunta, ¡y creo que habrá algunas buenas respuestas!

Aquí está el enfoque manual:

  1. Configurar el sistema de coordenadas ortogonales:
    1. La unidad es 1Km (pero no importa mucho).
    2. El origen está en la estación terrestre.
    3. el eje x apunta a 0° de acimut, el eje y a 90°, el eje z hacia arriba.
    4. Hecho: el plano xy es el plano tangente de la Tierra.
  2. Calcule los vectores unitarios apuntando de cada satélite. Cuidado con los radianes/grados.
    1. La coordenada x del vector unitario es el coseno del acimut.
    2. La coordenada y del vector unitario es el seno del acimut.
    3. Hecho: El vector ahora apunta al satélite, pero solo en el plano xy.
    4. La coordenada z del vector es tangente de elevación.
    5. Realidad: Ahora el vector apunta al satélite en el espacio 3D, pero ya no es un vector unitario.
    6. Normalícelo: calcule su longitud y divida cada coordenada por esta longitud.
  3. Calcule el ángulo del cono:
    1. Calcular el producto escalar de los vectores unitarios
    2. Arccos del producto escalar es el ángulo del cono.
¡Gracias! ¿Podría agregar algunos datos de prueba de una fuente independiente, para poder verificar?
¡Esto sí que me parece bien!
...excepto los puntos 2.2 y 2.3. Esos también deben multiplicarse por el coseno de la elevación además de lo que ya se muestra, como se muestra aquí .
@uhoh Gracias por la revisión, de hecho hubo un error. Ahora debería estar bien.

Calcular el ángulo entre dos vectores será difícil si primero transformas sus coordenadas x, y y z en ángulos, porque luego tendrás que sumergirte en las fórmulas de la trigonometría esférica. Skyfield considera de forma nativa que todas las posiciones son vectores x, y, z y, a menudo, es más fácil de calcular si los deja como objetos de "posición" hasta que esté listo para mostrar los resultados.

Si calcula las posiciones de dos satélites en relación con un observador de la Tierra, puede obtener el ángulo entre los satélites utilizando el .separation_from()método de Skyfield que llevan los objetos de posición:

from skyfield import api

# Time.
ts = api.load.timescale()
t = ts.utc(2018, 3, 30, 23, 8)

# Satellites.
sats = api.load.tle('https://celestrak.com/NORAD/elements/stations.txt')
s1 = sats['ISS']
s2 = sats['ASTERIA']

# Observe the satellites from a position on the Earth's surface.
usno = api.Topos('38.9215 N', '77.0669 W', elevation_m=92.0)
pos1 = (s1 - usno).at(t)
pos2 = (s2 - usno).at(t)

# How far apart are the satellites in the sky?
print(pos1.separation_from(pos2))
¡Gran! ¿Qué pasa con el ángulo entre 2 satélites desde el centro de la Tierra?
¿Qué pasa con el ángulo entre s1y usnodesde el centro de la Tierra?
Esta es, por supuesto, una mejor manera de hacerlo usando Skyfield. Respondí la pregunta del OP como "¿... dados sus ángulos de mirada? " sin usar trigonometría esférica aquí . ¿Estaría bien esto bajo las restricciones (innecesarias) de la pregunta?
@Leeloo Lo harías pos1 = s1.at(t)y pos2 = s2.at(t)para los vectores a los satélites desde el centro de la Tierra. El valor usno.at(t)es, de manera similar, el vector desde el centro de la Tierra hasta el USNO.
@BrandonRhodes ¡Gracias! Y pos1=s1.at(t); pos2=(s1-usno).at(t); pos1.separation_from(pos2);da el ángulo entre ground station-satellite vector and satellite-Earth center vector?
Sí, a primera vista parece que ese es el resultado que obtendrá, pero, como siempre, elija un conjunto de circunstancias para las que sepa la respuesta correcta y confirme que el código produce un valor razonable. :)