IRIG-B码对时

1、理论知识

1.1、IRIG-B:IRIG所定义的六种时间信息码(A、B、D、E、G和H码),均采用串行输出格式(串行输出码),用于与时间信号输入、输出的精确同步

1.2、B码对时的定义:是IRIG时间编码序列的一种,是将时间同步信号和秒、分、时、天等时间码信息加载到频率为1kHz的信号载体中。GPS系统接受卫星时间信号,输出IRIG- B时间码序列,变电站智能设备可以挂在统一的对时总线上进行时间同步。变电站的智能设备采用 B 码对时, 就不再需要进行基于现场总线的通信报文对时, 同时也不需要 GPS 输出大量脉冲对时信号。

格式 时帧周期 码元速率 二-十进制信息位数 表示时间的信息
IRIG-D 1小时 1个/分 16 天、时
IRIG-H 1分 1个/秒 23 天、时、分
IRIG-E 10秒 10个/秒 26 天、时、分、10秒
IRIG-B 1秒 100个/秒 30 天、时、分、秒
IRIG-A 0.1秒 1000个/秒 34 天、时、分、秒、0.1秒
IRIG-G 0.01秒 10,000个/秒 38 天、时、分、秒、0.1秒、0.01秒

1.3、IRIG-B时间序列码说明

示例图片

1.4、B码信号是每秒一帧的时间串码,一帧串码中包含100个码元,频率为1kHz,故每个码元占用10ms时间,其基本的码元是“0”码元、“1”码元和“P”码元。码元“0”和“1”对应的脉冲宽度为2ms和5ms,“P”码元是位置码元,对应的脉冲宽度为 8 ms,B码信息的基本码元的示意图如图所示。

示例图片

1.5、脉冲图(如何将脉冲和序列结合起来分析)

1
P0 Pr 0 1 0 0 0 1 1 0 P1|0 0 0 1 0 0 0 0 0 P2|0 0 1 0 0 1 0 0 0 P3
示例图片

2、实例

2.1、目标:通过B码时间序列码,解析出其所代表的内容
2.2、一秒B码序列(低前高后,0码元、1码元和P码元就是B码时间序列的内容)

一秒B码时间序列
(Pr)00010000(P1)000100010(P2)
100000000(P3)100001010(P4)
010000000(P5)100000100(P6)
000000000(P7)000001000(P8)
000110101(P9)001100000(P0)
  • 获取秒(Pr00010000P1,7位): 秒个位(0001)=8,秒十位(000)=0
  • 获取分(000100010P2,7位):分个位(0001)=8,分十位(001)=4
  • 获取时(100000000P3,6位):时个位(1000)=1,时十位(00)=0
  • 获取天(100001010(P4)010000000(P5),10位):日个位(1000)=1,日十位(1010)=5,日百位(01)=2
  • 获取年(100000100P6,8位):年个位(位置在P50-P53,1000)=1, 年十位(位置在P55-P58,0100)=2

则该B码对时为21年第251天01时48分08秒,则为21年09月08日01:48:08

解析的时候,需要对照官方B码解析图(这样解析的位才能对的上)

2.3、代码实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
/************************* 开始IRIG-B码校时处理 **********************************/
typedef struct
{
uint32 nBeginCycle; //准点对应的系统Cycle的值
//是一个步骤变量,用于跟踪解析IRIG-B码的当前步骤
uint8 nStep; //当前步骤0:等待同步 1:P0 2:秒 3:分 4:时 5:天(十、个) 6:天(百)
struct tagDateTime
{
uint32 s; //秒
uint32 m; //分
uint32 h; //时
uint32 d; //天
uint32 y; //年(标准IRIG中没有年信息,因此这里需要考虑有没有年,值为0,则采用当前的年信息)
} dt;
uint32 nBitTmp; //IRIG-B码中字段值暂存
uint32 nBitIdx; //当前信息索引(用于跟踪当前正在处理的位在IRIG-B码中的位置,0-8或者0-9)

uint8 mdy_y; // 预修改的年信息
uint8 mdy_y_ok; // 年要进行修改
}TIRIG_B;

SECTION_DEF_FastData TIRIG_B g_IRIG_B;

/*
1、一秒B码的内容(一个码元10ms,一共有100个码元)
| P00010000P000100010P |
| 100000000P100001010P |
| 010000000P100000100P |
| 000000000P000001000P |
| 000110101P001100000P |
2、位运算符:&与,|或,^异或,~取反
3、条件语句
if语句---->依次判断,选择执行的语句
switch语句---->依次判断,顺序执行的语句
4、
*/
SECTION_DEF_FastCode_Lv2 void GPSTimeProc_IRIG_B(void)
{
//读取定时器的计数值、周期和宽度
uint32 nCounter = *pTIMER2_COUNTER; //timer0 counter
uint32 nPeriod = *pTIMER2_PERIOD; //timer0 peroid
uint32 nWidth = *pTIMER2_WIDTH; //timer0 width
uint32 status = *pTIMER_STATUS; //timer status
uint32 nCycles;

//处理定时器溢出情况:如果定时器溢出,设置GPS信号丢失
if(status & TOVF_ERR2) //timer2 count overflow
{
*pTIMER_STATUS = TOVF_ERR2; //确认状态
//置GPS超时
gps_set_no_signal();
}
//当有中断请求时,根据脉冲的宽度识别IRIG-B码中的0码、1码和P码
else if(status & TIMIL2) //有中断请求,读数据---->根据中断标志,解析B码内容,然后时间同步
{
*pTIMER_STATUS = TIMIL2; //确认状态

//PRINTF("IRIG %d / %d \r\n", 1000 / (APP_CPU_SCLK_FREQ / nWidth ), 1000 / (APP_CPU_SCLK_FREQ / nPeriod ));
//有效的IRIG-B信号检查
//nPeriod > GPS_IRIG_PERIOD_MIN && nPeriod < GPS_IRIG_PERIOD_MAX-->IRIG-B信号的周期有一个标准范围,只有在这个范围内的信号才是有效的IRIG-B信号
//nCounter < (APP_CPU_SCLK_FREQ/1000)-->IRIG-B信号的宽度有一个标准范围,只有在这个范围内的信号才是有效的IRIG-B信号
if(nPeriod > GPS_IRIG_PERIOD_MIN && nPeriod < GPS_IRIG_PERIOD_MAX && nCounter < (APP_CPU_SCLK_FREQ/1000)/*限制1ms*/) //周期正确
{
_gps_pulse_overtimer = 0; //有10ms脉冲,GPS脉冲超时清零

//0码,二进制0
//g_IRIG_B为IRIG-B码校时处理结构体
if(nWidth > GPS_IRIG_2MS_WIDTH_MIN && nWidth < GPS_IRIG_2MS_WIDTH_MAX) //2ms脉冲 0
{
//P0,Pr,P1,P2,P3,P4,P5,P6,P7,P8,P9(0-10)
if((g_IRIG_B.nStep < 2) || (g_IRIG_B.nBitIdx > 10)) //每次信息结束不超过10位
{
g_IRIG_B.nStep = 0;
}
else
{
//g_IRIG_B.nBitIdx对应的位为0
g_IRIG_B.nBitIdx++; //下一位
}
}
//1码,二进制1
else if(nWidth > GPS_IRIG_5MS_WIDTH_MIN && nWidth < GPS_IRIG_5MS_WIDTH_MAX) //5ms脉冲 1
{
if((g_IRIG_B.nStep < 2) || (g_IRIG_B.nBitIdx > 10)) //每次信息结束不超过10位
{
g_IRIG_B.nStep = 0;
}
else
{
//g_IRIG_B.nBitIdx表示的是字段索引位
//g_IRIG_B.nBitTmp为B码时间序列码
g_IRIG_B.nBitTmp |= 1<<g_IRIG_B.nBitIdx; //位处理,置1
g_IRIG_B.nBitIdx++; //下一位
}
}
//P码,遇到P码才开始处理
else if(nWidth > GPS_IRIG_8MS_WIDTH_MIN && nWidth < GPS_IRIG_8MS_WIDTH_MAX) //8ms脉冲
{
//nStep,当前步骤0:等待同步 1:P0 2:秒 3:分 4:时 5:天(十、个) 6:天(百)
//条件执行
if(g_IRIG_B.nStep==0) //第一个同步脉冲 P0
{
hd_GetCycles(nCycles);
g_IRIG_B.nBeginCycle = nCycles - nCounter * APP_CPU_SCLK_DIV; //PR上升延的对应的准确时刻

g_IRIG_B.nStep = 1; //等待PR
}
//每帧的解析从参考标志Pr开始
else if(g_IRIG_B.nStep == 1) //第二个同步脉冲 PR
{
g_IRIG_B.nStep = 2; //秒传输
}
else
{
//当前信息索引,首次为8,后面都为9
if(g_IRIG_B.nBitIdx != 8 && g_IRIG_B.nBitIdx != 9)
{
g_IRIG_B.nStep = 0; //清除所有状态,重新开始检索P0
}
else if(g_IRIG_B.nStep == 2) //秒传输结束--->此时,g_IRIG_B.nBitTmp=00010000,g_IRIG_B.nBitIdx=8
{
//BCD码(Binary-Coded Decimal),用4位二进制数来表示1位十进制中的0~9这10个数码,是一种二进制的数字编码形式,用二进制编码的十进制代码
//从g_IRIG_B.nBitTmp获取秒信息,一共7位
/*
#define _BCD_TO_SECOND(nVal) (((nVal)&0x0F) + (((nVal)>>5)&0x07)*10) //秒 7
1、(nVal)&0x0F:获取低四位(秒的个位)
2、(((nVal)>>5)&0x07):获取6-8位得到秒的十位,第5位为索引标志(置0),乘十变高位
*/
g_IRIG_B.dt.s = _BCD_TO_SECOND(g_IRIG_B.nBitTmp);
g_IRIG_B.nStep = 3; //进入下一步

}
else if(g_IRIG_B.nStep == 3) //分传输结束---->,g_IRIG_B.nBitTmp=000100010,g_IRIG_B.nBitIdx=9
{
//从g_IRIG_B.nBitTmp获取分信息,一共7位
/*
#define _BCD_TO_MINITE(nVal) (((nVal)&0x0F) + (((nVal)>>5)&0x07)*10) //分 7
*/
g_IRIG_B.dt.m = _BCD_TO_MINITE(g_IRIG_B.nBitTmp);
g_IRIG_B.nStep = 4; //进入下一步

}
else if(g_IRIG_B.nStep == 4) //时传输结束----->,g_IRIG_B.nBitTmp=100000000,g_IRIG_B.nBitIdx=9
{
//从g_IRIG_B.nBitTmp获取时信息,一共6位
/*
#define _BCD_TO_HOUR(nVal) (((nVal)&0x0F) + (((nVal)>>5)&0x03)*10) //时 3
1、(nVal)&0x0F:获取低四位(时的个位)
2、(((nVal)>>5)&0x03):获取6-7位得到时的十位,第五位为索引标志,乘十变高位
*/
g_IRIG_B.dt.h = _BCD_TO_HOUR(g_IRIG_B.nBitTmp);
g_IRIG_B.nStep = 5; //进入下一步

}
else if(g_IRIG_B.nStep == 5) //天(十、个)----->,g_IRIG_B.nBitTmp=100001010,g_IRIG_B.nBitIdx=9
{
//从g_IRIG_B.nBitTmp获取天信息(个位+十位),一共8位
/*
#define _BCD_TO_DAY1(nVal) (((nVal)&0x0F) + (((nVal)>>5)&0x0F)*10) //天(十、个位)
1、(nVal)&0x0F:获取低四位(天的个位)
2、(((nVal)>>5)&0x03):获取6-9位得到天的十位,第五位为索引标志,乘十变高位
*/
g_IRIG_B.dt.d = _BCD_TO_DAY1(g_IRIG_B.nBitTmp);
g_IRIG_B.nStep = 6; //进入下一步

}
else if(g_IRIG_B.nStep == 6) //天(百)------>,g_IRIG_B.nBitTmp=010000000,g_IRIG_B.nBitIdx=9
{
//从g_IRIG_B.nBitTmp获取天信息(百位),一共2位
/*
#define _BCD_TO_DAY2(nVal) ((nVal)&0x03) //天(百位)
1、(nVal)&0x03:获取低两位(天的百位)
*/
g_IRIG_B.dt.d += 100 * _BCD_TO_DAY2(g_IRIG_B.nBitTmp);
g_IRIG_B.nStep = 7; //下一步

}
else if(g_IRIG_B.nStep == 7) //年------->,g_IRIG_B.nBitTmp=100000100,g_IRIG_B.nBitIdx=9
{
//从g_IRIG_B.nBitTmp获取年信息,一共8位
/*
#define _BCD_TO_YEAR(nVal) (((nVal)&0x0F) + (((nVal)>>5)&0x0F)*10) //年
1、(nVal)&0x0F:获取低四位(年的个位)
2、(((nVal)>>5)&0x0F):获取6-9位得到年的十位,第5位为索引标志,乘十变高位
*/
g_IRIG_B.dt.y = _BCD_TO_YEAR(g_IRIG_B.nBitTmp);
//校时结束,设置时间
g_IRIG_B.nStep = 0; //等待同步

//设置系统时间
GPSSetIRIGTime(); //根据解析出的时间信息设置系统时间的函数
}
else
{
g_IRIG_B.nStep = 0;
}
}
g_IRIG_B.nBitTmp = 0; //清空B码时间序列
g_IRIG_B.nBitIdx = 0; //当前信息索引
}
}
else
{
g_IRIG_B.nStep = 0; //等待同步
}
}
}

参考文章:IRIG-B码对时-CSDN博客

3、时间分类

1、嵌入式装置的时间一般分为三种:B码时间、系统时间和RTC时间

2、B码时间:B码装置的时间(本地时间,CST时间),也称对时装置,主要为了嵌入式装置提供时间基准

3、系统时间:Linux系统时间,为任务调度和时钟等提供服务(UTC时间)

4、RTC时间:实时时间,初次由系统时间设置,首次基于初值以后,该时间就会自走时,如果装置掉电以后,下次装置重启,就可以利用RTC时间设置系统时间

5、