FSD 通讯协议参考
本文档为 FSD(Flight Simulator Data)通讯协议开发参考,供客户端与服务端开发者参考。FSD_Cangjie 服务端实现的是 Legacy FSD(经典模式,无 VATSIM 认证握手)。
本协议源自 Marty Bochane 的 FSD Server;字段与编码参考 fsd-doc.norrisng.ca 及 swift-project/pilotclient(
src/core/fsd/)。
1. 概述
- FSD 是虚拟航网(VATSIM / IVAO / 各私有网络)使用的文本行协议,基于 TCP。
- 服务端把客户端与服务器间连接的报文解析、路由、广播;每种报文由**命令前缀(PDU)**标识。
- 本服务端使用 Legacy 模式:TCP 连上后直接发
#AP登录(无$DI/$ID认证握手)。 - 协议版本(Revision)常量:
9= Classic/Legacy(本服务端),10= VATSIM ATC,100= VATSIM 认证,101= VATSIM 视觉位置。只有9被接受。
2. 传输层
| 项目 | 说明 |
|---|---|
| 传输 | TCP |
| 默认端口 | 6809(客户端);3011(服务器互联);3010(系统管理) |
| 分帧 | 每行以 \r\n 结尾(兼容 \n) |
| 字段分隔 | 冒号 : |
| 文本编码 | 常规报文 UTF-8;原始字节(GBK/语音)走 #SB 透传 |
| 单行长度 | 最大 4096 字节,超长丢弃并断开 |
报文序列化规则:
报文 = PDU前缀 + 字段1 + ":" + 字段2 + ":" + ... + "\r\n"1
即 PDU前缀 紧跟 :发送方:接收方[:字段...]。发送端通常采用消息队列批量出队(一次连发若干条,防洪峰)。
3. 报文类型总表
3.1 客户端接口(端口 6809)
| # | PDU | 名称 | 方向 | 说明 |
|---|---|---|---|---|
| 0 | #AA | AddAtc | C→S | ATC/OBS 登录 |
| 1 | #DA | DeleteAtc | C→S | ATC 注销 |
| 2 | #AP | AddPilot | C→S | 飞行员登录 |
| 3 | #DP | DeletePilot | C→S | 飞行员注销 |
| 4 | $HO | ReqHandoff | C→S | 管制移交请求 |
| 5 | #TM | TextMessage | ↔ | 文本 / 频率 / 广播消息 |
| 6 | #RW | ReqWeather | C→S | 请求(重发)天气 |
| 7 | @ | PilotDataUpdate | C/S→ | 机组位置更新 |
| 8 | % | AtcDataUpdate | C/S→ | 管制位置更新 |
| 9 | $PI | Ping | ↔ | 保活 |
| 10 | $PO | Pong | ↔ | 保活应答 |
| 11 | $HA | ACHandoff | S→C | 移交确认 |
| 12 | $FP | FlightPlan | ↔ | 飞行计划 |
| 13 | #SB | PilotClientCom | ↔ | 机型信息 / 原始字节 |
| 14 | #PC | ProController | ATC→ | 管制移交(Pro Controller) |
| 15 | #WX | Weather | ↔ | 气象 |
| 16 | #CD | CloudData | →C | 云层数据 |
| 17 | #WD | WindData | →C | 风数据 |
| 18 | #TD | TempData | →C | 温度数据 |
| 19 | $C? | ReqCom | C→ | 通信查询 |
| 20 | $CI | RepCom | →C | 通信应答 |
| 21 | $AX | ReqACARS | C→ | ACARS 请求 |
| 22 | $AR | RepACARS | →C | ACARS 应答 |
| 23 | $ER | ServerError | S→C | 服务器错误 |
| 24 | $CQ | ClientQuery | C→ | 客户端查询 |
| 25 | $CR | ClientResponse | →C | 查询应答 |
| 26 | $!! | KillRequest | S→C | 服务器踢人 / 停机通知 |
3.2 服务器互联接口(端口 3011)
| # | 名称 | 说明 |
|---|---|---|
| 0 | NOTIFY | 服务器上线 / 身份通告 |
| 1 | REQMETAR | 请求气象 |
| 2 | PING | 心跳 |
| 3 | PONG | 心跳应答 |
| 4 | SYNC | 全量同步 |
| 5 | LINKDOWN | 链路断开 |
| 6 | NOWX | 该服务器无天气 |
| 7 | ADDCLIENT | 新增客户端 |
| 8 | RMCLIENT | 移除客户端 |
| 9 | PLAN | 飞行计划 |
| 10 | PD | 机组位置 |
| 11 | AD | 管制位置 |
| 12 | ADDCERT | 证书新增 / 修改 |
| 13 | MC | 多播(文本/命令转发) |
| 14 | WX | 天气 |
| 15 | METAR | METAR 报文 |
| 16 | AWPROF | 新增天气剖面 |
| 17 | DWPROF | 删除天气剖面 |
| 18 | KILL | 强制下线 |
| 19 | RESET | 重置 |
4. 登录与会话流程
4.1 登录(Legacy)
1. TCP connect(host, 6809)
2. 直接发送 #AP(无需握手)
3. 校验通过后进入 Connected,启动位置定时器1
2
3
2
3
机组 #AP:
#AP<呼号>:SERVER:<CID>:<密码>:<等级>:<协议版本>:<模拟器类型>:<真实姓名>1
管制 #AA:
#AA<呼号>:SERVER:<真实姓名>:<CID>:<密码>:<等级>:<协议版本>1
校验顺序:呼号长度 → 协议版本(须为 9)→ 证书(CID/口令)→ 等级 → 呼号/CID 唯一性;失败以 $ER 返回并断开。
4.2 注销 / 踢人 / 迁移
| 场景 | 报文 |
|---|---|
| 飞行员正常断开 | #DP<呼号>:<CID> |
| ATC/OBS 断开 | #DA<呼号>:<CID> |
| 服务器踢人 | $!!<sender>:<receiver>:<reason> → 客户端断开 |
| 服务器停机通知 | $!!SERVER:<呼号>:Server restarting, please reconnect |
5. 位置上报
5.1 @ PilotDataUpdate(广播给范围内客户端)
@<应答机模式>:<呼号>:<应答机编码>:<等级>:<纬度>:<经度>:<真高ft>:<地速kt>:<PBH>:<高度修正>1
| # | 字段 | 说明 |
|---|---|---|
| 0 | transponderMode | S=待机,N=Mode A/C/S 开,Y=ident |
| 1 | callsign | 发送方呼号 |
| 2 | transponderCode | 应答机编码(squawk) |
| 3 | rating | 客户端上报评级(客户端约定,见 13.2) |
| 4 | latitude | 度,5 位小数 |
| 5 | longitude | 度,5 位小数 |
| 6 | altitudeTrue | 真高(ft) |
| 7 | groundSpeed | 地速(kt) |
| 8 | pbh | 32 位 PBH 位域(见 5.3) |
| 9 | correction | 气压高 − 真高(可负);解析:pressureAlt = trueAlt + token[9] |
- 广播频率:约 5s 一次。
- 范围过滤:仅转发给
getrange()距离内用户,且不回发本人;机组可视范围与高度相关(约10 + 1.414·√高度海里)。
5.2 % AtcDataUpdate
%<呼号>:<频率kHz偏移>:<设施类型>:<可视范围NM>:<等级>:<纬度>:<经度>:<场压高ft>1
- 频率字段 = 实际 kHz − 100000。
- 设施类型:
0OBS /1FSS /2DEL /3GND /4TWR /5APP /6CTR。 - 观察员(OBS)以
#AA登录,按固定频率199998kHz、range300上报。
5.3 PBH 位域编码
bit 0 : 未用
bit 1 : onGround
bits 2-11 : heading(10 bit 无符号,0..1023)
bits 12-21: bank(10 bit 有符号)
bits 22-31: pitch(10 bit 有符号)1
2
3
4
5
2
3
4
5
编码公式(FSD 中 pitch/bank 取反):
pitch = floor(pitch * -256.0 / 90.0);
bank = floor(bank * -512.0 / 180.0);
hdg = round(heading * 1024.0 / 360.0);1
2
3
2
3
6. 文本消息 #TM
#TM<发送方>:<接收方>:<消息>1
| 接收方 | 含义 |
|---|---|
| 具体呼号 | 私聊 |
@<频率偏移> | 频率消息(偏移 = kHz − 100000,如 118.800MHz → @18800;多频率用 & 连接) |
* / *A / *P / *S | 全部 / 全部管制 / 全部飞行员 / 全部 SUP |
频率换算:
目标字符串 = "@%05d" % (round(mHz * 1000) - 100000)
解析时 freq_kHz = int(token) + 1000001
2
2
7. 客户端查询 $CQ / $CR
$CQ<发送方>:<目标>:<查询类型>[:数据...]
$CR<发送方>:<接收方>:<查询类型>[:应答数据...]1
2
2
| 类型码 | 含义 | 说明 |
|---|---|---|
ATC | IsValidATC | 发给 SERVER,数据为目标呼号;应答 Y/N |
CAPS | Capabilities | 应答为 KEY=1 键值列表 |
C? | Com1Freq | 应答 COM1 频率(MHz,3 位小数) |
RN | RealName | 应答:真实姓名 + 扇区文件 + 评级 |
SV | Server | 应答服务器地址 |
ATIS | ATIS | 仅 ATC 应答,行类型前缀 V/T/Z/E |
IP | PublicIP | 仅服务器应答 |
INF | 特权信息 | SUP 用,以 #TM 回传 |
FP | FlightPlan | 发给 SERVER,应答为 $FP |
8. 机型信息 #SB(PilotClientCom)
| 子类型 | 报文 | 说明 |
|---|---|---|
PIR | #SB<呼号>:<目标>:PIR | 请求机型信息 |
PI | #SB<呼号>:<目标>:PI:GEN:EQUIPMENT=<机型>[:AIRLINE=<航司>][:LIVERY=<涂装>] | 应答(GEN 现代格式) |
FSIPIR | #SB<呼号>:<目标>:FSIPIR:0:<航司>:<机型>::::::<组合类型>:<model> | FSInn 请求 |
FSIPI | #SB<呼号>:<目标>:FSIPI:0:<航司>:<机型>::::::<组合类型>:<model> | FSInn 应答 |
9. 飞行计划 $FP(17 字段)
$FP<呼号>:SERVER:<I|V|S|D>:<机型/设备码>:<巡航速度kt>:<起飞机场>:
<计划起飞hhmm>:<实际起飞hhmm>:<巡航高度>:<目的地>:
<航程时>:<航程分>:<燃油时>:<燃油分>:<备降场>:<备注>:<航路>1
2
3
2
3
- 飞行类型:
I=IFR、V=VFR、S=SVFR、D=DVFR。 - 巡航高度可为
FLxxx或xxxxxft。 - 报文内禁止冒号(route/remarks 中的
:需移除)。
10. Ping / Pong 保活
$PI<呼号>:<目标>:<时间戳ms> → 收到后须回
$PO<呼号>:<目标>:<时间戳ms>1
2
2
时间戳为毫秒;收到 $PI 立即原样回 $PO,可计算 RTT。
11. 服务器错误 $ER
$ER<server>:<呼号>:<错误码>:<出错参数>:<描述>1
错误码(本服务端):
| 码 | 常量 | 描述 | 致命 |
|---|---|---|---|
| 0 | OK | 无错误 | - |
| 1 | CSINUSE | 呼号已被占用 | 是 |
| 2 | CSINVALID | 呼号无效 | 是 |
| 3 | REGISTERED | 已注册 | 是 |
| 4 | SYNTAX | 语法错误 | - |
| 5 | SRCINVALID | 来源无效 | - |
| 6 | CIDINVALID | CID/口令错误 | 是 |
| 7 | NOSUCHCS | 无此呼号 | - |
| 8 | NOFP | 无飞行计划 | - |
| 9 | NOWEATHER | 无此天气剖面 | - |
| 10 | REVISION | 协议版本不匹配 | 是 |
| 11 | LEVEL | 请求等级过高 | 是 |
| 12 | SERVFULL | 服务器已满 | 是 |
| 13 | CSSUSPEND | CID 已停用 | 是 |
| 14 | CIDINUSE | CID 已在使用 | 是 |
收到致命错误应断开连接。
12. 定时器与速率参数
| 参数 | 值 | 说明 |
|---|---|---|
位置更新 @ | 约 5s | 客户端上报频率 |
| 会话读超时 | 300s | 服务端空闲断开 |
| 写超时 | 30s | 慢客户端保护 |
| 登录并发闸门 | 16 | 防瞬时 OOM |
| 最大客户端数 | 由 maxclients 配置 | - |
13. 编码与等级
13.1 本服务端权限等级(levelName)
本服务端用**权限等级(level)**表示用户权限,取值来自证书(cert.txt)或数据库:
| level | 名称 | 说明 |
|---|---|---|
| 0 | SUSPENDED | 已停用(不可登录) |
| 1 | OBSERVER | 观察员 |
| 2 | REGISTERED PILOT | 注册机组 |
| 3 | CERTIFIED PILOT | 认证机组 |
| 4 | APP TRAINEE | 见习管制 |
| 5 | CONTROLLER STAFF | 见习管制员 |
| 6 | CONTROLLER OPS | 管制员 |
| 7 | CONTROLLER SPECIALIST | 高级管制员 |
| 8 | CONTROLLER INSTRUCTOR | 管制教员 |
| 9 | AIR TRAFFIC MANAGER | 流量管理 |
| 10 | PILOT COORDINATOR | 机组协调 |
| 11 | SUPER ADMIN | 超级管理员 |
| 12 | ADMINISTRATOR | 管理员 |
登录校验:实际等级须高于
SUSPENDED,且不低于请求等级。
13.2 客户端约定值(服务端仅透传)
下列字段的取值含义由客户端软件自行约定(swift / SquawkBox / xPilot 等可能不一致),本服务端只原样透传、不解释:
| 字段 | 说明 |
|---|---|
simtype | 模拟器类型(如 swift 约定:0 未知 / 9 MSFSX / 10 MSFS2020 / 16 X-Plane 11 / 30 P3D …) |
transponder | 应答机模式:S 待机 / N 模式或 Ident / Y Ident |
facilitytype | 管制席位类型:0 OBS / 1 FSS / 2 DEL / 3 GND / 4 TWR / 5 APP / 6 CTR |
报文内 rating | 客户端上报的评级编码(各客户端约定不同),与本服务端权限等级(13.1)不是一回事 |
TIP
这些值本服务端只写入 #AP / % 广播与 WhazzUp 输出,不做名称映射;对接不同客户端时以其约定为准。
14. 参考资料
- FSD 协议文档:https://fsd-doc.norrisng.ca/
- swift-project/pilotclient:https://github.com/swift-project/pilotclient