diff --git a/.idea/misc.xml b/.idea/misc.xml
index 71e2a77..10608e0 100644
--- a/.idea/misc.xml
+++ b/.idea/misc.xml
@@ -4,6 +4,15 @@
+
+
+
+
+
+
+
+
+
diff --git a/ApiDoc/allclasses-frame.html b/ApiDoc/allclasses-frame.html
new file mode 100644
index 0000000..54da0e9
--- /dev/null
+++ b/ApiDoc/allclasses-frame.html
@@ -0,0 +1,22 @@
+
+
+
+
+
+ getLastADASRecognizedResult();
+}
diff --git a/modules/mogo-realtime/src/main/java/com/mogo/realtime/constant/SnapshotUploadInTime.java b/modules/mogo-realtime/src/main/java/com/mogo/realtime/constant/SnapshotUploadInTime.java
new file mode 100644
index 0000000..b2ea607
--- /dev/null
+++ b/modules/mogo-realtime/src/main/java/com/mogo/realtime/constant/SnapshotUploadInTime.java
@@ -0,0 +1,129 @@
+package com.mogo.realtime.constant;
+
+import android.content.Context;
+import android.util.Log;
+
+import com.mogo.cloud.passport.MoGoAiCloudClient;
+import com.mogo.realtime.InterfaceManager.RealTimeApisHandler;
+import com.mogo.realtime.InterfaceManager.RealTimeServiceApis;
+import com.mogo.realtime.entity.ADASRecognizedResult;
+import com.mogo.realtime.entity.CloudLocationInfo;
+import com.mogo.realtime.location.LocationResult;
+import com.mogo.realtime.location.MogoRTKLocation;
+import com.mogo.realtime.util.MortonCode;
+import com.mogo.realtime.util.SimpleLocationCorrectStrategy;
+import com.mogo.realtime.websocket.OnePerSecondSendContent;
+
+import java.util.List;
+import java.util.logging.Logger;
+
+/**
+ * @author congtaowang
+ * @since 2020/12/14
+ *
+ * 实时上报坐标、识别物体
+ */
+public class SnapshotUploadInTime implements MogoRTKLocation.RTKLocationListener {
+
+ private static final String TAG = "SnapshotUploadInTime";
+
+ private static volatile SnapshotUploadInTime sInstance;
+ private Context mContext;
+
+ private SnapshotUploadInTime() {
+ }
+
+ public static SnapshotUploadInTime getInstance() {
+ if (sInstance == null) {
+ synchronized (SnapshotUploadInTime.class) {
+ if (sInstance == null) {
+ sInstance = new SnapshotUploadInTime();
+ }
+ }
+ }
+ return sInstance;
+ }
+
+ public synchronized void release() {
+ sInstance = null;
+ }
+
+ private Object readResolve() {
+ // 阻止反序列化,必须实现 Serializable 接口
+ return sInstance;
+ }
+
+ public void start(Context context) {
+ mContext = context.getApplicationContext();
+ MogoRTKLocation.getInstance().registerRTKLocationListener(this);
+ }
+
+ public void stop() {
+ MogoRTKLocation.getInstance().unregisterRTKLocationListener();
+ MogoRTKLocation.getInstance().stop();
+ }
+
+ @Override
+ public void onLocationChanged(List cloudLocationInfos) {
+ startSendCarLocationAndAdasRecognizedResult2Server(cloudLocationInfos);
+ }
+
+ private CloudLocationInfo mLastInfo;
+
+ private void startSendCarLocationAndAdasRecognizedResult2Server(List cloudLocationInfo) {
+ CloudLocationInfo lastInfo = null;
+ // 如果数组内容不为空,就用数组最后一个值
+ if (cloudLocationInfo != null && !cloudLocationInfo.isEmpty()) {
+ lastInfo = cloudLocationInfo.get(cloudLocationInfo.size() - 1);
+ mLastInfo = lastInfo;
+ }
+ if (lastInfo == null) {
+ lastInfo = mLastInfo;
+ }
+ LocationResult locationResult = null;
+ if (lastInfo != null) {
+ // 定位点预测纠偏
+ lastInfo = SimpleLocationCorrectStrategy.getInstance().correct(lastInfo);
+ locationResult = new LocationResult();
+ if (lastInfo != null) {
+ locationResult.lastCoordinate = lastInfo;
+ locationResult.mortonCode = MortonCode.wrapEncodeMorton(lastInfo.getLon(), lastInfo.getLat());
+ }
+// locationResult.coordinates = new ArrayList<>();
+ locationResult.sn = MoGoAiCloudClient.getInstance().getAiCloudClientConfig().getSn();
+// if ( cloudLocationInfo == null ) {
+// locationResult.coordinates.addAll( new ArrayList<>() );
+// } else {
+// locationResult.coordinates.addAll( cloudLocationInfo );
+// }
+ }
+ List recognizedResults = RealTimeApisHandler.getInstance().getApis().getRecognizedResultManager().getLastADASRecognizedResult();//外显接口返回
+ OnePerSecondSendContent content = new OnePerSecondSendContent();
+ content.self = locationResult;
+ content.adas = recognizedResults;
+
+ if (content.self == null &&
+ (content.adas == null || content.adas.isEmpty())) {
+ Log.d(TAG, "no information 2 sent");
+ return;
+ }
+
+ //备注
+ /*
+ 等钟超SocketManagerSDK
+ *
+ MarkerServiceHandler.getApis().getWebSocketManagerApi( mContext ).sendMsg( content, new IMogoOnWebSocketMessageListener() {
+ @Override
+ public WebSocketMsgType getDownLinkType() {
+ return null;
+ }
+
+ @Override
+ public WebSocketMsgType getUpLinkType() {
+ return WebSocketMsgType.MSG_TYPE_UPLINK_CAR_DATA;
+ }
+ } );
+
+ */
+ }
+}
diff --git a/modules/mogo-realtime/src/main/java/com/mogo/realtime/entity/ADASRecognizedResult.java b/modules/mogo-realtime/src/main/java/com/mogo/realtime/entity/ADASRecognizedResult.java
new file mode 100644
index 0000000..ab8a0b7
--- /dev/null
+++ b/modules/mogo-realtime/src/main/java/com/mogo/realtime/entity/ADASRecognizedResult.java
@@ -0,0 +1,72 @@
+package com.mogo.realtime.entity;
+
+public
+/**
+ * @author congtaowang
+ * @since 2020/10/25
+ *
+ * adas 识别物体参数
+ */
+class ADASRecognizedResult {
+
+ /**
+ * 识别物体类型
+ */
+ public int type;
+
+ /**
+ * 识别物体唯一标识
+ */
+ public String uuid;
+
+ /**
+ * 红绿灯颜色
+ */
+ public String color;
+
+ /**
+ *
+ */
+ public String carId;
+
+ /**
+ * 识别物体的纬度
+ */
+ public double lat;
+
+ /**
+ * 识别物体的经度
+ */
+ public double lon;
+
+ /**
+ * 朝向
+ */
+ public double heading;
+
+ /**
+ * 系统时间
+ */
+ public long systemTime;
+
+ /**
+ * 定位卫星时间
+ */
+ public long satelliteTime;
+
+ /**
+ * 海拔
+ */
+ public double alt;
+
+ /**
+ * 速度
+ */
+ public double speed;
+
+ /**
+ * 莫顿码
+ */
+ public long mortonCode;
+
+}
diff --git a/modules/mogo-realtime/src/main/java/com/mogo/realtime/entity/CloudLocationInfo.java b/modules/mogo-realtime/src/main/java/com/mogo/realtime/entity/CloudLocationInfo.java
new file mode 100644
index 0000000..ea09ba1
--- /dev/null
+++ b/modules/mogo-realtime/src/main/java/com/mogo/realtime/entity/CloudLocationInfo.java
@@ -0,0 +1,174 @@
+package com.mogo.realtime.entity;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import com.mogo.utils.CoordinateUtils;
+
+import java.util.Objects;
+
+/**
+ * 云端定位信息和自车定位信息
+ *
+ * @author tongchenfei
+ */
+public class CloudLocationInfo implements Parcelable {
+ private double lat;
+ private double lon;
+ private double heading;
+ private long systemTime;
+ private long satelliteTime;
+ private double alt;
+ private double speed;
+
+ public CloudLocationInfo() {
+ }
+
+ public CloudLocationInfo(CloudLocationInfo info ) {
+ this.lat = info.getLat();
+ this.lon = info.getLon();
+ this.heading = info.getHeading();
+ this.systemTime = System.currentTimeMillis();
+ this.satelliteTime = System.currentTimeMillis();
+ this.alt = info.alt;
+ this.speed = info.speed;
+ }
+
+ public void convertCoor2GCJ02(){
+ double[] amapCoord = CoordinateUtils.transformFromWGSToGCJ( lat, lon );
+ if ( amapCoord != null ) {
+ this.lat = amapCoord[0];
+ this.lon = amapCoord[1];
+ }
+ }
+
+ protected CloudLocationInfo(Parcel in ) {
+ lat = in.readDouble();
+ lon = in.readDouble();
+ heading = in.readDouble();
+ systemTime = in.readLong();
+ satelliteTime = in.readLong();
+ alt = in.readDouble();
+ speed = in.readDouble();
+ }
+
+ @Override
+ public void writeToParcel( Parcel dest, int flags ) {
+ dest.writeDouble( lat );
+ dest.writeDouble( lon );
+ dest.writeDouble( heading );
+ dest.writeLong( systemTime );
+ dest.writeLong( satelliteTime );
+ dest.writeDouble( alt );
+ dest.writeDouble( speed );
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ public static final Creator< CloudLocationInfo > CREATOR = new Creator< CloudLocationInfo >() {
+ @Override
+ public CloudLocationInfo createFromParcel( Parcel in ) {
+ return new CloudLocationInfo( in );
+ }
+
+ @Override
+ public CloudLocationInfo[] newArray( int size ) {
+ return new CloudLocationInfo[size];
+ }
+ };
+
+ public double getLat() {
+ return lat;
+ }
+
+ public void setLat( double lat ) {
+ this.lat = lat;
+ }
+
+ public double getLon() {
+ return lon;
+ }
+
+ public void setLon( double lon ) {
+ this.lon = lon;
+ }
+
+ public double getHeading() {
+ return heading;
+ }
+
+ public void setHeading( double heading ) {
+ this.heading = heading;
+ }
+
+ public long getSystemTime() {
+ return systemTime;
+ }
+
+ public void setSystemTime( long systemTime ) {
+ this.systemTime = systemTime;
+ }
+
+ public long getSatelliteTime() {
+ return satelliteTime;
+ }
+
+ public void setSatelliteTime( long satelliteTime ) {
+ this.satelliteTime = satelliteTime;
+ }
+
+ public double getAlt() {
+ return alt;
+ }
+
+ public void setAlt( double alt ) {
+ this.alt = alt;
+ }
+
+ public double getSpeed() {
+ return speed;
+ }
+
+ public void setSpeed( double speed ) {
+ this.speed = speed;
+ }
+
+ @Override
+ public String toString() {
+ return "CloudLocationInfo{" +
+ "lat=" + lat +
+ ", lon=" + lon +
+ ", heading=" + heading +
+ ", systemTime=" + systemTime +
+ ", satelliteTime=" + satelliteTime +
+ ", alt=" + alt +
+ ", speed=" + speed +
+ '}';
+ }
+
+ public String print() {
+ return "CloudLocation{ lon: " + lon + " lat: " + lat + " heading: " + heading + " speed: "
+ + speed + "}";
+ }
+
+ @Override
+ public boolean equals( Object o ) {
+ if ( this == o ) {
+ return true;
+ }
+ if ( o == null || getClass() != o.getClass() ) {
+ return false;
+ }
+ CloudLocationInfo that = ( CloudLocationInfo ) o;
+ return Double.compare( that.lat, lat ) == 0 &&
+ Double.compare( that.lon, lon ) == 0;
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash( lat, lon );
+ }
+}
diff --git a/modules/mogo-realtime/src/main/java/com/mogo/realtime/location/LocationResult.java b/modules/mogo-realtime/src/main/java/com/mogo/realtime/location/LocationResult.java
new file mode 100644
index 0000000..fb8ff9f
--- /dev/null
+++ b/modules/mogo-realtime/src/main/java/com/mogo/realtime/location/LocationResult.java
@@ -0,0 +1,35 @@
+package com.mogo.realtime.location;
+
+import com.mogo.realtime.entity.CloudLocationInfo;
+
+import java.util.List;
+
+public
+/**
+ * @author congtaowang
+ * @since 2020/10/25
+ *
+ * 自车定位信息
+ */
+class LocationResult {
+
+ /**
+ * sn
+ */
+ public String sn;
+
+ /**
+ * 最后一个定位点的莫顿码
+ */
+ public long mortonCode;
+
+ /**
+ * 最后一个定位点
+ */
+ public CloudLocationInfo lastCoordinate;
+
+ /**
+ * 1s 内的连续定位点
+ */
+ public List< CloudLocationInfo > coordinates;
+}
diff --git a/modules/mogo-realtime/src/main/java/com/mogo/realtime/location/MogoRTKLocation.java b/modules/mogo-realtime/src/main/java/com/mogo/realtime/location/MogoRTKLocation.java
new file mode 100644
index 0000000..68bcf03
--- /dev/null
+++ b/modules/mogo-realtime/src/main/java/com/mogo/realtime/location/MogoRTKLocation.java
@@ -0,0 +1,179 @@
+package com.mogo.realtime.location;
+
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.location.Criteria;
+import android.location.Location;
+import android.location.LocationListener;
+import android.location.LocationManager;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.Message;
+
+import com.mogo.realtime.entity.CloudLocationInfo;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.logging.Logger;
+
+public class MogoRTKLocation {
+
+ private static final String TAG = "MogoRTKLocation";
+ private static final int MSG_DATA_CHANGED = 0x100;
+ private static final long MSG_DATA_INTERNAL = 500L;
+
+ private Handler mHandler;
+ private LocationManager locationManager;
+ private RTKLocationListener rtkLocationListener;
+ private List cacheList = new ArrayList<>();
+
+ public static MogoRTKLocation getInstance() {
+ return RTKHolder.rtkLoc;
+ }
+
+ private static class RTKHolder {
+ private static final MogoRTKLocation rtkLoc = new MogoRTKLocation();
+ }
+
+ private MogoRTKLocation() {
+ mHandler = new Handler(WorkThreadHandler.newInstance( TAG ).getLooper() ) {
+ @Override
+ public void handleMessage(Message msg) {
+ super.handleMessage(msg);
+ if (msg.what == MSG_DATA_CHANGED) {
+ mHandler.sendEmptyMessageDelayed(MSG_DATA_CHANGED, uploadDelay);
+ sendLocationData();
+
+// Logger.d(TAG,"handleMessage开始发送消息");
+ }
+ }
+ };
+ mHandler.sendEmptyMessage(MSG_DATA_CHANGED);
+ Logger.d(TAG,"构造方法开始发送消息");
+ }
+
+ public interface RTKLocationListener {
+ void onLocationChanged(List cloudLocationInfos);
+ }
+
+ private void sendLocationData() {
+
+ if (rtkLocationListener != null) {
+ List list = new ArrayList<>(cacheList);
+ rtkLocationListener.onLocationChanged(list);
+ }
+ if (cacheList != null && cacheList.size() > 0) {
+ cacheList.clear();
+ }
+ }
+
+ public void registerRTKLocationListener(RTKLocationListener locationListener) {
+ rtkLocationListener = locationListener;
+ }
+
+ public void unregisterRTKLocationListener(){
+ rtkLocationListener = null;
+ }
+
+ public void init() {
+ locationManager = (LocationManager) AbsMogoApplication.getApp().getApplicationContext().getSystemService(Context.LOCATION_SERVICE);
+ String provider = locationManager.getBestProvider(getCriteria(), true);
+ Logger.d(TAG, "init provider : " + provider);
+ if (locationManager.isProviderEnabled(LocationManager.GPS_PROVIDER)) {
+ try {
+ locationManager.requestLocationUpdates(provider, 0, 0, locationListener);
+ Location location = locationManager.getLastKnownLocation(provider);
+ if (location != null) {
+ Logger.i(TAG, "location : " + location.toString());
+ }
+ } catch (Exception e) {
+ e.printStackTrace();
+ Logger.d(TAG, "RTK LocationManager requestLocationUpdates has Exception : " + e.getMessage());
+ }
+ } else {
+ Logger.d(TAG, "RTK LocationManager Provider GPS_PROVIDER unable");
+ }
+
+ // 注册修改上报间隔的广播, 临时使用,后面可直接干掉,发送广播的地方在EntranceFragment
+ IntentFilter filter = new IntentFilter("com.mogo.launcher.action.FIX_UPLOAT_DELAY");
+ AbsMogoApplication.getApp().registerReceiver(fixUploadDelayReceiver, filter);
+ }
+
+ private Criteria getCriteria() {
+ Criteria criteria = new Criteria();
+ criteria.setAccuracy(Criteria.ACCURACY_FINE); //高精
+ criteria.setAltitudeRequired(false);
+ criteria.setBearingRequired(true);
+ criteria.setSpeedRequired(true);
+ criteria.setPowerRequirement(Criteria.POWER_LOW);
+ return criteria;
+ }
+
+ private LocationListener locationListener = new LocationListener() {
+ @Override
+ public void onLocationChanged(Location location) {
+ if (location != null) {
+ CloudLocationInfo cloudLocationInfo = new CloudLocationInfo();
+ cloudLocationInfo.setAlt(location.getAltitude());
+ cloudLocationInfo.setHeading(location.getBearing());
+ cloudLocationInfo.setLat(location.getLatitude());
+ cloudLocationInfo.setLon(location.getLongitude());
+ cloudLocationInfo.setSpeed(location.getSpeed());
+ cloudLocationInfo.setSatelliteTime(location.getTime());
+ cloudLocationInfo.setSystemTime(System.currentTimeMillis());
+ cloudLocationInfo.convertCoor2GCJ02();
+ cacheList.add(cloudLocationInfo);
+ } else {
+ Logger.e(TAG, "location == null");
+ }
+ }
+
+ @Override
+ public void onStatusChanged(String provider, int status, Bundle extras) {
+ Logger.d(TAG, "onStatusChanged status: " + status);
+ }
+
+ @Override
+ public void onProviderEnabled(String provider) {
+ Logger.d(TAG, "onProviderEnabled");
+ }
+
+ @Override
+ public void onProviderDisabled(String provider) {
+ Logger.d(TAG, "onProviderEnabled");
+ }
+ };
+
+ public void stop() {
+ Logger.d(TAG, "stop RTK Location");
+ if (locationManager != null && locationListener != null) {
+ locationManager.removeUpdates(locationListener);
+ } else {
+ Logger.d(TAG, "stop failed , reason : loc" + locationManager + " , or loc listener: " + locationListener + " is null");
+ }
+ }
+
+ private long uploadDelay = MSG_DATA_INTERNAL;
+
+ private FixUploadDelayReceiver fixUploadDelayReceiver = new FixUploadDelayReceiver();
+
+ private class FixUploadDelayReceiver extends BroadcastReceiver{
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ uploadDelay = intent.getIntExtra("fixTime", 0);
+ }
+ }
+
+ /**
+ * 默认保持{@link #uploadDelay}间隔进行位置上报,如遇服务端控制,进行上报间隔修改
+ * @param delay 上报间隔
+ */
+ public void resetUploadDelay(long delay) {
+ if (mHandler != null && mHandler.hasMessages(MSG_DATA_CHANGED)) {
+ mHandler.removeMessages(MSG_DATA_CHANGED);
+ mHandler.sendEmptyMessageDelayed(MSG_DATA_CHANGED, delay);
+ }
+ }
+}
diff --git a/modules/mogo-realtime/src/main/java/com/mogo/realtime/util/MortonCode.java b/modules/mogo-realtime/src/main/java/com/mogo/realtime/util/MortonCode.java
new file mode 100644
index 0000000..90257fb
--- /dev/null
+++ b/modules/mogo-realtime/src/main/java/com/mogo/realtime/util/MortonCode.java
@@ -0,0 +1,149 @@
+package com.mogo.realtime.util;
+
+import java.text.DecimalFormat;
+
+/**
+ * 莫顿编码
+ *
+ * @author linyang
+ * @since 2020.07.09
+ */
+public class MortonCode {
+
+ /**
+ * morton 转 经纬度 时的中间常量
+ */
+ private static final long NDS_180_DEGREES = 0x7fffffff;
+
+ /**
+ * morton 转 经纬度 时的中间常量
+ */
+ private static final long NDS_360_DEGREES = 4294967295L;
+
+ /**
+ * morton 转 经纬度 时的中间常量
+ */
+ private static final long NDS_90_DEGREES = 0x3fffffff;
+
+ /**
+ * 经纬度转 morton 时的中间常量
+ */
+ private static final double RULE_MORTON = Math.pow( 2, 32 ) / 360;
+
+ /**
+ * morton 转 经纬度 时的中间常量
+ */
+ private static final double RULE_MORTON_TO_LONLAT = 360.0 / Math.pow( 2, 32 );
+
+ /**
+ * @param lon
+ * @param lat
+ * @return
+ */
+ public static long wrapEncodeMorton( Double lon, Double lat ) {
+ DecimalFormat decimalFormat = new DecimalFormat( "#.######" );
+ return encodeMorton( Double.valueOf( decimalFormat.format( lon ) ),
+ Double.valueOf( decimalFormat.format( lat ) ) );
+ }
+
+ /**
+ * 编码 morton code
+ *
+ * @param lon
+ * @param lat
+ * @return
+ */
+ public static long encodeMorton( Double lon, Double lat ) {
+
+ Long bit = 1L;
+ long mortonCode = 0L;
+ long x = ( long ) ( lon * RULE_MORTON );
+ long y = ( long ) ( lat * RULE_MORTON );
+
+ if ( y < 0 ) {
+ y += 0x7FFFFFFF;
+ }
+ y = y << 1;
+ for ( int i = 0; i < 32; i++ ) {
+ // x-part
+ mortonCode = mortonCode | ( x & bit );
+ x = x << 1;
+ bit = bit << 1;
+ // y-part
+ mortonCode = mortonCode | ( y & bit );
+ y = y << 1;
+ bit = bit << 1;
+ }
+
+ return mortonCode;
+ }
+
+ /**
+ * 将莫顿码解码为坐标
+ *
+ * @param mortonCode
+ * @return
+ */
+ public static double[] decodeMorton( long mortonCode ) {
+ long[] midPoint = mortonCodeToCoord( mortonCode );
+ normalizeCoord( midPoint );
+ double[] point = new double[2];
+
+ // 将经纬度长整数转化为 浮点类型
+ point[0] = midPoint[0] * RULE_MORTON_TO_LONLAT;
+ point[1] = midPoint[1] * RULE_MORTON_TO_LONLAT;
+ return point;
+ }
+
+ /**
+ * 莫顿码分别拆解为 编码后的经纬度长整数
+ *
+ * @param mortonCode
+ * @return
+ */
+ private static long[] mortonCodeToCoord( long mortonCode ) {
+ long bit = 1L;
+ long[] longPoint = new long[2];
+
+ for ( int i = 0; i < 32; i++ ) {
+ longPoint[0] |= mortonCode & bit;
+ mortonCode >>= 1;
+ longPoint[1] |= mortonCode & bit;
+ bit <<= 1;
+ }
+ return longPoint;
+ }
+
+ /**
+ * 对编码后的经纬度长整数进行解码
+ *
+ * @param midPoint
+ */
+ private static void normalizeCoord( long[] midPoint ) {
+ // if x > 180 degrees, then subtract 360 degrees
+ if ( midPoint[0] > NDS_180_DEGREES ) {
+ midPoint[0] -=
+ NDS_360_DEGREES + 1; // add 1 because 0 must be counted as well
+ } else if ( midPoint[0] < -NDS_180_DEGREES ) // if x < 180 , x += 360
+ {
+ midPoint[0] +=
+ NDS_360_DEGREES + 1; // add 1 because 0 must be counted as well
+ }
+
+ // if y > 90 degrees, then subtract 180 degrees
+ if ( midPoint[1] > NDS_90_DEGREES ) {
+ midPoint[1] -=
+ NDS_180_DEGREES + 1; // add 1 because 0 must be counted as well
+ } else if ( midPoint[1] < -NDS_90_DEGREES ) // if y < 90, y += 180
+ {
+ midPoint[1] +=
+ NDS_180_DEGREES + 1; // add 1 because 0 must be counted as well
+ }
+ return;
+ }
+
+
+ public static void main( String[] args ) {
+ System.out.println( encodeMorton( 116.39584, 39.98152 ) );
+ }
+}
diff --git a/modules/mogo-realtime/src/main/java/com/mogo/realtime/util/SimpleLocationCorrectStrategy.java b/modules/mogo-realtime/src/main/java/com/mogo/realtime/util/SimpleLocationCorrectStrategy.java
new file mode 100644
index 0000000..d1d3795
--- /dev/null
+++ b/modules/mogo-realtime/src/main/java/com/mogo/realtime/util/SimpleLocationCorrectStrategy.java
@@ -0,0 +1,257 @@
+package com.mogo.realtime.util;
+
+import android.os.SystemClock;
+
+import com.mogo.map.MogoLatLng;
+import com.mogo.module.common.MogoApisHandler;
+import com.mogo.module.common.entity.CloudLocationInfo;
+import com.mogo.realtime.entity.CloudLocationInfo;
+import com.mogo.utils.logger.Logger;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * 定位预测纠错策略
+ *
+ * @author tongchenfei
+ */
+public class SimpleLocationCorrectStrategy {
+ private static final String TAG = "SimpleLocationCorrectStrategy";
+ private static final int ERR_COUNT_THRESHOLD = 3;
+ /**
+ * 目标距离误差是10米,就是在原目标距离基础上增加10米
+ */
+ private static final float TARGET_DISTANCE_DEVIATION = 10;
+
+ private CloudLocationInfo lastLocation = null;
+ private long anchorTime;
+ private int errCount;
+
+ private static SimpleLocationCorrectStrategy instance = new SimpleLocationCorrectStrategy();
+
+ public static SimpleLocationCorrectStrategy getInstance(){
+ return instance;
+ }
+
+ private List historyList = new ArrayList<>();
+ private List validList = new ArrayList<>();
+ private List correctList = new ArrayList<>();
+ private List errList = new ArrayList<>();
+
+ public CloudLocationInfo correct(CloudLocationInfo info) {
+ Logger.d(TAG, "info: " + info.print());
+ if(isLocationValid(info)) {
+ if(recordLocation()) {
+ historyList.add(info);
+ }
+
+ if (lastLocation == null) {
+ lastLocation = info;
+ anchorTime = SystemClock.elapsedRealtime();
+ Logger.d(TAG, "第一条数据");
+ if(recordLocation()) {
+ validList.add(lastLocation);
+ }
+ return info;
+ }
+ if (lastLocation.equals(info)) {
+ Logger.d(TAG, "相同坐标点==");
+ return info;
+ }
+ try {
+ float targetDistance =
+ (float) (lastLocation.getSpeed() * (SystemClock.elapsedRealtime() - anchorTime) / 1000) + TARGET_DISTANCE_DEVIATION;
+ float distance = MogoApisHandler.getInstance().getApis().getMapServiceApi().getMapUIController().calculateLineDistance(LocationParseUtil.cloudLocationToMogoLatLng(lastLocation), LocationParseUtil.cloudLocationToMogoLatLng(info));
+ Logger.d(TAG,
+ "准备计算{ lastInfo: " + lastLocation.print() + " info: " + info.print() + " targetDistance: " + targetDistance + " distance : " + distance + "}");
+ if (distance <= targetDistance) {
+ // 新的定位点在目标距离范围内,认为此数据有效
+ lastLocation = info;
+ anchorTime = SystemClock.elapsedRealtime();
+ errCount = 0;
+ Logger.d(TAG, "在范围内,为有效点");
+ if(recordLocation()) {
+ validList.add(lastLocation);
+ }
+ return info;
+ } else {
+ // 出现异常点
+ if (errCount >= ERR_COUNT_THRESHOLD) {
+ // 出错次数超过阈值,认为本次出错点为正确点
+ if(recordLocation()) {
+ errList.add(new CloudLocationInfo(lastLocation));
+ correctList.add(info);
+ }
+ lastLocation = info;
+ anchorTime = SystemClock.elapsedRealtime();
+ errCount = 0;
+ Logger.d(TAG, "出错次数超限,异常点变有效点");
+ return info;
+ } else {
+ // 按照上一个点的方向和速度,计算下一个点的位置,下一个点除坐标点外,其余数据与上一个点相同
+ CloudLocationInfo nextInfo = new CloudLocationInfo(lastLocation);
+ MogoLatLng nextLatLon = computerThatLonLat(lastLocation.getLon(),
+ lastLocation.getLat(), lastLocation.getHeading(), targetDistance);
+ nextInfo.setLon(nextLatLon.lon);
+ nextInfo.setLat(nextLatLon.lat);
+ if(recordLocation()) {
+ errList.add(info);
+ correctList.add(nextInfo);
+ }
+ lastLocation = nextInfo;
+ anchorTime = SystemClock.elapsedRealtime();
+ errCount++;
+ Logger.d(TAG, "异常点纠偏 info: " + lastLocation);
+// return lastLocation;
+ if(recordLocation()) {
+ correctList.add(nextInfo);
+ }
+ return nextInfo;
+ }
+ }
+ } catch (Exception e) {
+ Logger.e(TAG, e, "纠偏异常");
+ e.printStackTrace();
+ }
+ }else{
+ Logger.d(TAG, "定位点异常");
+ if (lastLocation == null) {
+ return null;
+ }else{
+ try {
+ float targetDistance =
+ (float) (lastLocation.getSpeed() * (SystemClock.elapsedRealtime() - anchorTime) / 1000) + TARGET_DISTANCE_DEVIATION;
+ float distance = MogoApisHandler.getInstance().getApis().getMapServiceApi().getMapUIController().calculateLineDistance(LocationParseUtil.cloudLocationToMogoLatLng(lastLocation), LocationParseUtil.cloudLocationToMogoLatLng(info));
+ Logger.d(TAG,
+ "异常定位点\n准备计算{ lastInfo: " + lastLocation.print() + " info: " + info.print() + " targetDistance: " + targetDistance + " distance : " + distance + "}");
+ // 按照上一个点的方向和速度,计算下一个点的位置,下一个点除坐标点外,其余数据与上一个点相同
+ CloudLocationInfo nextInfo = new CloudLocationInfo(lastLocation);
+ MogoLatLng nextLatLon = computerThatLonLat(lastLocation.getLon(),
+ lastLocation.getLat(), lastLocation.getHeading(), targetDistance);
+ nextInfo.setLon(nextLatLon.lon);
+ nextInfo.setLat(nextLatLon.lat);
+ if(recordLocation()) {
+ errList.add(info);
+ correctList.add(nextInfo);
+ }
+ lastLocation = nextInfo;
+ anchorTime = SystemClock.elapsedRealtime();
+ errCount++;
+ Logger.d(TAG, "异常点纠偏 info: " + lastLocation);
+ if(recordLocation()) {
+ correctList.add(nextInfo);
+ }
+// return lastLocation;
+ return nextInfo;
+ }catch (Exception e){
+ Logger.e(TAG, e, "纠偏异常");
+ e.printStackTrace();
+ }
+ }
+ }
+ return null;
+ }
+
+ private boolean isLocationValid(CloudLocationInfo info) {
+ return info.getLat() != 0 && info.getLon() != 0;
+ }
+
+ private RecordLocationListener recordLocationListener = null;
+ private boolean hasCallbackReocrd = false;
+
+ public void setRecordLocationListener(RecordLocationListener recordLocationListener) {
+ this.recordLocationListener = recordLocationListener;
+ }
+
+ private boolean recordLocation(){
+ if (historyList.size() >= 100 && !hasCallbackReocrd && recordLocationListener != null) {
+ hasCallbackReocrd = true;
+ recordLocationListener.onRecordFinish(historyList, correctList,validList,correctList);
+ }
+ return historyList.size() < 100;
+ }
+
+
+ /**
+ * 根据距离和角度计算下一个经纬度
+ * 大地坐标系资料WGS-84 长半径a=6378137 短半径b=6356752.3142 扁率f=1/298.2572236
+ */
+ public MogoLatLng computerThatLonLat(double lon, double lat, double brng, double dist) {
+
+ double alpha1 = rad(brng);
+ double sinAlpha1 = Math.sin(alpha1);
+ double cosAlpha1 = Math.cos(alpha1);
+
+ // 扁率f=1/298.2572236
+ double f = 1 / 298.2572236;
+ double tanU1 = (1 - f) * Math.tan(rad(lat));
+ double cosU1 = 1 / Math.sqrt((1 + tanU1 * tanU1));
+ double sinU1 = tanU1 * cosU1;
+ double sigma1 = Math.atan2(tanU1, cosAlpha1);
+ double sinAlpha = cosU1 * sinAlpha1;
+ double cosSqAlpha = 1 - sinAlpha * sinAlpha;
+ // 长半径a=6378137
+ double a = 6378137;
+ // 短半径b=6356752.3142
+ double b = 6356752.3142;
+ double uSq = cosSqAlpha * (a * a - b * b) / (b * b);
+ double A = 1 + uSq / 16384 * (4096 + uSq * (-768 + uSq * (320 - 175 * uSq)));
+ double B = uSq / 1024 * (256 + uSq * (-128 + uSq * (74 - 47 * uSq)));
+
+ double cos2SigmaM=0;
+ double sinSigma=0;
+ double cosSigma=0;
+ double sigma = dist / (b * A), sigmaP = 2 * Math.PI;
+ while (Math.abs(sigma - sigmaP) > 1e-12) {
+ cos2SigmaM = Math.cos(2 * sigma1 + sigma);
+ sinSigma = Math.sin(sigma);
+ cosSigma = Math.cos(sigma);
+ double deltaSigma = B * sinSigma * (cos2SigmaM + B / 4 * (cosSigma * (-1 + 2 * cos2SigmaM * cos2SigmaM)
+ - B / 6 * cos2SigmaM * (-3 + 4 * sinSigma * sinSigma) * (-3 + 4 * cos2SigmaM * cos2SigmaM)));
+ sigmaP = sigma;
+ sigma = dist / (b * A) + deltaSigma;
+ }
+
+ double tmp = sinU1 * sinSigma - cosU1 * cosSigma * cosAlpha1;
+ double lat2 = Math.atan2(sinU1 * cosSigma + cosU1 * sinSigma * cosAlpha1,
+ (1 - f) * Math.sqrt(sinAlpha * sinAlpha + tmp * tmp));
+ double lambda = Math.atan2(sinSigma * sinAlpha1, cosU1 * cosSigma - sinU1 * sinSigma * cosAlpha1);
+ double C = f / 16 * cosSqAlpha * (4 + f * (4 - 3 * cosSqAlpha));
+ double L = lambda - (1 - C) * f * sinAlpha
+ * (sigma + C * sinSigma * (cos2SigmaM + C * cosSigma * (-1 + 2 * cos2SigmaM * cos2SigmaM)));
+
+ // final bearing
+ double revAz = Math.atan2(sinAlpha, -tmp);
+
+ System.out.println(revAz);
+ System.out.println(lon+deg(L)+","+deg(lat2));
+ return new MogoLatLng(deg(lat2), lon + deg(L));
+ }
+
+ /**
+ * 度换成弧度
+ *
+ * @param d
+ * 度
+ * @return 弧度
+ */
+ private double rad(double d) {
+ return d * Math.PI / 180.0;
+ }
+
+ /**
+ * 弧度换成度
+ *
+ * @param x
+ * 弧度
+ * @return 度
+ */
+ private double deg(double x) {
+ return x * 180 / Math.PI;
+ }
+
+ public interface RecordLocationListener{
+ void onRecordFinish(List history, List correct, List valid, List err);
+ }
+}
diff --git a/modules/mogo-realtime/src/main/java/com/mogo/realtime/websocket/LocationResult.java b/modules/mogo-realtime/src/main/java/com/mogo/realtime/websocket/LocationResult.java
new file mode 100644
index 0000000..25ed4f2
--- /dev/null
+++ b/modules/mogo-realtime/src/main/java/com/mogo/realtime/websocket/LocationResult.java
@@ -0,0 +1,36 @@
+package com.mogo.realtime.websocket;
+
+
+import com.mogo.realtime.entity.CloudLocationInfo;
+
+import java.util.List;
+
+public
+/**
+ * @author congtaowang
+ * @since 2020/10/25
+ *
+ * 自车定位信息
+ */
+class LocationResult {
+
+ /**
+ * sn
+ */
+ public String sn;
+
+ /**
+ * 最后一个定位点的莫顿码
+ */
+ public long mortonCode;
+
+ /**
+ * 最后一个定位点
+ */
+ public CloudLocationInfo lastCoordinate;
+
+ /**
+ * 1s 内的连续定位点
+ */
+ public List< CloudLocationInfo > coordinates;
+}
diff --git a/modules/mogo-realtime/src/main/java/com/mogo/realtime/websocket/OnePerSecondSendContent.java b/modules/mogo-realtime/src/main/java/com/mogo/realtime/websocket/OnePerSecondSendContent.java
new file mode 100644
index 0000000..5ec0a3a
--- /dev/null
+++ b/modules/mogo-realtime/src/main/java/com/mogo/realtime/websocket/OnePerSecondSendContent.java
@@ -0,0 +1,27 @@
+package com.mogo.realtime.websocket;
+
+
+import com.mogo.realtime.entity.ADASRecognizedResult;
+import com.mogo.realtime.location.LocationResult;
+
+import java.util.List;
+
+public
+/**
+ * @author congtaowang
+ * @since 2020/10/25
+ *
+ * 一秒一次的上行数据
+ */
+class OnePerSecondSendContent {
+
+ /**
+ * 自车定位点
+ */
+ public LocationResult self;
+
+ /**
+ * adas 识别物体:1s 识别到的最后帧
+ */
+ public List adas;
+}