Con una cuenta desbloqueas el cuestionario, el foro de dudas y el tutor con IA de esta lección, además del registro de progreso y el certificado verificable al terminar «Python para Ingeniería Espacial».
El TLE (two-line element set, conjunto de dos líneas de elementos) es el formato en que el catálogo público de objetos en órbita publica el estado orbital de cada satélite. Es el dato de entrada de casi cualquier herramienta del segmento terreno: si quieres saber cuándo pasa la ISS sobre tu antena, lo primero que necesitas es su TLE.
Su diseño es de los años sesenta y se nota: son exactamente 69 caracteres por línea, y cada campo ocupa columnas fijas. No hay comas, no hay etiquetas, no hay JSON. Un número que ocupa menos espacio del reservado se rellena con espacios a la izquierda; el punto decimal de la excentricidad no se escribe porque siempre está en la misma posición. Es un formato pensado para tarjetas perforadas de 80 columnas, y sobrevive porque medio mundo escribió software contra él.
Ese pasado es exactamente lo que lo convierte en el mejor ejercicio posible de manejo de texto: te obliga a usar las dos herramientas fundamentales de Python para cadenas, y a distinguir cuándo sirve cada una.
Referencia cruzada. El curso Mecánica Orbital explica qué significa cada campo del TLE: por qué la inclinación de la ISS es 51.64°, qué implica una excentricidad de 0.00067 o cómo se propaga el estado con SGP4. Aquí aprendemos algo distinto y previo: cómo extraerlo de forma fiable de una cadena de texto.
El TLE de la ISS, línea a línea
Este es un TLE de tres líneas (el nombre más las dos líneas canónicas) de la Estación Espacial Internacional:
La primera línea es el nombre común, y es opcional. La línea 1 lleva identificación, época y coeficientes de arrastre. La línea 2 lleva los elementos orbitales propiamente dichos, que son los que nos interesan hoy.
Fíjate en el final de la línea 2: 15.50377579478209. Ahí hay tres campos pegados sin separador: el mean motion 15.50377579, el número de revolución 47820 y el dígito de control 9. Ese detalle es el que hace imposible parsear un TLE con split(), y es la razón de ser de esta lección.
Las dos herramientas: split() y el rebanado por columnas
split(): cuando hay un separador
texto.split(",") parte una cadena por un separador y devuelve una lista. Sin argumento, texto.split() parte por cualquier bloque de espacios en blanco y descarta los vacíos, lo que la hace cómoda para texto separado por espacios de ancho variable.
strip() es su compañera inseparable: elimina espacios (y saltos de línea) al principio y al final. " 51.6416 ".strip() da "51.6416". Sus variantes lstrip() y rstrip() recortan solo por un lado, y rstrip() en particular es la forma correcta de quitar el \n que arrastra cada línea leída de un archivo.
Por qué split() fracasa con el TLE
Aplica split() a la línea 2 y cuenta lo que sale: obtendrías ["2", "25544", "51.6416", "247.4627", "0006703", "130.5360", "325.0288", "15.50377579478209"], ocho elementos. El último es un amasijo de tres campos distintos. Y si un satélite tuviera un número de revolución de cuatro dígitos en lugar de cinco, la posición de las cosas cambiaría. Un formato de columnas fijas se lee por columnas, no por separadores.
El rebanado [inicio:fin]
Python indexa desde cero y el rebanado s[i:j] toma desde el índice iinclusive hasta el jexclusive. Esa asimetría tiene una consecuencia práctica muy cómoda: la longitud del trozo es siempre j - i.
La documentación del formato TLE numera las columnas desde uno. La traducción es mecánica: una columna documentada como c es el índice c−1 en Python, así que el rango de columnas [c1,c2] se rebana como s[c1 - 1 : c2]. La inclinación vive en las columnas 9 a 16, luego es linea2[8:16].
Mapa de columnas de la línea 2
Campo
Columnas (1-indexadas)
Rebanado en Python
Valor en la ISS
Número de línea
1
linea2[0:1]
2
Número de catálogo
3–7
linea2[2:7]
25544
Inclinación (grados)
9–16
linea2[8:16]
51.6416
Ascensión recta del nodo (grados)
18–25
linea2[17:25]
247.4627
Excentricidad (punto implícito)
27–33
linea2[26:33]
0006703
Argumento del perigeo (grados)
35–42
linea2[34:42]
130.5360
Anomalía media (grados)
44–51
linea2[43:51]
325.0288
Mean motion (rev/día)
53–63
linea2[52:63]
15.50377579
Número de revolución
64–68
linea2[63:68]
47820
Dígito de control
69
linea2[68:69]
9
Ejemplo resuelto: extraer, limpiar y convertir
Extraer da siempre una cadena, nunca un número. El pipeline correcto tiene tres pasos y ninguno es opcional:
Rebanar por columnas: bruto = linea2[8:16] produce " 51.6416", con el espacio de relleno incluido.
Limpiar con strip(): " 51.6416".strip() produce "51.6416".
Convertir con float() o int(): float("51.6416") produce 51.6416.
En la práctica float() ya tolera los espacios sobrantes, así que float(linea2[8:16]) funciona directo; pero int() es igual de tolerante y strip() sigue siendo obligatorio cuando el campo se usa como texto (comparar, concatenar o imprimir alineado). Escribirlo siempre cuesta nada y evita una clase entera de sorpresas.
La excentricidad es el caso especial del formato: se guarda como 0006703sin punto decimal, porque para un satélite en órbita terrestre siempre vale entre 0 y 1. Se reconstruye concatenando el prefijo: float("0." + linea2[26:33]) da 0.0006703. Casi órbita circular, como corresponde a la ISS.
De mean motion a periodo
El mean motionn es el número de revoluciones que el satélite completa en un día. Como un día tiene 1440 minutos, el periodo sale de una división:
T[min]=n[rev/dıˊa]1440
Con el valor del TLE de arriba, n=15.50377579:
T=15.503775791440=92.88min
Compara ese resultado con el de la lección 6: allí calculamos 92.82 min desde una altitud supuesta de 420 km, aquí obtenemos 92.88 min desde el TLE real. La diferencia de 0.06 min (unos 3.6 segundos) es informativa, no un error: significa que la altitud media real de la estación en esa época era ligeramente mayor que los 420 km que asumimos. Dos caminos independientes que llegan al mismo número validan ambos; que difieran en un 0.06 % es exactamente lo que se espera de una hipótesis redondeada.
Y un dato operativo que sale gratis: si el satélite da 15.5 vueltas al día, sobrevuela tu región varias veces cada 24 horas, y las ventanas útiles se agrupan en dos tandas separadas por unas doce horas. Ese es el ritmo de trabajo de una estación terrena.
Validar antes de confiar
Un parser de producción comprueba lo que recibe antes de creérselo. Las tres verificaciones baratas y de mayor rendimiento son:
Longitud: cada línea canónica mide 69 caracteres. Si mide 68, alguien recortó espacios finales al copiar y pegar, y todas las columnas siguen bien salvo la última.
Prefijo: la línea 1 empieza por "1 " y la línea 2 por "2 ". Es la comprobación que detecta el error más frecuente de todos, pasar las líneas en orden invertido.
Rangos físicos: la inclinación vive en [0°,180°], la excentricidad en [0,1) y el mean motion de un objeto en órbita baja ronda [11,17] rev/día. Un mean motion de 1.0027 no es un error de parseo: es un satélite geoestacionario.
Existe además el dígito de control de la columna 69: la suma de todos los dígitos de la línea, contando cada signo menos como 1 y todo lo demás como 0, módulo 10. Es un detector de erratas de transcripción, no un mecanismo de seguridad, pero comprobarlo cuesta cuatro líneas y atrapa el clásico dígito cambiado al copiar de un correo.
Escribe el parser completo y compruébalo contra la ISS:
Playground · python
Tres retos sobre ese código. Primero: añade una comprobación de que len(linea2) == 69 y prueba a borrar un carácter del final para ver qué ocurre. Segundo: implementa digito_de_control(linea) con la regla de la suma módulo 10 y verifica que da 9 para la línea 2. Tercero: cambia el mean motion a 01.00270000 (columnas 53 a 63, con el relleno correcto) y comprueba que el periodo sale de ~1436 minutos: acabas de convertir la ISS en un satélite geoestacionario editando once caracteres.
1 de 7
Comprueba el punto que distingue un parser correcto de uno frágil:
¿Por qué no se puede parsear la línea 2 de un TLE con linea2.split()?
Ya sabes extraer datos reales. En la lección 8 nos ocuparemos de lo que ocurre cuando esos datos llegan rotos — que es siempre.