martes, 23 de mayo de 2017

ISIS cubesat Telemetry (Part 2)

Introduccion:

En la parte uno de esta entrada ISIS Cubesat Telemetry (Part 1) explicamos brevemente algunas particularidades de la telemetría en el área satelital. Encontramos un XML que describía parcialmente los paquetes que llegan del satelite por TCP/IP y decimos parcialmente porque en la practica si intentamos desempaquetar la trama utilizando unicamente el XML el proceso falla.
La razón es que ese XML describe el paquete (packet) de una trama mayor que viene dada de la siguiente manera.


En las siguientes lineas vamos a desarmar completamente la trama hasta llegar a las variables de telemetria.
Primero establecemos hasta donde nos es posible posición y tamaño de las cosas. Como se observa en la tabla 7.1 existen campos de tamaño variable (Ejemplo: modulation name con tamaño indicado por modulation name length) que impiden presetear todo de antemano.

PosFrameCommand         = 0

LenFrameCommand         = 1

   

PosFrameLen             = PosFrameCommand+LenFrameCommand

LenFrameLen             = 4

   

PosDataRate             = PosFrameLen+LenFrameLen

LenDataRate             = 4

   

PosModuluationNameLen   = PosDataRate+LenDataRate

LenModuluationNameLen   = 1

   

PosModulationName       = PosModuluationNameLen+LenModuluationNameLen

LenModulationName       = 0

   

PosRSSI                 = PosModulationName+LenModulationName

LenRSSI                 = 8

   

PosFrequency            = PosRSSI+LenRSSI

LenFrequency            = 8

   

PosPktLen               = PosFrequency+LenFrequency

LenPktLen               = 2


Luego en un bucle infinito desarmamos los paquetes que van llegando hasta llegar al payload que es lo que nos interesa. Esta variable es la que en futuras entradas vamos a desarmar en función del xml de configuración. Por lo pronto el siguiente código recibe en un bucle infinito la trama (chunk) del socket y la desarma hasta llegar al payload, el código tiene una breve descripción de lo que hace antes de cada linea y se puede observar que el mismo es no bloqueante porque por el mismo canal debe enviarse los comandos....

s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

try:

    s.connect( (uhfServerIp, int(uhfServerPort)) )

    print("Successfully connection to..", uhfServerPort)

        while True:

            try:

                """

                Establezco un timeout para la bajada, con o sin bajada los comandos deben ser enviados

                """

                s.settimeout(5.0)

                   

                """

                Me quedo esperando recibir informacion del socket (IPC)

                """                       

                chunk = s.recv(int(BUFFER_SIZE))

                             

                unconnectionLimit = 0

                               

                """

                Buena o mala la telemetria fue recibida, reseteo el watchdog

                """

                wd.reset()

                               

                """

                Si recibo telemetria, defenitivamente estoy en contacto

                """

                sat.setInContact(True)

                               

                """

                Si la informacion es una trama de bits completa la proceso

                """

                if chunk == b'':

                    raise RuntimeError("socket connection broken")

                else:

                    
                    os.system('cls||clear')

                    print(bcolors.OKGREEN+"--------------------Data Received-------------------"+bcolors.OKGREEN)

                    print("Data length:", len(chunk))

                    #Me guardo el crudo tal cual llego antes de procesarlo, la tabla donde se guarda es UHFRawData


                    data = UHFRawData()

                    data.source = source

                    data.data = chunk

                    data.processed = False

                    data.save()

                                 

                    framecommand = unpack("<B",chunk[PosFrameCommand:PosFrameCommand+LenFrameCommand])[0]

                    frameLength  = unpack("<I",chunk[PosFrameLen:PosFrameLen+LenFrameLen])

                    datarate     = unpack("<I",chunk[PosDataRate:PosDataRate+LenDataRate])

                    modulationnamelen = (unpack("<B",chunk[PosModuluationNameLen:PosModuluationNameLen+LenModuluationNameLen]))[0]

                    modulationname    = chunk[PosModulationName:PosModulationName+modulationnamelen]

                    PosRSSI           = PosModulationName+modulationnamelen

                    rssi              = unpack("<d", chunk[PosRSSI:PosRSSI+LenRSSI])

                    PosFrequency        = PosRSSI+LenRSSI

                    freq                = unpack("<d", chunk[PosFrequency:PosFrequency+LenFrequency])

                    PosPktLen           = PosFrequency+LenFrequency

                    pktLen             = unpack("<H",  chunk[PosPktLen:PosPktLen+LenPktLen])

                    PosUtcTime = PosPktLen+pktLen[0]

                    LenUtcTime = 4

                                   

                    PosPayload = PosPktLen+int(LenPktLen)

                    ax25 = chunk[PosPayload:PosPayload+pktLen[0]]

                                   

                                   

                    destination  = ax25[0:7]

                    asource      = ax25[7:7+7]

                    control      = ax25[7+7:7+7+1]

                    protocol     = ax25[7+7+1:7+7+1+1]

                                   

                           

                                        

                    vardataoffset = 7+7+1+1

                    payload = ax25[ vardataoffset: ]

                                   

                    pn = unpack("<H",  payload[1:3])

                    print("Packet number:", pn)

                                   

                    frameTypeId = payload[0]

                                   

                    dl = DownlinkFrame()

                                   

                    dl.frameCommand     = framecommand

                    dl.frameLength      = frameLength[0]

                    dl.dataRate         = datarate[0]

                    dl.modulationName   = str(modulationname)

                    dl.rssi             = rssi[0]

                    dl.frequency        = freq[0]

                    dl.packetLength     = pktLen[0]

                    dl.satellite        = sat

                    dl.ax25Destination  = "Pending"#destination.decode("utf-8")

                    dl.ax25Source       = "Pending"#asource.decode("utf-8")

                    dl.ax25Protocol     = "Pending"#protocol.decode("utf-8")

                    dl.ax25Control      = "Pending"#control.decode("utf-8")

                    dl.packetNumber     = pn[0]

                    dl.frameTypeId      = frameTypeId

                                                               


            

domingo, 14 de mayo de 2017

ISIS cubesat Telemetry (Part 1)

Introducción

El los últimos meses estuve cursando una maestría en desarrollo software de aplicación espacial. Una de las asignaturas exige algunas practicas mínimas sobre un pequeño satélite. El satélite fue comprado hace algunos años por la agencia espacial donde se desarrollan las practicas, pero nunca había sido utilizado. El problema es que también había caducado el periodo de soporte y no teníamos documentación para nuestro primer objetivo que era procesar la telemetría de este diminuto satélite.

Telemetría vs Telemetría Satelital

Se denomina telemetria a la medición y transmisión remota de magnitudes, físicas o lógicas. ¿Que diferencia hay entre la telemetría de cualquier equipo remoto y de un satélite? Las diferencias son muy pocas pero existen y son generadas por los siguientes problemas:

Eficiencia de la transmisión:

En el area espacial existe un obsesión (Relativamente justificada) por el ahorro de bytes a transferir. Las capacidades y tiempos de transmisión son limitadas. El hardware en los sistemas de vuelo suele ser viejo, muy viejo o con limitaciones en términos de capacidad de computo. Esto se traduce en que pueden no ser aplicables técnicas de compresión o protocolos que utilicen exhaustivamente la lógica para reducir la cantidad de bytes como por ejemplo  Google Protocols Buffers.

Desgaste de componentes:

Los componentes en el espacio sufren un desgaste y ya no es posible remplazarlos. Los valores de telemetria recibidos pueden estar modificados por ese desgaste y no representan el verdadero valor.

Estas cuestiones definen y acotan el margen de acción, en contraste con el mundo informático actual donde nos damos el lujo de transferir por protocolos autocontenidos, de transferir texto, XML etc, acá se opta por trabajar, como antaño, con protocolos binarios  y los datos transferidos no representan el valor final de la variable que se pretende analizar tanto por la necesidad de reducir la cantidad de bytes transferidos al mínimo como la necesidad de ajustar los valores.

RF checkout box

Afortunadamente el kit del satélite disponía de un equipo de radio conectable a un PC por USB y un sencillo software de test que alcanzaba a mostrar algunos valores de telemetria. El manual indicaba que ese software publicaba la trama en un server TCP/IP pero no indicaba el formato completo de la trama. Con estos escasos recursos algunos compañeros comenzaron a decompilar los .class del software y llegaron a la conclusion de que utilizaba un XML para desarmar parte de la trama...buena parte del problema esta resuelto.

De un golpe de vista se entiende el formato de este paquete (o subpaquete). Por ejemplo, el frame type 1 es el de toda la telemetria (allTelemetry), esto viene indicado en un valor char en la posición 0. En la posición 8 tenemos la temperatura 1 de la OBC, viene en un short (2 bytes calculados a prueba y error) y offset y gain indican los valores por los que hay que multiplicar y sumar ese short para transformarlo en el valor real de temperatura Celsius.
Como dijimos RF checkout box publica las tramas por un server TCP/IP puerto default 3210, si el satélite esta conectado y funcionando con conectarse al server se comienzan a recibir las tramas.

 s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

 s.connect( (uhfServerIp, int(uhfServerPort)) )

 ...

 chunk = s.recv(int(BUFFER_SIZE))


Sin embargo estas tramas contienen información de cabecera previa al paquete que describe el XML. Esto lo iremos desarmando en la Parte 2 (Part 2) de esta entrada.

Continua en...Parte 2



miércoles, 10 de mayo de 2017

Misión Cubesat FS2017

La función del software del segmento terreno es la operación y mantenimiento del estado de salud del satélite (bajada y análisis de Telemetría -TM- y subida de Telecomandos -TC) y la bajada, procesamiento y publicación de datos de ciencia. A tal fin se ha estudiado el diseño y desarrollo de un SW multimisión que, ademas de contar con un alto nivel de robustez, posea atributos de generalidad que permitan su aplicación a futuras misiones.  El sistema esta desarrollado sobre una arquitectura cliente-servidor clásica utilizando Python sobre el framework Django, el cual fuerza el uso del paradigma orientado a objetos y un diseño del tipo Model-Driven Architecture (MDA). La aplicación esta hosteada sobre apache en Linux Ubuntu 15.10. El sistema intenta ser una alternativa a otros desarrollos ad-hoc ofreciendo una variante multiplataforma y multimisión que maximiza los atributos de: interoperabilidad, eficiencia, accesibilidad/disponibilidad, integridad, seguridad y recuperación.