/* This file is part of RouteConverter. RouteConverter is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. RouteConverter is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with RouteConverter; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA Copyright (C) 2007 Christian Pesch. All Rights Reserved. */ package slash.navigation.wbt; import slash.navigation.base.*; import slash.navigation.common.NavigationPosition; import java.io.*; import java.nio.ByteBuffer; import java.util.ArrayList; import java.util.Calendar; import java.util.List; import static java.lang.Long.parseLong; import static java.nio.ByteBuffer.allocate; import static java.nio.ByteOrder.LITTLE_ENDIAN; import static java.util.Calendar.*; import static slash.common.type.CompactCalendar.*; import static slash.navigation.base.RouteCharacteristics.Track; import static slash.navigation.base.RouteCharacteristics.Waypoints; /** * The base of all Wintec WBT-201 formats. * * @author Malte Neumann, Christian Pesch */ public abstract class WintecWbt201Format extends SimpleFormat<Wgs84Route> { private static final String TRACK_NAME_DATE_FORMAT = "yyyy-MM-dd HH:mm:ss"; public String getName() { return "Wintec WBT-201 (*" + getExtension() + ")"; } public int getMaximumPositionCount() { return UNLIMITED_MAXIMUM_POSITION_COUNT; } public boolean isSupportsReading() { return true; } public boolean isSupportsWriting() { return false; } public boolean isSupportsMultipleRoutes() { return true; } protected abstract int getHeaderSize(); @SuppressWarnings({"unchecked"}) public <P extends NavigationPosition> Wgs84Route createRoute(RouteCharacteristics characteristics, String name, List<P> positions) { return new Wgs84Route(this, characteristics, (List<Wgs84Position>) positions); } public void read(BufferedReader reader, String encoding, ParserContext<Wgs84Route> context) throws IOException { // this format parses the InputStream directly but wants to derive from SimpleFormat to use Wgs84Route throw new UnsupportedOperationException(); } public void write(Wgs84Route route, PrintWriter writer, int startIndex, int endIndex) { // this format parses the InputStream directly but wants to derive from SimpleFormat to use Wgs84Route throw new UnsupportedOperationException(); } protected abstract boolean checkFormatDescriptor(ByteBuffer buffer) throws IOException; protected abstract List<Wgs84Route> internalRead(ByteBuffer buffer); public void read(InputStream source, ParserContext<Wgs84Route> context) throws Exception { byte[] header = new byte[getHeaderSize()]; if (source.read(header) == getHeaderSize()) { // copy headerbytes in ByteBuffer, because header contains little endian int ByteBuffer headerBuffer = allocate(getHeaderSize()); headerBuffer.position(0); headerBuffer.put(header); if (checkFormatDescriptor(headerBuffer)) { // read whole file in ByteBuffer with a size limit of about 2 MB int available = source.available(); ByteBuffer sourceBuffer = allocate(header.length + available); byte[] data = new byte[available]; if (source.read(data) != available) throw new IOException("Could not read " + available + " bytes"); sourceBuffer.position(0); sourceBuffer.put(header); sourceBuffer.put(data); context.appendRoutes(internalRead(sourceBuffer)); } } } List<Wgs84Route> readPositions(ByteBuffer source, int startDataAddress, long trackInfoAddress) { /* http://forum.pocketnavigation.de/attachment.php?attachmentid=1082953 2 byte Trackflag 00001 = 1 --> That point is the start point of a trajectory 00010 = 2 --> That point is push to log 00100 = 4 --> That point is over speed point * The flag of one point may be combination with two or three flags. The Wintec WSG-1000 has a lot more flags, don't now what they mean. 4 byte Date & Time (UTC) 6 bits year (+ 2000) 4 bits month 5 bits day 5 bits hour 6 bits minute 6 bits second 4 byte Latitude integer / 10000000 (degree) 4 byte Longitude integer / 10000000 (degree) 2 byte Altitude short in meters */ // seek to begin of trackpoints source.position(startDataAddress); source.order(LITTLE_ENDIAN); List<Wgs84Route> result = new ArrayList<>(); List<NavigationPosition> trackPoints = null; List<NavigationPosition> pushPoints = null; int trackPointNo = 1; int pushPointNo = 1; while ((source.position() < trackInfoAddress) && (source.position() + 2+4+4+4+2 <= source.capacity())) { short trackFlag = source.getShort(); int time = source.getInt(); int latitude = source.getInt(); int longitude = source.getInt(); short altitude = source.getShort(); // if internal gps memory is full, the first points will override and no trackflag is set if ((trackPoints == null) && (trackFlag == 0)) trackFlag = 1; if ((trackFlag & 1) == 1) { // new track trackPoints = new ArrayList<>(); Wgs84Route track = createRoute(Track, null, trackPoints); result.add(track); trackPointNo = 1; // trackname = time of first point NavigationPosition newPoint = createWaypoint(time, latitude, longitude, altitude, 0, true); track.setName(createDateFormat(TRACK_NAME_DATE_FORMAT).format(newPoint.getTime().getTime())); } if ((trackFlag & 2) == 2) { // track pushpoint if (pushPoints == null) { pushPoints = new ArrayList<>(); Wgs84Route points = createRoute(Waypoints, "Pushpoints", pushPoints); result.add(points); } pushPoints.add(createWaypoint(time, latitude, longitude, altitude, pushPointNo++, false)); } // all points are included in the track if (trackPoints != null) trackPoints.add(createWaypoint(time, latitude, longitude, altitude, trackPointNo++, true)); } return result; } private static final long YEAR_MASK = parseLong("11111100000000000000000000000000", 2); private static final long MONTH_MASK = parseLong("00000011110000000000000000000000", 2); private static final long DAY_MASK = parseLong("00000000001111100000000000000000", 2); private static final long HOUR_MASK = parseLong("00000000000000011111000000000000", 2); private static final long MINUTE_MASK = parseLong("00000000000000000000111111000000", 2); private static final long SECOND_MASK = parseLong("00000000000000000000000000111111", 2); private static final double FACTOR = 10000000.0; protected BaseNavigationPosition createWaypoint(long time, long latitude, long longitude, int altitude, int pointNo, boolean isTrackpoint) { int year = (int) ((time & YEAR_MASK) >> 26); int month = (int) ((time & MONTH_MASK) >> 22); int day = (int) ((time & DAY_MASK) >> 17); int hour = (int) ((time & HOUR_MASK) >> 12); int minute = (int) ((time & MINUTE_MASK) >> 6); int second = (int) ((time & SECOND_MASK)); Calendar calendar = Calendar.getInstance(UTC); calendar.set(YEAR, 2000 + year); calendar.set(MONTH, month - 1); calendar.set(DAY_OF_MONTH, day); calendar.set(HOUR_OF_DAY, hour); calendar.set(MINUTE, minute); calendar.set(SECOND, second); calendar.set(MILLISECOND, 0); String description; if (isTrackpoint) description = "Trackpoint " + String.valueOf(pointNo); else description = "Pushpoint " + String.valueOf(pointNo); return new Wgs84Position(longitude / FACTOR, latitude / FACTOR, (double) altitude, null, fromCalendar(calendar), description); } public void write(Wgs84Route route, OutputStream target, int startIndex, int endIndex) throws IOException { throw new UnsupportedOperationException(); } }