题目类别: 实验
关键字: 使用对象
内容要求:
NMEA-0183协议是为了在不同的GPS(全球定位系统)导航设备中建立统一的BTCM(海事无线电技术委员会)标准,由美国国家海洋电子协会(NMEA-The National Marine Electronics Associa-tion)制定的一套通讯协议。GPS接收机根据NMEA-0183协议的标准规范,将位置、速度等信息通过串口传送到PC机、PDA等设备。
NMEA-0183协议是GPS接收机应当遵守的标准协议,也是目前GPS接收机上使用最广泛的协议,大多数常见的GPS接收机、GPS数据处理软件、导航软件都遵守或者至少兼容这个协议。
NMEA-0183协议定义的语句非常多,但是常用的或者说兼容性最广的语句只有 G P G G A 、 GPGGA、 GPGGA、GPGSA、 G P G S V 、 GPGSV、 GPGSV、GPRMC、 G P V T G 、 GPVTG、 GPVTG、GPGLL等。
其中$GPRMC语句的格式如下:
$GPRMC,024813.640,A,3158.4608,N,11848.3737,E,10.05,324.27,150706,A*50
这里整条语句是一个文本行,行中以逗号“,”隔开各个字段,每个字段的大小(长度)不一,这里的示例只是一种可能,并不能认为字段的大小就如上述例句一样。
字段0:$GPRMC,语句ID,表明该语句为Recommended Minimum Specific GPS/TRANSIT Data(RMC)推荐最小定位信息
字段1:UTC时间,hhmmss.sss格式
字段2:状态,A=定位,V=未定位
字段3:纬度ddmm.mmmm,度分格式(前导位数不足则补0)
字段4:纬度N(北纬)或S(南纬)
字段5:经度dddmm.mmmm,度分格式(前导位数不足则补0)
字段6:经度E(东经)或W(西经)
字段7:速度,节,Knots
字段8:方位角,度
字段9:UTC日期,DDMMYY格式
字段10:磁偏角,(000 - 180)度(前导位数不足则补0)
字段11:磁偏角方向,E=东W=西
字段16:校验值
这里,“*”为校验和识别符,其后面的两位数为校验和,代表了“$”和“*”之间所有字符(不包括这两个字符)的异或值的十六进制值。上面这条例句的校验和是十六进制的50,也就是十进制的80。
提示:^运算符的作用是异或。将$和*之间所有的字符做^运算(第一个字符和第二个字符异或,结果再和第三个字符异或,依此类推)之后的值对65536取余后的结果,应该和*后面的两个十六进制数字的值相等,否则的话说明这条语句在传输中发生了错误。注意这个十六进制值中是会出现A-F的大写字母的。另外,如果你需要的话,可以用Integer.parseInt(s)从String变量s中得到其所表达的整数数字;而Integer.parseInt(s, 16)从String变量s中得到其所表达的十六进制数字
现在,你的程序要读入一系列GPS输出,其中包含$GPRMC,也包含其他语句。在数据的最后,有一行单独的
END
表示数据的结束。
你的程序要从中找出$GPRMC语句,计算校验和,找出其中校验正确,并且字段2表示已定位的语句,从中计算出时间,换算成北京时间。一次数据中会包含多条$GPRMC语句,以最后一条语句得到的北京时间作为结果输出。
你的程序一定会读到一条有效的$GPRMC语句。
输入格式:
多条GPS语句,每条均以回车换行结束。最后一行是END三个大写字母。
输出格式:
6位数时间,表达为:
hh:mm:ss
其中,hh是两位数的小时,不足两位时前面补0;mm是两位数的分钟,不足两位时前面补0;ss是两位数的秒,不足两位时前面补0。
输入样例:
$GPRMC,024813.640,A,3158.4608,N,11848.3737,E,10.05,324.27,150706,A*50
END
输出样例:
10:48:13
备注
打包为可以执行的JAR文档,其中要包含源程序文件。
实现代码:
Data类:
package zg;
/**
* @author zg
*/
public class Data {
/**
* 语句ID,表明该语句为Recommended Minimum Specific GPS/TRANSIT Data(RMC)推荐最小定位信息
*/
private String id;
/**
* UTC时间,hhmmss.sss格式
*/
private String utc;
/**
* 校验值
*/
private String code;
/**
* 无参构造
*/
public Data() {
}
/**
* 全参构造
* @param id 语句ID
* @param utc UTC时间
* @param code 校验值
*/
public Data(String id, String utc, String code) {
this.id = id;
this.utc = utc;
this.code = code;
}
/**
*访问器和修改器
*/
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getUtc() {
return utc;
}
public void setUtc(String utc) {
this.utc = utc;
}
public String getCode() {
return code;
}
public void setCode(String code) {
this.code = code;
}
}
主类:
package zg;
import java.util.Scanner;
/**
* @author zg
*/
public class Main {
private static final String END="END";
private static final char SIGN=',';
private static final char SIGN2='*';
private static final String ID ="$GPRMC";
public static void main(String[] args) {
String[] strings=getValue();
// 如果一次数据中包含多条$GPRMC语句,以最后一条语句得到的北京时间作为结果输出
for(int i=strings.length-1;i>=0;i--){
if(strings[i]!=null){
System.out.println(strings[i]);
break;
}
}
}
/**
*
* @return 获取有效$GPRMC语句的集合
*/
public static String[] getValue(){
// $GPRMC语句的下标
int index=0;
// 存储多条$GPRMC语句,容量为100
String[] strings=new String[100];
Scanner sc = new Scanner(System.in);
// 读取GPS语句
String s=sc.next();
// 最后一行是END三个大写字母,读取到END就结束输入
while(!END.equals(s)){
// 针对该程序要求,只有三个字段有作用:语句ID、UTC时间和校验值
String[] string=new String[3];
char[] chars = s.toCharArray();
int j=0;string[j]="";
// flag是读完前两个字段的标记
boolean flag=true;
// start是开始读取最后的校验码的标记
boolean start=false;
for (char c : chars) {
if(j>1){
flag=false;
}
// 以','分隔字段
if (c!=SIGN && flag) {
string[j] += c;
}else if(flag){
j++;
string[j]="";
}
if(start){
string[j]+=c;
}
if(c==SIGN2){
start=true;
}
}
// 创建GPS语句对象
Data data=create(string);
// 语句ID为$GPRMC
if(judge(data,s)){
// 获取utc字段,格式为hhmmss.sss,因而只需取前六个utc时间转换为utf-8时间即可
String utc = data.getUtc();
// hh
String hour="";hour+=utc.charAt(0);hour+=utc.charAt(1);
// mm
String minute="";minute+=utc.charAt(2);minute+=utc.charAt(3);
// ss
String second="";second+=utc.charAt(4);second+=utc.charAt(5);
// utc时间转换为utf-8时间(北京时间为东八区时间,即utc+8)
int hourChina=(Integer.parseInt(hour)+8)%24;
// 转换为hh:mm:ss的格式
String ans="";
// hh是两位数的小时,不足两位时前面补0;mm是两位数的分钟,不足两位时前面补0;ss是两位数的秒,不足两位时前面补0
// 因为mm和ss的格式与实验要求的格式一致,所以不需要改动
ans+=String.format("%02d",hourChina);ans+=":";
ans+=minute;ans+=":";
ans+=second;
strings[index]="";
strings[index++]+=ans;
}
s=sc.next();
}
sc.close();
return strings;
}
/**
*
* @param strings GPS语句字段集
* @return GPS对象
*/
public static Data create(String[] strings){
Data data=new Data();
data.setId(strings[0]);
data.setUtc(strings[1]);
data.setCode(strings[2]);
return data;
}
/**
*
* @param data GPS对象
* @param s 输入的GPS语句
* @return 判断GPS语句是否是$GPRMC语句,如果是,并校验语句的校验码是否正确
*/
public static boolean judge(Data data,String s){
// 判断GPS语句是否是$GPRMC语句
if(!data.getId().equals(ID)){
return false;
}
char[] a= s.toCharArray();
int i=0,sum=0;
// start为开始^运算的标记
boolean start=false;
// SING2为'*',结束运算
while(a[i]!=SIGN2){
if(start){
// 将$和*之间所有的字符做^运算(不包括这两个字符)
sum^=a[i];
// 值对65536取余
sum%=65536;
}
if(a[i++]=='$'){
start=true;
}
}
// 把ans转换为16进制,因为校验码为16进制
String ans=Integer.toHexString(sum);
// 校验语句的校验码是否正确
return ans.equals(data.getCode());
}
}
/*
样例一:
输入:
$GPRMC,024813.640,A,3158.4608,N,11848.3737,E,10.05,324.27,150706,,,A*50
END
输出:
10:48:13
样例二:
输入:
$GPRMC,020803.640,A,3158.4608,N,11848.3737,E,10.05,324.27,150706,,,A*55
END
输出:
10:08:03
*/