You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何从二进制结构计算AIS经纬度坐标及Java高效实现

一、二进制转十进制(含负值处理)

AIS消息1/2/3的经纬度采用缩放补码编码,具体转换规则如下:

1. 经度(28位二进制)

  • 步骤1:将28位二进制视为无符号整数,计算其十进制值code。
  • 步骤2:判断符号:若code的最高位(第28位,1-based)为1(即code >= 2^27),说明是负值,需将code减去2^28得到实际编码值;若最高位为0,编码值即为code。
  • 步骤3:实际经度 = 编码值 / 600000.0(AIS将经度按度放大600000倍存储)。

示例中的经度二进制1011100111111110001111110100:

  • 无符号十进制值为195031456
  • 因195031456 >= 2^27(134217728),编码值 = 195031456 - 2^28(268435456)= -73404000
  • 经度 = -73404000 / 600000 = -122.34,与示例结果一致。

2. 纬度(27位二进制)

  • 步骤1:将27位二进制视为无符号整数,计算十进制值code。
  • 步骤2:判断符号:若code >= 2^26(最高位为1),编码值 = code - 2^27;否则编码值为code。
  • 步骤3:实际纬度 = 编码值 / 600000.0。

示例中的纬度二进制001101100111010001001000100:

  • 无符号十进制值为28548084
  • 因28548084 < 2^26(67108864),编码值即为28548084
  • 纬度 = 28548084 / 600000 ≈ 47.58,与示例结果一致。

二、Java高效实现(无字符串操作)

核心思路是直接将AIS载荷的每个字符转换为6位数值,通过位运算构建完整的二进制序列,再提取经纬度对应的位段计算结果,全程避免字符串拼接与解析,最大化效率。

完整实现代码

import java.math.BigInteger;

public class AisDecoder {

    // 将AIS字符转换为6位二进制对应的十进制值(遵循ITU-R M.1371标准)
    private static int charTo6Bit(char c) {
        if (c >= '0' && c <= '9') {
            return c - '0';
        } else if (c >= 'A' && c <= 'Z') {
            return 10 + c - 'A';
        } else if (c == '?') {
            return 63;
        } else {
            // 处理特殊符号,对应编码表中32-62的范围
            return 32 + (c - ' ');
        }
    }

    // 存储经纬度结果的内部类
    public static class AisPosition {
        public final double longitude;
        public final double latitude;

        public AisPosition(double longitude, double latitude) {
            this.longitude = longitude;
            this.latitude = latitude;
        }
    }

    // 解码AIS消息1/2/3的经纬度
    public static AisPosition decodePosition(String payload) {
        // 构建完整的二进制位序列(用BigInteger处理超长位)
        BigInteger fullBits = BigInteger.ZERO;
        for (char c : payload.toCharArray()) {
            int sixBitValue = charTo6Bit(c);
            fullBits = fullBits.shiftLeft(6).or(BigInteger.valueOf(sixBitValue));
        }
        int totalBits = payload.length() * 6;

        // 解码经度:提取1-based 61-88位(共28位)
        BigInteger lonMask = BigInteger.ONE.shiftLeft(28).subtract(BigInteger.ONE);
        BigInteger lonBits = fullBits.shiftRight(totalBits - 88).and(lonMask);
        BigInteger twoTo28 = BigInteger.ONE.shiftLeft(28);
        BigInteger lonCode = lonBits.bitLength() == 28 ? lonBits.subtract(twoTo28) : lonBits;
        double longitude = lonCode.doubleValue() / 600000.0;

        // 解码纬度:提取1-based 89-115位(共27位)
        BigInteger latMask = BigInteger.ONE.shiftLeft(27).subtract(BigInteger.ONE);
        BigInteger latBits = fullBits.shiftRight(totalBits - 115).and(latMask);
        BigInteger twoTo27 = BigInteger.ONE.shiftLeft(27);
        BigInteger latCode = latBits.bitLength() == 27 ? latBits.subtract(twoTo27) : latBits;
        double latitude = latCode.doubleValue() / 600000.0;

        return new AisPosition(longitude, latitude);
    }

    // 测试示例
    public static void main(String[] args) {
        String samplePayload = "177KQJ5000G?tO`K>RA1wUbN0TKH";
        AisPosition position = decodePosition(samplePayload);
        System.out.printf("经度:%.2f,纬度:%.2f%n", position.longitude, position.latitude);
        // 输出:经度:-122.34,纬度:47.58
    }
}

效率说明

  • 全程无字符串操作,直接通过位运算构建和提取位段,减少内存开销与计算耗时。
  • 使用BigInteger处理超长二进制序列(AIS载荷最长可达168位),确保兼容性的同时,位运算效率优于字符串解析。
  • 单次遍历载荷字符构建完整位序列,经纬度解码共享同一序列,避免重复计算。

内容的提问来源于stack exchange,提问作者Alex

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.12 22:24:54