NovAtel GNSS受信機のCRCコード
はじめに
私は、測位衛星の信号観測に、NovAtel(ノバテル)の受信機OEM729を利用しています。この受信機は、イーサネットインターフェースを備え、測位結果だけなでなく、生データ(衛星が放送するそのままのデータ)もTCP/IPにて出力できます。生データを読み出すときに必要な、CRC(cyclic redundancy check)による誤り検出のためのC++とPythonのコードを考えてみました。
C++版CRC32コード
NovAtelのPDF DocumentページのOEM7 Receiver Cardsをクリックすると、Commands and Logs Reference Manualを入手できます。C++で書かれたサンプルコードとテストデータは、このマニュアルにあります。
ここで用いられるCRC32は、一般的なもののようです。サンプルコードがあるので油断していました。しかし、このコードは、動作するものの、難読化されているようです。CRCには、いろいろな表現方法や初期値設定があるようで、Python標準モジュールbinascii
のCRC32コードや一般的なCRCコードでは、このテストデータを処理できませんでした。ネット検索してみると、みなさん苦労しているようです。
そこで、このCommands and Logs Reference ManualにあるC++コードの変数名やキャストを整理して、簡潔に書き直しました。
novcrc.cc
#include <stdio.h>
#include <stdint.h>
uint32_t crc32(uint_fast16_t data_len, uint8_t *data) {
const uint_fast32_t CRC32_POLYNOMIAL = 0xEDB88320L;
uint_fast32_t crc = 0;
for (uint_fast16_t i = 0; i < data_len; i++) {
uint_fast32_t tmp2 = (crc ^ data[i]) & 0xFF;
for (uint_fast8_t j = 0 ; j < 8; j++) {
if (tmp2 & 1)
tmp2 = (tmp2 >> 1) ^ CRC32_POLYNOMIAL;
else
tmp2 >>= 1;
}
uint_fast32_t tmp1 = (crc >> 8) & 0x00FFFFFFL;
crc = tmp1 ^ tmp2;
}
return crc;
}
int main() {
uint8_t data[] = {
0xAA, 0x44, 0x12, 0x1C, 0x2A, 0x00, 0x02, 0x20, 0x48, 0x00, 0x00, 0x00, 0x90, 0xB4, 0x93, 0x05, 0xB0, 0xAB, 0xB9, 0x12, 0x00, 0x00, 0x00, 0x00, 0x45, 0x61, 0xBC, 0x0A, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x1B, 0x04, 0x50, 0xB3, 0xF2, 0x8E, 0x49, 0x40, 0x16, 0xFA, 0x6B, 0xBE, 0x7C, 0x82, 0x5C, 0xC0, 0x00, 0x60, 0x76, 0x9F, 0x44, 0x9F, 0x90, 0x40, 0xA6, 0x2A, 0x82, 0xC1, 0x3D, 0x00, 0x00, 0x00, 0x12, 0x5A, 0xCB, 0x3F, 0xCD, 0x9E, 0x98, 0x3F, 0xDB, 0x66, 0x40, 0x40, 0x00, 0x30, 0x30, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0B, 0x0B, 0x00, 0x00, 0x00, 0x06, 0x00, 0x03
};
uint32_t CRCle = crc32(sizeof(data), data);
uint32_t CRCbe = __builtin_bswap32(CRCle);
printf("little-endian %x\n", CRCle);
printf("big-endian %x\n", CRCbe);
printf("Expected: 42dc4c48\n");
}
このコードをc++ -o novcrc novcrc.cc
でコンパイルして、./novcrc
にて実行します。
little-endian 484cdc42
big-endian 42dc4c48
Expected: 42dc4c48
このコードならば、簡単にPythonコードに変換できます。
Python版CRC32コード
上述のC++コードをPythonコードに書き直しました。
novcrc.py
#! /usr/bin/env python
data=b'\xaa\x44\x12\x1c\x2a\x00\x02\x20\x48\x00\x00\x00\x90\xb4\x93\x05\xb0\xab\xb9\x12\x00\x00\x00\x00\x45\x61\xbc\x0a\x00\x00\x00\x00\x10\x00\x00\x00\x1b\x04\x50\xb3\xf2\x8e\x49\x40\x16\xfa\x6b\xbe\x7c\x82\x5c\xc0\x00\x60\x76\x9f\x44\x9f\x90\x40\xa6\x2a\x82\xc1\x3d\x00\x00\x00\x12\x5a\xcb\x3f\xcd\x9e\x98\x3f\xdb\x66\x40\x40\x00\x30\x30\x30\x00\x00\x00\x00\x00\x00\x00\x00\x0b\x0b\x00\x00\x00\x06\x00\x03'
def crc32(data):
polynomial = 0xedb88320
crc = 0
for byte in data:
tmp2 = (crc ^ byte) & 0xff
for _ in range(8):
if tmp2 & 1:
tmp2 = (tmp2 >> 1) ^ polynomial
else:
tmp2 = tmp2 >> 1
tmp1 = (crc >> 8) & 0x00ffffff
crc = tmp1 ^ tmp2
return crc.to_bytes(4,'little')
print(crc32(data).hex()) # expects 42dc4c48
このコードを実行すると42dc4c48
と表示されます。QZS L6 Toolのnov2has.pyに反映させておきました。