Декодирование телеметрии Aistechsat-3

Вступление

Наступило лето, а это значит начался отпускной сезон. Самое время немного расслабиться и добавить декодирование телеметрии для новых спутников. С января Daniel добавил десяток новых спутников. Я начал с Aistechsat-3.

Декодирование

Как и в других случаях я взял готовый демодулятор и реализовал его на Java. Daniel написал, что формат телеметрии задокументирован, но он не успел задекодировать телеметрию.

Казалось бы, что может пойти не так? Документация есть, данные есть. Надо просто написать код.

На практике оказалось всё не так просто.

Каждый пакет начинается с CSP заголовка. Тут всё понятно. Это не первый спутник, который использует этот протокол. Даже в таком урезанном виде как здесь.

Далее идёт небольшой заголовок и данные.

Вот тут прекрасно всё. Во-первых, A3200 manual - это платная документация. Та ссылка, которая есть в спецификации ведёт на описание A3200 в интернет магазине. Во-вторых, непонятно сколько всего дата элементов может быть. Вот пример описания одного из типов радиомаяка:

Как видно, никакого разделения на дата элементы тут нет. Изначально я сделал чтение дата элемента после каждой строчки. Это логично, так как каждая строчка имеет номер “Elements”, который хоть как то похож на “Data element”. Получилось что-то вроде следующего:

DataFieldMeta
extmag
DataFieldMeta
gyro
DataFieldMeta
torquer_duty
...

Не сработало. Количество байт в сообщении значительно меньше.

Тогда я начал смотреть непосредственно в байты. Каждый радиомаяк всегда начинается на метадата. Например, после отбрасывания всех заголовков:

2E1E5CA265820004C1150000432DB7B0C28CD4DE43CEEF8642F8DACDC281BDAA43B15FB900000000433600000000000043A8000000000000000000000706070600000706070607063F54A3DBDC3F094D823D9929D0BA8347B838A85029B9C6108FDBDC08CF360000000000000000000000000000000000800000008000000080000000AA925CA265820004000000000002003A003A3D8DFC993D956E1DBCA30789BC5E8BBB3C9D734B01020201010000B40851B8C0

Метадата занимает 8 байт. Последний из них - это некий Source. Если предположить, что он всегда одинаковый в рамках одного маяка, то можно попытаться найти его и таким образом найти все места, где должна быть метадата.

В данном примере можно поискать 20004. Эти байты включают в себя секунды из временной метки. Получается ещё одно место, где записана метадата: AA925CA265820004. Осталось отсчитать количество байт от первой метки и вставить чтение метадаты. И как раз для типа 20 у меня не получился этот трюк. Если отсчитать с конца количество байт и вставить чтение метадаты, то она будет неправильной.

{
	"checksum":		0,	
	"source":		25986,	
	"timestamp":	2861718690	
}

Это значит только одно: количество байт между первой метадатой и второй неправильное. Я остановился в дебаге посмотреть, что же получается. suns_temp хоть и выглядит странно, но вполне логично: [1798, 1798, 0, 1798, 1798, 1798]. А вот уже следующее поле gyro выглядит неправильно: [0.83062524, -2.15087787E17, -1.3929482E-37]. Как правило, данные со спутника вряд ли могут содержать E17 и E-37. Я ещё раз проверил спецификацию, но не нашёл расхождения. Видимо в спецификации есть ошибки. А пересчитав количество байт между первой и второй метадатой я нашёл расхождение в 2 байта с тем, что указано в спецификации. Если добавить dis.skip(2);, то метадата отлично считывается и дальнейшие данные выглядят логично.

К счастью, это был единственный тип радиомаяка с ошибками в документации. Остальные мне удалось достаточно быстро декодировать.

Я использовал открытые данные Satnogs, чтобы найти всевозможные типы маяков. И не нашёл. Например, в спецификации сказано, что тип 30 будет отправляться каждые 60 секунд. По факту же, я ни разу его не встретил.

Зато я встретил множество недокументированных типов сообщений. 0x40:

0180D78301400100012E1E5D1057690004000000000000000000000000000000000000000000000000000000000000412A6717

Или 0x3f:

0180D783013F0100013B1A5D10576900044142C2115A2205B0CA63E6B5CA8989494A5B046B44CC6065456BB17045C8C1370000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000CFBA95A751FB7C4A515A09C946C9497046C99111439D28A59B855D10576900043F42686A3EDC03153EE9BC70BE32157BBF202419BF286A303EA4DAEE3E8997F4BD4F205B3B4880E83AB47643C26D172442C80000B8F27416D7BBC179

Получается, спецификация не содержит все типы сообщений.

Ещё одна интересная вещь, которая меня заинтересовала - размер сообщения. После того, как я полностью прочитываю из DataInputStream данные, там всё ещё остаётся 4 байта. Это очень похоже на CRC32. Обычно контрольная сумма берётся от всех данных, поэтому я написал следующий код:

long expectedCrc32 = Crc32c.calculate(data, 0, data.length - 4);
long actualCrc32 = ((data[data.length - 4] & 0xFFL) << 24) | ((data[data.length - 3] & 0xFFL) << 16) | ((data[data.length - 2] & 0xFFL) << 8) | (data[data.length - 1] & 0xFFL);

Контрольная сумма при этом не совпадала. Я решил попробовать взять контрольную сумму от данных без учёта CSP заголовка:

long expectedCrc32 = Crc32c.calculate(data, Header.LENGTH, data.length - 4 - Header.LENGTH);
long actualCrc32 = ((data[data.length - 4] & 0xFFL) << 24) | ((data[data.length - 3] & 0xFFL) << 16) | ((data[data.length - 2] & 0xFFL) << 8) | (data[data.length - 1] & 0xFFL);

И это на удивление сработало. Опять же, в документации ни слова про контрольную сумму. А ведь это очень мощный инструмент определения корректности данных.

Выводы

Мне удалось декодировать большинство радиомаяков, хотя и с некоторыми трудностями. В целом это было очень увлекательно и чем-то похоже на детективную историю с поиском улик.

С другой стороны, документация от Aistech вызывает больше вопросов, чем ответов. Из-за того, что они используют радиолюбительский диапазон, им необходимо раскрыть протоколы. Однако, из-за того, что это коммерческая компания, им необходимо скрыть свои эксперименты на орбите и технологию работы спутников. Получается такая двоякая непростая ситуация для компании. Мне же кажется, что доступ к нелицензируемым радиочастотам сильно ускорит разработку коммерческих спутников и в целом положительно скажется на освоении космоса. Главное, чтобы этим не злоупотребляли. А вот тут сложно.