由于现有的aprs, 系统基础设施不完善, 很难在一个地区形成有规模的网络. 而现有的aprs-g系统终端造价不菲, 系统不开放, 普通用户无法仿制, 虽有一定用户群, 但也限制了aprs的发展, 而且这些系统都有将aprs商业化的趋势, 个人理解这是有违aprs的设计初衷的.
这篇学习笔记是近两天看了一些文档的记录, 搜了一下net上的贴,也发现有朋友希望投入这方面的制作,但不能理解英文协议中的文字怎样具体化到代码. 希望这篇文档可以起到抛砖引玉的作用.
终端系统的结构非常简单, gps模块->mcu->dtu, mcu负责接收gps模块输出的定位信息, 封装(或不封装,因为aprs-is支持nmea的标准语句), 负责登陆aprs-is服务器, 发送源路径头数据后,最简单的做法就是可以直接发送nmea格式的gps输出信息. 这里的mcu需要2个uart,或者将一个uart的rx和tx分开用,rx用来接收gps的数据,tx用来连接dtu发送数据.
另外,为了降低系统成本, 也可以在mcu上实现精简的tcp/ip协议,如uip等,使用gprs模块等可使成本降低.
daprs的优势在于: 弥补了aprs本身的缺陷,由于tcpip是有状态的协议, 所以被精简了的ax.25发送数据后不能确认数据是否被送达的缺点被弥补了, 每一条数据都会被准确送到aprs-is服务器; 另外,一个很小区域内的节点数量,不再是35个或者更少, 只取决于移动通信运营商是否有足够的信道; 最后, daprs依赖基础电信运营商, 不依赖个人搭建的数据平台, 比较适合目前的国情.
由于解决了上述两大aprs的缺陷, 我们有理由相信,daprs会给我们描绘一个美好的aprs的未来!
--------------------------------------------------------------
名词解释:
daprs: digital automatic position reporting system
dtu: data transmit unit的简称(或称ip modem),是基于gprs或者cdma或phs无线公网开发的数据传输设备,内嵌精简或完整的tcp/ip协议栈,提供标准的rs232或485接口,可适用于所有带串口的终端设备,为不具备tcp/ip协议处理的终端设备提供无线通信能力。
---------------------------------------------------------------
自动位置报告系统 学习笔记 ver0.9
作者: bg5epe
版本: 0.9
更新: 2007年12月1日
aprs(r)是bob bruninga的注册商标
参考文献
1, aprs101.pdf aprs protocol reference protocol version 1.01
2, aprs 和 aprs-is 协议简报 ba5ag学生的作品
3, whereavr 源代码
以太网络连接aprs
aprs 为了支持第三方(比如非ax.25)网络提供了格式化分包的一种机制, 就好比因特网, 局域网或者直接电缆连接.这些网络不知道 aprs 源地
址, 目标地址和数字中继地址, 所以必须把它们封装成数据, 和原始数据一起被发送出去.
源路径头
在发送一条 arps 封包到第三方网络之前, aprs 地址路径要比数据类型标识和其他原始数据更优先发送.优先地址路径就是源路径头.
tnc-2格式
bg5epe-0>aprs,relay*,wide:
aea格式
bg5epe-0>relay*>wide>aprs:
第三方头
当一个封包的头出现在第三方网络的时候, 接收网关节点在把它转发到本地 aprs网络前对封包进行修改 (插入一个} 即第三方数据类型标识,
以及修改源路径头).被修改的源路径头被称作第三方头.
tnc-2格式
bg5epe-0>aprs,relay,tcpip,bg4uvr*:
aea格式
bg5epe-0>relay>tcpip>bg4uvr*>aprs:
通过因特网发送一条信息
假设:
wb4apr-14 想通过因特网发送一条消息给 g3nrw.
离 wb4apr-14 最近的一个因特网网关是 k4hg, 可以通过一个 relay,wide 的路径到达.
离 g3nrw 最近的一个因特网网关是 g9rxg.
处理过程:
在通常的情况下, wb4apr-14 创建一条信息封包包含:
g3nrwvvvv:hi ian{001
wb4apr-14 用他的 unproto 通过路径 relay,wide 发送这个封包.
因特网网关 k4hg 从路径中的数字中继 relay 接收到这个封包.
k4hg 创建一个新的封包, 该封包包含源路径和原始的消息:
wb4apr-14>aprs,relay*,wide::g3nrwvvvv:hi ian{001
k4hg 发送这个封包 (使用 telnet) 到因特网上的一台 aprs 服务器.
因特网上所有连网的 aprs 服务器 (包括 g9rxg 的) 接收到这个封包.
g9rxg 转换封包格式成为第三方封包:
}wb4apr-14>aprs,relay,tcpip,g9rxg*::g3nrwvvvv:hi ian{001
注意一点 wide 数字中继被除去因为这时候它已经没有用了.
g9rxg 通过本地的 aprs 网络发送该封包.
g3nrw 收到封包, 除去第三方头, 并发现封包中包含有发送给他的一条消息.
然后再从头开始, g3nrw 确认到达通知并发送回给 wb4apr-14.
通用数据格式
通常情况下, ax.25 信息字段能包含以下一些或者全部的信息内容:
aprs 字段类型标识
aprs 数据
aprs 数据扩展
备注
位置报告格式
以=开头为不带时间戳,带aprs消息;
以!开头为不带时间戳,不带aprs消息;
以@开头为带时间戳,带aprs消息;
以/开头为带时间戳,不带aprs消息;
@234517h 开头, hhmmss时间, 以h表示;
@092345z 开头, 世界标准时间, 以z表示;
@092345 开头, 本地时间, 不跟任何字母;
>088/036 结尾, 航向/速度, 注意速度是英制单位;
/a=001234 结尾, 高度=1234 ft, 注意单位;
无时间戳(!或=开头)
!4903.50n/07201.75w-test 001234 无时间戳, 无消息, 无备注.
!4903.50n/07201.75w-test /a=001234 无时间戳, 无消息, 高度 = 1234 ft.
!49 . n/072 . w- 无时间戳, 无消息, 定位到附近位置
thenet x-1j4 (bfld)!4903.50n/07201.75wn 无时间戳, 无消息, 带x1j节点头
带时间戳(/或@开头)
/092345z4903.50n/07201.75w>test1234 带时间戳, 无消息, 标准时间, 带备注.
@092345/4903.50n/07201.75w>test1234 带时间戳, 带消息, 本地时间, 带备注.
带数据扩展, 无时间戳(!或=开头)
=4903.50n/07201.75w#phg5132 无时间戳, 带消息, 带 phg.
=4903.50n/07201.75w_225/000g000t050r000p001...h00b10138du2k 天气预报.
带数据扩展, 带时间戳(!或=开头)
@092345/4903.50n/07201.75w>088/036 带时间戳, 带消息, 本地时间, 航向/速度.
@234517h4903.50n/07201.75w>phg5132 带时间戳, 带消息, 时分秒时间, phg.
@092345z4903.50n/07201.75w>rng0050 带时间戳, 带消息, 标准时间, 电台范围
/234517h4903.50n/07201.75w>dfs2360 带时间戳, 无消息, 时分秒时间, df,
@092345z4903.50n/07201.75w_090/000g000t066r000p000...dui 天气预报
首次位置信标头
[ gird locator ] comment
bytes: 1 4-6字符 1 n字符
[io91sx] 35 miles nnw of london
[io91]
nmea 数据流位置报告格式
$ !-,!-,!-,!-,!-,!-,!
bytes:1 25-209个字符
$gpgga,102705,5157.9762,n,00029.3256,w,1,04,2.0,75.7,m,47.6,m,,*62
$gpgll,2554.459,n,08020.187,w,154027.281,a
$gprmc,063909,a,3349.4302,n,11700.3721,w,43.022,89.3,291099,13.6,e*52
$gpvtg,318.7,t,,m,35.1,n,65.0,k*69
ax.25 送出一条源路径头的c演示代码(ax.25 ui-frame format的具体实现):
/******************************************************************************/
extern void ax25sendheader(void)
/*******************************************************************************
* abstract: this function keys the transmitter, sends the source and
* destination address, and gets ready to send the actual data.
*
* input: none
* output: none
* return: none
*/
{
static unsigned char loop_delay;
crc = 0xffff; // initialize the crc register
// transmit the flag field to begin the ui-frame
// adjust length for txdelay (each one takes 6.7ms)
for (loop_delay = 0 ; loop_delay < txdelay ; loop_delay++)
{
(ax25sendbyte(0x7e)); //千万别忘了这个标记
}
/* * * * this is where the callsigns are determined * * *
each callsign character is shifted to use the high seven bits of the byte.
use the table below to determine the hex values for these characters.
for callsigns less than six digits, pad the end of the callsign with spaces.
for all bytes in header, except for the very last byte, bit0 must be clear.
yes, this means only the very last byte has bit0 set to 1 in the station id!
callsign byte lookup table
-----------------------------------------------------------
| letters: | numbers and ssid's: |
| a = 0x82 n = 0x9c | 0 = 0x60 8 = 0x70 |
| b = 0x84 o = 0x9e | 1 = 0x62 9 = 0x72 |
| c = 0x86 p = 0xa0 | 2 = 0x64 10 = 0x74 |
| d = 0x88 q = 0xa2 | 3 = 0x66 11 = 0x76 |
| e = 0x8a r = 0xa4 | 4 = 0x68 12 = 0x78 |
| f = 0x8c s = 0xa6 | 5 = 0x6a 13 = 0x7a |
| g = 0x8e t = 0xa8 | 6 = 0x6c 14 = 0x7c |
| h = 0x90 u = 0xaa | 7 = 0x6e 15 = 0x7e |
| i = 0x92 v = 0xac | space = 0x40 |
| j = 0x94 w = 0xae | |
| k = 0x96 x = 0xb0 | remember! set bit0 in |
| l = 0x98 y = 0xb2 | the last ssid to one! |
| m = 0x9a z = 0xb4 | |
-----------------------------------------------------------
end of lookup table */
/* to save on flash, the following was moved into eeprom
// begin transmission of packet destination address (apxxxx0)
ax25sendbyte(0x82); // a
ax25sendbyte(0xa0); // p
ax25sendbyte(0x82); // a
ax25sendbyte(0xac); // v
ax25sendbyte(0xa4); // r
ax25sendbyte(0x60); // 0
ax25sendbyte(0x60); // ssid=0
// begin transmission of packet source address
ax25sendbyte(0x84); // byte 1 (b)
ax25sendbyte(0x8e); // byte 2 (g)
ax25sendbyte(0x6a); // byte 3 (5)
ax25sendbyte(0x8a); // byte 4 (e)
ax25sendbyte(0xa0); // byte 5 (p)
ax25sendbyte(0x8a); // byte 6 (e)
ax25sendbyte(0x76); // station id (11)
ax25sendbyte(0xa4); // byte 1 (r)
ax25sendbyte(0x8a); // byte 2 (e)
ax25sendbyte(0x98); // byte 3 (l)
ax25sendbyte(0x82); // byte 4 (a)
ax25sendbyte(0xb2); // byte 5 (y)
ax25sendbyte(0x40); // byte 6 (space)
ax25sendbyte(0x60); // station id (0)
ax25sendbyte(0xae); // byte 1 (w)
ax25sendbyte(0x92); // byte 2 (i)
ax25sendbyte(0x88); // byte 3 (d)
ax25sendbyte(0x8a); // byte 4 (e)
ax25sendbyte(0x64); // byte 5 (2)
ax25sendbyte(0x40); // byte 6 (space)
ax25sendbyte(0x65); // station id (2)
// finish out the header with two more bytes
ax25sendbyte(0x03); // control field - 0x03 is aprs ui-frame
ax25sendbyte(0xf0); // protocol id - 0xf0 is no layer 3
*/
// ax25sendeepromstring(0); // send the header for use on 144.39 mhz
ax25sendeepromstring(31); // trimmed header for use in 144.34 mhz
return;
} // end ax25sendheader(void)