这是一篇来自于旧站的重置文章。
第四篇无敌了,1篇人工智能、1篇信息安全、2篇电子工程,我真的无敌了。
前言
加油!明天写!
2022年4月5日 1:45 am
2025年8月20日 0:47 am 开始补全!
这其实是一个很久之前的痛,我直到硕士毕业完成,我都没有弄明白这个 dump1090 是怎么操作的,我看着我的IQ信号不知所措(如果当时有AI、有ChatGPT估计一切都会大不相同。果然AI改变时代啊。) 我甚至发现这个 Github仓库现在有新的版本了,有人在维护:
正文
1. 如何探测 ADS-B 报文?
一个一直围绕着我的问题就是,我拿到的是IQ信号,我要去转换成二进制的01信号,但是问题是,我这一段IQ信号,我怎么知道哪一段是一个完整的ADS-B报文呢?一段IQ信号里面应该有很多才对,但是如何去找开头、如何去找结束呢?
1.1 前导(preamble)检测
这个问题的核心就是 前导检测:靠前导检测来判定一帧的开始。
Mode S/1090ES 的前导是4个脉冲,每个0.5 µs,后3个相对第一个脉冲的时间间隔分别是1.0 µs、3.5 µs、4.5 µs;数据块起点在第一个前导脉冲后8 µs。检测思路就是在幅度序列里找这个时间指纹。
简单判据(幅度/相关法都行):
高:t=0、1.0、3.5、4.5 µs 有脉冲峰;
低:t=0.5、1.5、2.0、2.5、3.0、4.0 µs 等“应该没有脉冲”的位置能量较低;
通过阈值和相对幅度比值过滤伪检出。这一步常用匹配滤波/相关来做更稳。
1.2 位时钟与PPM判决(确定每个比特)
数据块从前导起点+8 µs对齐后,按照1 µs一位划窗;每位内前半0.5 µs与后半0.5 µs做能量比较:
前半有脉冲→比特1;后半有脉冲→比特0。相邻位若出现“后半(0)紧接前半(1)”时,两脉冲会合并成1 µs宽的单脉冲,积分比较同样适用。
1.3 用“长度”确定“结束”
结束信号的核心是 固定长度切断:ADS-B总长112帧(112 µs的数据块),加上8 µs前导检测,总计120 µs,然后这 0-120 µs就是整个报文,然后通过最后的(89-112 PI)做 CRC验证,看看是不是一段合法的ADS-B报文。
2. ADS-B 二进制报文结构化
一旦获取到ADS-B的二进制报文,其实结构化就很简单了
广泛意义上,参考这张表格就可以了:
更具体的 dump1090 里面都有
/* Capability table. */
char *ca_str[8] = {
/* 0 */ "Level 1 (Survillance Only)",
/* 1 */ "Level 2 (DF0,4,5,11)",
/* 2 */ "Level 3 (DF0,4,5,11,20,21)",
/* 3 */ "Level 4 (DF0,4,5,11,20,21,24)",
/* 4 */ "Level 2+3+4 (DF0,4,5,11,20,21,24,code7 - is on ground)",
/* 5 */ "Level 2+3+4 (DF0,4,5,11,20,21,24,code7 - is on airborne)",
/* 6 */ "Level 2+3+4 (DF0,4,5,11,20,21,24,code7)",
/* 7 */ "Level 7 ???"
};
/* Flight status table. */
char *fs_str[8] = {
/* 0 */ "Normal, Airborne",
/* 1 */ "Normal, On the ground",
/* 2 */ "ALERT, Airborne",
/* 3 */ "ALERT, On the ground",
/* 4 */ "ALERT & Special Position Identification. Airborne or Ground",
/* 5 */ "Special Position Identification. Airborne or Ground",
/* 6 */ "Value 6 is not assigned",
/* 7 */ "Value 7 is not assigned"
};
/* ME message type to description table. */
char *me_str[] = {
};
char *getMEDescription(int metype, int mesub) {
char *mename = "Unknown";
if (metype >= 1 && metype <= 4)
mename = "Aircraft Identification and Category";
else if (metype >= 5 && metype <= 8)
mename = "Surface Position";
else if (metype >= 9 && metype <= 18)
mename = "Airborne Position (Baro Altitude)";
else if (metype == 19 && mesub >=1 && mesub <= 4)
mename = "Airborne Velocity";
else if (metype >= 20 && metype <= 22)
mename = "Airborne Position (GNSS Height)";
else if (metype == 23 && mesub == 0)
mename = "Test Message";
else if (metype == 24 && mesub == 1)
mename = "Surface System Status";
else if (metype == 28 && mesub == 1)
mename = "Extended Squitter Aircraft Status (Emergency)";
else if (metype == 28 && mesub == 2)
mename = "Extended Squitter Aircraft Status (1090ES TCAS RA)";
else if (metype == 29 && (mesub == 0 || mesub == 1))
mename = "Target State and Status Message";
else if (metype == 31 && (mesub == 0 || mesub == 1))
mename = "Aircraft Operational Status Message";
return mename;
}
3. 具体dump1090实现
3.1 信号幅度计算
首先,将原始的I/Q样本转换为幅度向量:
Modes.maglut = malloc(129*129*2);
for (i = 0; i <= 128; i++) {
for (q = 0; q <= 128; q++) {
Modes.maglut[i*129+q] = round(sqrt(i*i+q*q)*360);
}
}
...
void computeMagnitudeVector(void) {
// 计算幅度向量:SQRT(I^2 + Q^2)并重标定到0-255范围
for (j = 0; j < Modes.data_len; j += 2) {
int i = p[j]-127;
int q = p[j+1]-127;
if (i < 0) i = -i;
if (q < 0) q = -q;
m[j/2] = Modes.maglut[i*129+q];
}
}
3.2 前导码检测
在 detectModeS
函数中,通过检查特定的幅度模式来识别Mode S前导码:
void detectModeS(uint16_t *m, uint32_t mlen) {
unsigned char bits[MODES_LONG_MSG_BITS];
unsigned char msg[MODES_LONG_MSG_BITS/2];
uint16_t aux[MODES_LONG_MSG_BITS*2];
uint32_t j;
int use_correction = 0;
/* The Mode S preamble is made of impulses of 0.5 microseconds at
* the following time offsets: (Mode S前导码由0.5微秒的脉冲组成,时间偏移如下:)
*
* 0 - 0.5 usec: first impulse.(0 - 0.5 usec: 第一个脉冲)
* 1.0 - 1.5 usec: second impulse.(1.0 - 1.5 usec: 第二个脉冲)
* 3.5 - 4 usec: third impulse.(3.5 - 4 usec: 第三个脉冲)
* 4.5 - 5 usec: last impulse. (4.5 - 5 usec: 最后一个脉冲)
*
* Since we are sampling at 2 Mhz every sample in our magnitude vector
* is 0.5 usec, so the preamble will look like this, assuming there is
* an impulse at offset 0 in the array:
*
* 0 -----------------
* 1 -
* 2 ------------------
* 3 --
* 4 -
* 5 --
* 6 -
* 7 ------------------
* 8 --
* 9 -------------------
*/
前导码检测的关键步骤:
检查前10个样本之间的关系 (这个模式检查确保信号具有Mode S前导码的特征波形。):
/* First check of relations between the first 10 samples
* representing a valid preamble. We don't even investigate further
* if this simple test is not passed. */
if (!(m[j] > m[j+1] &&
m[j+1] < m[j+2] &&
m[j+2] > m[j+3] &&
m[j+3] < m[j] &&
m[j+4] < m[j] &&
m[j+5] < m[j] &&
m[j+6] < m[j] &&
m[j+7] > m[j+8] &&
m[j+8] < m[j+9] &&
m[j+9] > m[j+6]))
{
if (Modes.debug & MODES_DEBUG_NOPREAMBLE &&
m[j] > MODES_DEBUG_NOPREAMBLE_LEVEL)
dumpRawMessage("Unexpected ratio among first 10 samples",
msg, m, j);
continue;
}
检查脉冲之间的样本 :确保脉冲之间的样本幅度足够低
/* The samples between the two spikes must be < than the average
* of the high spikes level. We don't test bits too near to
* the high levels as signals can be out of phase so part of the
* energy can be in the near samples. */
high = (m[j]+m[j+2]+m[j+7]+m[j+9])/6;
if (m[j+4] >= high ||
m[j+5] >= high)
{
if (Modes.debug & MODES_DEBUG_NOPREAMBLE &&
m[j] > MODES_DEBUG_NOPREAMBLE_LEVEL)
dumpRawMessage(
"Too high level in samples between 3 and 6",
msg, m, j);
continue;
}
检查前导码后的空间 :确保前导码和实际数据之间的空间幅度足够低
/* Similarly samples in the range 11-14 must be low, as it is the
* space between the preamble and real data. Again we don't test
* bits too near to high levels, see above. */
if (m[j+11] >= high ||
m[j+12] >= high ||
m[j+13] >= high ||
m[j+14] >= high)
{
if (Modes.debug & MODES_DEBUG_NOPREAMBLE &&
m[j] > MODES_DEBUG_NOPREAMBLE_LEVEL)
dumpRawMessage(
"Too high level in samples between 10 and 15",
msg, m, j);
continue;
}
3.3 比特解码
一旦检测到有效的前导码,程序会解码后续的112个比特:
/* Decode all the next 112 bits, regardless of the actual message
* size. We'll check the actual message type later. */
errors = 0;
for (i = 0; i < MODES_LONG_MSG_BITS*2; i += 2) {
low = m[j+i+MODES_PREAMBLE_US*2];
high = m[j+i+MODES_PREAMBLE_US*2+1];
delta = low-high;
if (delta < 0) delta = -delta;
if (i > 0 && delta < 256) {
bits[i/2] = bits[i/2-1];
} else if (low == high) {
/* Checking if two adiacent samples have the same magnitude
* is an effective way to detect if it's just random noise
* that was detected as a valid preamble. */
bits[i/2] = 2; /* error */
if (i < MODES_SHORT_MSG_BITS*2) errors++;
} else if (low > high) {
bits[i/2] = 1;
} else {
/* (low < high) for exclusion */
bits[i/2] = 0;
}
}
3.4 消息类型识别
解码比特后,程序会将比特打包成字节,并确定消息类型:
// 打包比特为字节
for (i = 0; i < MODES_LONG_MSG_BITS; i += 8) {
msg[i/8] =
bits[i]<<7 |
bits[i+1]<<6 |
bits[i+2]<<5 |
bits[i+3]<<4 |
bits[i+4]<<3 |
bits[i+5]<<2 |
bits[i+6]<<1 |
bits[i+7];
}
// 确定消息类型和长度
int msgtype = msg[0]>>3;
int msglen = modesMessageLenByType(msgtype)/8;
3.5 信号质量验证
程序会进行最终的幅度差异检查,以过滤掉随机噪声:
/* Last check, high and low bits are different enough in magnitude
* to mark this as real message and not just noise? */
// 计算平均幅度差异
delta = 0;
for (i = 0; i < msglen*8*2; i += 2) {
delta += abs(m[j+i+MODES_PREAMBLE_US*2]-
m[j+i+MODES_PREAMBLE_US*2+1]);
}
delta /= msglen*4;
/* Filter for an average delta of three is small enough to let almost
* every kind of message to pass, but high enough to filter some
* random noise. */
// 过滤掉幅度差异太小的信号
if (delta < 10*255) {
use_correction = 0;
continue;
}
3.6 消息解码
最后,如果消息通过了前面的检查,程序会解码消息
/* If we reached this point, and error is zero, we are very likely
* with a Mode S message in our hands, but it may still be broken
* and CRC may not be correct. This is handled by the next layer. */
if (errors == 0 || (Modes.aggressive && errors < 3)) {
struct modesMessage mm;
/* Decode the received message and update statistics */
// 解码消息
decodeModesMessage(&mm,msg);
...
3.7 CRC验证
最后,验证CRC
/* Update statistics. */
if (mm.crcok || use_correction) {
if (errors == 0) Modes.stat_demodulated++;
if (mm.errorbit == -1) {
if (mm.crcok)
Modes.stat_goodcrc++;
else
Modes.stat_badcrc++;
} else {
Modes.stat_badcrc++;
Modes.stat_fixed++;
if (mm.errorbit < MODES_LONG_MSG_BITS)
Modes.stat_single_bit_fix++;
else
Modes.stat_two_bits_fix++;
}
}
总结
完成了,远古时期的不明白,虽然还有测试文件,但是由于我现在的Windows系统,这个是linux环境,就先不测试他提供的modes1.bin
了。