最近有点累,一直在摸鱼,想在平差方面做点事情,但是一直在构思应该怎么去做,有想法的朋友可以交流。
收到了TW和国内顶尖测绘行业朋友的需求:
1、将NtripShare Cloud的CORS服务模块由单基站模块改为VRS。
2、由于电离层影响,基于VRS的CORS服务出现大面积的不能固定,需要使用单基站的网络CORS服务,但是由于CORS站本身的特殊性,不能对外暴扣精确的基准站坐标,能否在CORS软件中将单基站播发的RTCM中的基准站坐标进行编辑修改(测量结果需要后处理)。
其中VRS这件事有很多朋友提过,一直以来也没有做,主要考虑一下三点:
1、友商的VRS产品很多,没有必要再去做一款,已经很卷了。
2、VRS需要一定的技术难度,可以查找到的资料非常少。
3、NtripShare Cloud的CORS模块采用的是Java的技术路线,在应对大并发方面有一定优势,但是对于RTCM这种实时数据流解析与计算不是很适用。
RTCM基准站坐标编辑修改这事评估了一下可行,耗费四天时间,实现了在Java端的RTCM数据解析与1005、1006报文的实时修正。不得不说关于GNSS方面的java开源项目实在是太少了,真的只能手撸。这次的代码开放一部分,以便大家少走弯路。
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;import java.util.Arrays;public class Rtcm1005 extends RtcmMessage {private static Logger logger = LoggerFactory.getLogger(Rtcm1005.class);public double[] ecef = new double[3];public double[] lla = new double[3];public Rtcm1005(byte[] data, int index, int length) {super(data, index, length);}public Rtcm1005() {super(new byte[25], 0, 25);}public void setECEF(double x, double y, double z) {ecef[0] = x;ecef[1] = y;ecef[2] = z;lla = GeoCode.ecef2lla(ecef);}public void setLatLon(double lat, double lon, double alt) {lla[0] = lat;lla[1] = lon;lla[2] = alt;ecef = GeoCode.lla2ecef(lla);}@Overridepublic Rtcm1005 decode() {int bitPoint = HEADER_BYTE_LENGTH * 8;// DF002 Message NumbermessageType = Utilities.bitToInt(data, bitPoint, 12);bitPoint += 12;// DF003 Reference Station ID@SuppressWarnings("unused")int stationID = Utilities.bitToInt(data, bitPoint, 12);bitPoint += 12;// DF021 ITRF (not used)bitPoint += 6;// DF022/023/024/141bitPoint += 4;// DF025 Antenna Reference Point ECEF-Xecef[0] = Utilities.bitToLongMSB(data, bitPoint, 38) * 0.0001;bitPoint += 38;// DF142/001bitPoint += 2;// DF026 Antenna Reference Point ECEF-Yecef[1] = Utilities.bitToLongMSB(data, bitPoint, 38) * 0.0001;bitPoint += 38;// DF364bitPoint += 2;// DF027 Antenna Reference Point ECEF-Yecef[2] = Utilities.bitToLongMSB(data, bitPoint, 38) * 0.0001;
// bitPoint += 38;lla = GeoCode.ecef2lla(ecef);return this;}@Overridepublic String getSuccinctInfo() {return "ECEF\t" + Arrays.toString(ecef) + "\r\nLLA\t" + Arrays.toString(lla);}@Overridepublic String getAnalyzedString() {return "Stationary RTK Reference Station ARP\r\n" +"ECEF(X) " + ecef[0] + ", ECEF(Y) " + ecef[1] + ", ECEF(Z) " + ecef[2] + "\r\n";}public byte[] recode() {ecef = GeoCode.lla2ecef(lla);int i = 24;
// Utilities.setbitu(datass, i, 12, 1005);i += 12; /* message no */
// Utilities.setbitu(datass, i, 12, 0);i += 12; /* ref station id */
// Utilities.setbitu(datass, i, 6, 0);i += 6; /* itrf realization year */
// Utilities.setbitu(datass, i, 1, 1);i += 1; /* gps indicator */
// Utilities.setbitu(datass, i, 1, 1);i += 1; /* glonass indicator */
// Utilities.setbitu(datass, i, 1, 0);i += 1; /* galileo indicator */
// Utilities.setbitu(datass, i, 1, 0);i += 1; /* ref station indicator */
// logger.info("--"+Utilities.getBinaryStrFromByte2( data,i,38));Utilities.longToBitMSB(data, i, ecef[0] / 0.0001);
// logger.info("AA"+Utilities.getBinaryStrFromByte2( data,i,38));i += 38; /* antenna ref point ecef-y */
// Utilities.setbitu(datass, i, 1, 1);i += 1; /* oscillator indicator */
// Utilities.setbitu(datass, i, 1, 1);i += 1; /* reserved */Utilities.longToBitMSB(data, i, ecef[1] / 0.0001);
// logger.info("BB"+Utilities.getBinaryStrFromByte( data));i += 38; /* antenna ref point ecef-y */
// Utilities.setbitu(datass, i, 2, 0);i += 2; /* quarter cycle indicator */Utilities.longToBitMSB(data, i, ecef[2] / 0.0001);
// logger.info("CC"+Utilities.getBinaryStrFromByte( data));i += 38; /* antenna ref point ecef-z */int crc = 0;for (int m = 0; m < data.length - DecodeRTCM3.CRCBYTELENGTH; m++) {crc ^= data[0 + m] << 16;for (int j = 0; j < 8; j++) {crc <<= 1;if ((crc & 0x1000000) != 0) {crc ^= DecodeRTCM3.CRC24_POLY;}}}Utilities.intToBit(data, (data.length - DecodeRTCM3.CRCBYTELENGTH) * 8, DecodeRTCM3.CRCBYTELENGTH * 8, crc);return data;}@Overridepublic byte[] encode() {ecef = GeoCode.lla2ecef(lla);data = Utilities.hexToByteArray("d300133ed00003fab70dd5be0a44a89893094e19c0e5f16d7b");int i = 24;
// Utilities.setbitu(data, i, 12, 1005);i += 12; /* message no */
// Utilities.setbitu(data, i, 12, 0);i += 12; /* ref station id */
// Utilities.setbitu(data, i, 6, 0);i += 6; /* itrf realization year */
// Utilities.setbitu(data, i, 1, 1);i += 1; /* gps indicator */
// Utilities.setbitu(data, i, 1, 1);i += 1; /* glonass indicator */
// Utilities.setbitu(data, i, 1, 0);i += 1; /* galileo indicator */
// Utilities.setbitu(data, i, 1, 0);i += 1; /* ref station indicator */
// logger.info("--"+Utilities.getBinaryStrFromByte2( data,i,38));Utilities.longToBitMSB(data, i, ecef[0] / 0.0001);
// logger.info("AA"+Utilities.getBinaryStrFromByte2( data,i,38));i += 38; /* antenna ref point ecef-y */
// Utilities.setbitu(data, i, 1, 1);i += 1; /* oscillator indicator */
// Utilities.setbitu(data, i, 1, 1);i += 1; /* reserved */Utilities.longToBitMSB(data, i, ecef[1] / 0.0001);
// logger.info("BB"+Utilities.getBinaryStrFromByte( data));i += 38; /* antenna ref point ecef-y */
// Utilities.setbitu(data, i, 2, 0);i += 2; /* quarter cycle indicator */Utilities.longToBitMSB(data, i, ecef[2] / 0.0001);
// logger.info("CC"+Utilities.getBinaryStrFromByte( data));i += 38; /* antenna ref point ecef-z */int crc = 0;for (int m = 0; m < data.length - DecodeRTCM3.CRCBYTELENGTH; m++) {crc ^= data[0 + m] << 16;for (int j = 0; j < 8; j++) {crc <<= 1;if ((crc & 0x1000000) != 0) {crc ^= DecodeRTCM3.CRC24_POLY;}}}Utilities.intToBit(data, (data.length - DecodeRTCM3.CRCBYTELENGTH) * 8, DecodeRTCM3.CRCBYTELENGTH * 8, crc);return data;}
}
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;public class RtcmMsm extends RtcmMessage {private static final String[] GPSSIGNALMAP = {"","","1C","1P","1W","","","","2C","2P","2W","","","","","2S","2L","2X","","","","","5I","5Q","5X","","","","","","1S","1L","1X"};private static final String[] GLONASSSIGNALMAP = {"","","1C","1P","","","","","2C","2P"};private static final String[] GALILEOSIGNALMAP = {"","","1C","1A","1B","1X","1Z","","6C","6A","6B","6X","6Z","","7I","7Q","7X","","8I","8Q","8X","","5I","5Q","5X"};private static final String[] SBASSINGALMAP = {"","","1C","","","","","","","","","","","","","","","","","","","","5I","5Q","5X"};private static final String[] QZSSIGNALMAP = {"","","1C","","","","","","","6S","6L","6X","","","","2S","2L","2X","","","","","5I","5Q","5X","","","","","","1S","1L","1X"};private static final String[] BEIDOUSIGNALMAP = {"","","2I","2Q","2X","","","","6I","6Q","6X","","","","7I","7Q","7X"};private static final String[][] SIGNALMAP = {GPSSIGNALMAP,GLONASSSIGNALMAP,GALILEOSIGNALMAP,SBASSINGALMAP,QZSSIGNALMAP,BEIDOUSIGNALMAP};private int msmType;private int gnssType;private List<Integer> satelliteList; private List<Integer> signalList; private long cellMask; private int epochTime;private List<Integer> ltiList;private List<Integer> cnrList;public RtcmMsm(byte[] data, int index, int length) {super(data, index, length);}@OverrideRtcmMsm decode() {int bitPoint = decodeHeader();ltiList = new ArrayList<>();cnrList = new ArrayList<>();/** satellite Data*/int satelliteNumber = satelliteList.size();if(msmType == 4 || msmType == 5 || msmType == 6 || msmType == 7) {// DF397 8bits*NsatbitPoint += 8 * satelliteNumber;}if(msmType == 5 || msmType == 7) {// Extended Satellite Information 4bits*NsatbitPoint += 4 * satelliteNumber;}// DF398 10bits*NsatbitPoint += 10 * satelliteNumber;if(msmType == 5 || msmType == 7) {// DF399 14bits*NsatbitPoint += 14 * satelliteNumber;}/** signal Data*/int cellNumber = Long.bitCount(cellMask);if(msmType == 1 || msmType == 3 || msmType == 4 || msmType == 5) {// DF400 signal fine pseudorangebitPoint += 15 * cellNumber;}if(msmType == 2 || msmType == 3 || msmType == 4 || msmType == 5) {// DF401 signal fine phaserange databitPoint += 22 * cellNumber;// DF402 phaserange Lock Time Indicatorfor(int i = 0; i < cellNumber; i++) {ltiList.add(Utilities.bitToInt(data, bitPoint, 4));bitPoint += 4;}// DF420 half-cycle ambiguity indicatorbitPoint += cellNumber;}if(msmType == 4 || msmType == 5) {// DF403 signal CNRsfor(int i = 0; i < cellNumber; i++) {cnrList.add(Utilities.bitToInt(data, bitPoint, 6));bitPoint += 6;}}if(msmType == 5) {// DF404 signal fine phaserangeratebitPoint += 15 * cellNumber;}if(msmType == 6 || msmType == 7) {// DF405 signal fine pseudorangesbitPoint += 20 * cellNumber;// DF406 signal fine PhaserangebitPoint += 24 * cellNumber;// DF407 Phaserange Lock Time Indicatorfor(int i = 0; i < cellNumber; i++) {ltiList.add(Utilities.bitToInt(data, bitPoint, 10));bitPoint += 10;}// DF420 Half-cycle ambiguity indicatorbitPoint += cellNumber;// DF408 signal CNR for(int i = 0; i < cellNumber; i++) {cnrList.add((int)(Utilities.bitToInt(data, bitPoint, 10) * Math.pow(2, -4)));bitPoint += 10;}}// if(msmType == 5 || msmType == 7) {// // DF404 signal fine PhaseRangeRates// bitPoint += 15 * cellNumber;// }return this;}private int decodeHeader() {int bitPoint = HEADER_BYTE_LENGTH * 8;// DF002 Message NumbermessageType = Utilities.bitToInt(data, bitPoint, 12);bitPoint += 12;classifyMSM();// DF003 Reference Station ID@SuppressWarnings("unused")int stationID = Utilities.bitToInt(data, bitPoint, 12);bitPoint += 12;// GNSS Epoch Timeif(gnssType == GeoCode.GNSSGLONASS) {@SuppressWarnings("unused")int dow = Utilities.bitToInt(data, bitPoint, 3);epochTime = Utilities.bitToInt(data, bitPoint + 3, 27);} else {epochTime = Utilities.bitToInt(data, bitPoint, 30);}bitPoint += 30;// MMB:1, IODS:3, Rsv:7, CSI:2, ECI:2, GDSI:1, GSI:3bitPoint += 19;// DF394 satellite MasksatelliteList = getBitMask(data, bitPoint, 64);bitPoint += 64;// DF395 signal MasksignalList = getBitMask(data, bitPoint, 32);bitPoint += 32;// DF396 cell MaskcellMask = Utilities.bitToLong(data, bitPoint, satelliteList.size() * signalList.size());bitPoint += satelliteList.size() * signalList.size();return bitPoint;}private List<Integer> getCombinedCellList(List<Integer> target) {List<Integer> list = new ArrayList<>();int cellLength = satelliteList.size() * signalList.size();for(int i = 0, counter = 0; i < satelliteList.size() * signalList.size(); i++) {if(((cellMask >> --cellLength) & 0x01) == 1) {list.add(target.get(counter++));} else {list.add(0);}}return list;}private void classifyMSM() {if(messageType >= 1071 && messageType <= 1077) {msmType = messageType - 1070; gnssType = GeoCode.GNSSGPS;} else if(messageType >= 1081 && messageType <= 1087) {msmType = messageType - 1080; gnssType = GeoCode.GNSSGLONASS;} else if(messageType >= 1091 && messageType <= 1097) {msmType = messageType - 1090; gnssType = GeoCode.GNSSGALILEO;} else if(messageType >= 1101 && messageType <= 1107) {msmType = messageType - 1100; gnssType = GeoCode.GNSSSBAS;} else if(messageType >= 1111 && messageType <= 1117) {msmType = messageType - 1110; gnssType = GeoCode.GNSSQZSS;} else if(messageType >= 1121 && messageType <= 1127) {msmType = messageType - 1120; gnssType = GeoCode.GNSSBEIDOU;}}private List<String> getPRNList() {int weight;switch(gnssType) {case GeoCode.GNSSSBAS:weight = 119;break;case GeoCode.GNSSQZSS:weight = 192;break;default:weight = 0;}return satelliteList.stream().map(Object -> Object + weight).map(Object::toString).collect(Collectors.toList());}private List<Integer> getBitMask(byte[] src, int bitPosition, int bitLength) {List<Integer> list = new ArrayList<>();int j = 0;for(int i = bitPosition; i < bitPosition + bitLength; i++) {++j;if(((src[i / 8] >> (7 - i % 8)) & 0x01) == 1) {list.add(j);}}return list;}private String printTable(List<Integer> data) {return GeoCode.print2DimensionTable(getPRNList(), getSignalStringList(), data);}@OverrideString getSuccinctInfo() {return GeoCode.getGnssName(gnssType) + " MSM" + msmType +"\r\n" +"Satellite: " + getPRNList().size() +", Signal: " + getSignalStringList().size();}@OverrideString getAnalyzedString() {StringBuilder sb = new StringBuilder();sb.append(GeoCode.getGnssName(gnssType)).append(" MSM").append(msmType).append("\r\n");sb.append("epochtime: ").append(epochTime).append("\r\n");sb.append("satellite: ").append(getPRNList()).append("\r\n");sb.append("signal: ").append(getSignalStringList()).append("\r\n");if(ltiList.size() > 0) {sb.append("Lock Time Indicator\r\n").append(printTable(getCombinedCellList(ltiList))).append("\r\n");}if(cnrList.size() > 0) {sb.append("CNR\r\n").append(printTable(getCombinedCellList(cnrList))).append("\r\n");}return sb.toString();}@Overridepublic byte[] encode() {byte [] bytes = new byte[25];return bytes;}@SuppressWarnings("unused")private String getConvertedEpocTime() {return null;}private List<String> getSignalStringList() {List<String> list = new ArrayList<>();try {for (Integer integer : signalList) {list.add(SIGNALMAP[gnssType][integer]);}return list;}catch (Exception e){}return list;}// GetterList<Integer> getSatelliteList() {return satelliteList;}List<Integer> getSignalList() {return signalList;}int getGnssType() {return gnssType;}int getMsmType() {return msmType;}int getEpochTime() {return epochTime;}List<Integer> getLtiList() {return ltiList;}List<Integer> getCnrList() {return cnrList;}
}
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;import static java.lang.Math.floor;public class Utilities {private static Logger logger = LoggerFactory.getLogger(Utilities.class);public static final int INDEX_NOT_FOUND = -1;public static int indexOf(final byte[] dstArray, final byte[] toFind) {return indexOf(dstArray, dstArray.length, toFind, 0);}public static int indexOf(final byte[] dstArray, int dstArrayLength, final byte[] toFind, int startIndex) {if (dstArray == null || toFind == null) {return INDEX_NOT_FOUND;}if (startIndex < 0) {startIndex = 0;}if (dstArrayLength > dstArray.length) {dstArrayLength = dstArray.length;}for (int i = startIndex; i < dstArrayLength - toFind.length + 1; i++) {boolean found = true;for (int j = 0; j < toFind.length; j++) {if (dstArray[i + j] != toFind[j]) {found = false;break;}}if (found) {return i;}}return INDEX_NOT_FOUND;}public static String bytesToHex(byte[] bytes) {StringBuffer sb = new StringBuffer();for (int i = 0; i < bytes.length; i++) {String hex = Integer.toHexString(bytes[i] & 0xFF);if (hex.length() < 2) {sb.append(0);}sb.append(hex);}return sb.toString();}public static byte[] hexToByteArray(String inHex) {int hexlen = inHex.length();byte[] result;if (hexlen % 2 == 1) {hexlen++;result = new byte[hexlen / 2];inHex = "0" + inHex;} else {result = new byte[hexlen / 2];}int j = 0;for (int i = 0; i < hexlen; i += 2) {result[j] = hexToByte(inHex.substring(i, i + 2));j++;}return result;}public static byte hexToByte(String inHex) {return (byte) Integer.parseInt(inHex, 16);}public static void set38bits(byte[] buff, int pos, double value) {int word_h = (int) (floor(value / 64.0));int word_l = (int) (value - word_h * 64.0);setbits(buff, pos, 32, word_h);setbitu(buff, pos + 32, 6, word_l);}public static int bitToInt(final byte[] data, int bitPosition, int bitLength) {if (data == null || bitLength > 32) {return 0;}int sum = 0;for (int i = bitPosition; i < bitPosition + bitLength; i++) {sum = (sum << 1) + ((data[i / 8] >> (7 - i % 8)) & 0x01);}return sum;}static void setbits(byte[] buff, int pos, int len, int data) {if (data < 0) {data |= 1 << (len - 1);byte[] dataaa = intToByteArray(data);logger.info(getBinaryStrFromByte(dataaa));} else {data &= ~(1 << (len - 1));} /* set sign bit */setbitu(buff, pos, len, data);logger.info(getBinaryStrFromByte2(buff, pos, len));}public static byte[] intToByteArray(int i) {byte[] result = new byte[4];result[0] = (byte) ((i >> 24) & 0xFF);result[1] = (byte) ((i >> 16) & 0xFF);result[2] = (byte) ((i >> 8) & 0xFF);result[3] = (byte) (i & 0xFF);return result;}public static byte[] LongToBytes(long values) {byte[] buffer = new byte[8];for (int i = 0; i < 8; i++) {int offset = 64 - (i + 1) * 8;buffer[i] = (byte) ((values >> offset) & 0xff);}return buffer;}public static void intToBit(byte[] buff, int pos, int len, int data) {int mask = 1 << (len - 1);int i;if (len <= 0 || 32 < len) return;for (i = pos; i < pos + len; i++, mask >>= 1) {if ((data & mask) != 0) {buff[i / 8] |= 1 << (7 - i % 8);} else {buff[i / 8] &= ~(1 << (7 - i % 8));}}}public static long bitToLong(byte[] data, int bitPosition, int bitLength) {if (data == null || bitLength > 64) {return 0;}long sum = 0;for (int i = bitPosition; i < bitPosition + bitLength; i++) {sum = (sum << 1) + ((data[i / 8] >> (7 - i % 8)) & 0x01);}return sum;}public static void setbitu(byte[] buff, int pos, int len, int data) {int mask = 1 << (len - 1);int i;if (len <= 0 || 32 < len) return;for (i = pos; i < pos + len; i++, mask >>= 1) {if ((data & mask) != 0) {buff[i / 8] |= 1 << (7 - i % 8);} else {buff[i / 8] &= ~(1 << (7 - i % 8));}}}public static long bitToLongMSB(byte[] data, int bitPosition, int bitLength) {if (data == null || bitLength > 64) {return 0;}long sum = bitToLong(data, bitPosition, bitLength);if (((data[bitPosition / 8] >> (7 - bitPosition % 8)) & 0x01) == 1) {long mask = -1L << bitLength;sum |= mask;}return sum;}public static void longToBitMSB(byte[] data, int bitPosition, double val) {long myData = (long) val;
// logger.info("--"+getBinaryStrFromByte2( LongToBytes(myData),64-38,38));
// if (val < 0) {
// myData |= 1 << (63);
// } else {
// myData &= ~(1 << 63);
// } /* set sign bit */
// logger.info("$$"+getBinaryStrFromByte2( data,bitPosition,38));
// logger.info("@@"+getBinaryStrFromByte2( LongToBytes(myData),64-38,38));
// logger.info(current+"$$"+getBinaryStrFromByte2( data,bitPosition,38));byte[] b = LongToBytes(myData);for (int m = 0; m < b.length; m++) {byte a = b[m];for (int i = 0; i < 8; i++) {byte c = a;a = (byte) (a >> 1);//每移一位如同将10进制数除以2并去掉余数。a = (byte) (a << 1);int current = m * 8 + i - (64 - 38) + bitPosition;if (current >= bitPosition) {if (a == c) {
// logger.info(current+"$$0");data[current / 8] &= ~(1 << current % 8);} else {
// logger.info(current+"$$1");data[current / 8] |= 1 << current % 8;}}a = (byte) (a >> 1);}}
// logger.info("@@"+getBinaryStrFromByte2( data,bitPosition,38));int word_h = (int) (floor(val / 64.0));int word_l = (int) (val - word_h * 64.0);setbitu(data, bitPosition + 32, 6, word_l);
// longToBit(data, bitPosition, 38, myData);
// logger.info("$$"+getBinaryStrFromByte2( data,bitPosition,38));}public static void longToBit(byte[] buff, int pos, int len, long data) {if (len <= 0 || 38 < len) return;byte[] b = LongToBytes(data);String result = "";for (int m = 0; m < b.length; m++) {byte a = b[m];for (int i = 0; i < 8; i++) {if ((m * 8 + i) < 38 || (m * 8 + i) >= 38 + len) {continue;}byte c = a;a = (byte) (a >> 1);//每移一位如同将10进制数除以2并去掉余数。a = (byte) (a << 1);int current = m * 8 + i - 38 + pos;if (a == c) {result = "0" + result;buff[current / 8] |= 1 << (7 - current % 8);} else {result = "0" + result;buff[current / 8] &= ~(1 << (7 - current % 8));}a = (byte) (a >> 1);}}logger.info("**" + result);}/*** 把byte转化成2进制字符串** @param b* @return*/public static String getBinaryStrFromByte(byte[] b) {String result = "";for (byte bb : b) {byte a = bb;for (int i = 0; i < 8; i++) {byte c = a;a = (byte) (a >> 1);//每移一位如同将10进制数除以2并去掉余数。a = (byte) (a << 1);if (a == c) {result = "0" + result;} else {result = "1" + result;}a = (byte) (a >> 1);}}return result;}/*** 把byte转化成2进制字符串** @param b* @return*/public static String getBinaryStrFromByte2(byte[] b, int start, int len) {String result = "";for (int m = 0; m < b.length; m++) {byte a = b[m];for (int i = 0; i < 8; i++) {if ((m * 8 + i) < start || (m * 8 + i) >= start + len) {continue;}byte c = a;a = (byte) (a >> 1);//每移一位如同将10进制数除以2并去掉余数。a = (byte) (a << 1);if (a == c) {result = "0" + result;} else {result = "1" + result;}a = (byte) (a >> 1);}}return result;}public static byte[] byteConcat(byte[] bt1, byte[] bt2) {byte[] bt4 = new byte[bt1.length + bt2.length];int len = 0;System.arraycopy(bt1, 0, bt4, 0, bt1.length);len += bt1.length;System.arraycopy(bt2, 0, bt4, len, bt2.length);return bt4;}
}
欢迎交流,VX:NtripShare