using a different sample file for NMEA tests

Change-Id: I599fc44ed169e4cd72ddcca8f75831d099855a59
This commit is contained in:
Axel Uhl
2017-04-28 19:47:37 +02:00
parent c154698e2e
commit d5dc2fdbc2
10 changed files with 3773 additions and 503 deletions
@@ -1,28 +1,33 @@
package com.sap.sailing.nmeaconnector.impl;
import com.sap.sailing.domain.common.Bearing;
import com.sap.sailing.domain.common.impl.KnotSpeedWithBearingImpl;
import com.sap.sailing.domain.tracking.Track;
import com.sap.sailing.nmeaconnector.TimedSpeedWithBearing;
import com.sap.sse.common.TimePoint;
/**
* Can be used as a wind vector in a {@link Track} because it has a {@link TimePoint}.
*
* @author Axel Uhl (d043530)
*
*/
public class KnotSpeedWithBearingAndTimepoint extends KnotSpeedWithBearingImpl implements TimedSpeedWithBearing {
private static final long serialVersionUID = -627441174825774742L;
private final TimePoint timePoint;
public KnotSpeedWithBearingAndTimepoint(TimePoint timePoint, double speedInKnots, Bearing bearing) {
super(speedInKnots, bearing);
this.timePoint = timePoint;
}
@Override
public TimePoint getTimePoint() {
return timePoint;
}
}
package com.sap.sailing.nmeaconnector.impl;
import com.sap.sailing.domain.common.Bearing;
import com.sap.sailing.domain.common.impl.KnotSpeedWithBearingImpl;
import com.sap.sailing.domain.tracking.Track;
import com.sap.sailing.nmeaconnector.TimedSpeedWithBearing;
import com.sap.sse.common.TimePoint;
/**
* Can be used as a wind vector in a {@link Track} because it has a {@link TimePoint}.
*
* @author Axel Uhl (d043530)
*
*/
public class KnotSpeedWithBearingAndTimepoint extends KnotSpeedWithBearingImpl implements TimedSpeedWithBearing {
private static final long serialVersionUID = -627441174825774742L;
private final TimePoint timePoint;
public KnotSpeedWithBearingAndTimepoint(TimePoint timePoint, double speedInKnots, Bearing bearing) {
super(speedInKnots, bearing);
this.timePoint = timePoint;
}
@Override
public TimePoint getTimePoint() {
return timePoint;
}
@Override
public String toString() {
return getTimePoint().toString()+": "+super.toString();
}
}
@@ -1,102 +1,258 @@
package com.sap.sailing.nmeaconnector.impl;
import java.util.concurrent.ConcurrentHashMap;
import com.sap.sailing.domain.common.Wind;
import com.sap.sailing.domain.common.tracking.GPSFix;
import com.sap.sailing.domain.tracking.DynamicTrack;
import com.sap.sailing.domain.tracking.WindListener;
import com.sap.sailing.domain.tracking.impl.DynamicTrackImpl;
import com.sap.sailing.nmeaconnector.NMEAWindReceiver;
import com.sap.sailing.nmeaconnector.TimedBearing;
import net.sf.marineapi.nmea.event.AbstractSentenceListener;
import net.sf.marineapi.nmea.io.SentenceReader;
import net.sf.marineapi.nmea.sentence.HeadingSentence;
import net.sf.marineapi.nmea.sentence.MDASentence;
import net.sf.marineapi.nmea.sentence.MWDSentence;
import net.sf.marineapi.nmea.sentence.MWVSentence;
import net.sf.marineapi.nmea.sentence.SentenceId;
import net.sf.marineapi.nmea.sentence.VWRSentence;
public class NMEAWindReceiverImpl implements NMEAWindReceiver {
private final ConcurrentHashMap<WindListener, WindListener> listeners;
private final DynamicTrack<KnotSpeedWithBearingAndTimepoint> trueWindDirections;
private final DynamicTrack<KnotSpeedWithBearingAndTimepoint> magneticWindDirections;
private final DynamicTrack<GPSFix> sensorPositions;
private final DynamicTrack<KnotSpeedWithBearingAndTimepoint> sensorSpeeds;
private final DynamicTrack<TimedBearing> magneticHeadings;
private final DynamicTrack<TimedBearing> trueHeadings;
private class MWVSentenceListener extends AbstractSentenceListener<MWVSentence> {
@Override
public void sentenceRead(MWVSentence sentence) {
// TODO Auto-generated method stub
}
}
private class MWDSentenceListener extends AbstractSentenceListener<MWDSentence> {
@Override
public void sentenceRead(MWDSentence sentence) {
// TODO Auto-generated method stub
}
}
private class VWRSentenceListener extends AbstractSentenceListener<VWRSentence> {
@Override
public void sentenceRead(VWRSentence sentence) {
// TODO Auto-generated method stub
}
}
private class MDASentenceListener extends AbstractSentenceListener<MDASentence> {
@Override
public void sentenceRead(MDASentence sentence) {
// TODO Auto-generated method stub
}
}
private class HeadingSentenceListener extends AbstractSentenceListener<HeadingSentence> {
@Override
public void sentenceRead(HeadingSentence sentence) {
// TODO Auto-generated method stub
}
}
/**
* @param sentenceReader
* The reader that this receiver will listen to for those NMEA sentences of interest for the construction
* of wind fixes
*/
public NMEAWindReceiverImpl(SentenceReader sentenceReader) {
super();
this.listeners = new ConcurrentHashMap<>();
this.trueWindDirections = new DynamicTrackImpl<>("trueWindDirection in "+getClass().getName());
this.magneticWindDirections = new DynamicTrackImpl<>("magneticWindDirection in "+getClass().getName());
this.sensorPositions = new DynamicTrackImpl<>("measurementPositions in "+getClass().getName());
this.sensorSpeeds = new DynamicTrackImpl<>("sensorSpeeds in "+getClass().getName());
this.magneticHeadings = new DynamicTrackImpl<>("headings in "+getClass().getName());
this.trueHeadings = new DynamicTrackImpl<>("trueHeadings in "+getClass().getName());
sentenceReader.addSentenceListener(new MWVSentenceListener(), SentenceId.MWV);
sentenceReader.addSentenceListener(new MWDSentenceListener(), SentenceId.MWD);
sentenceReader.addSentenceListener(new VWRSentenceListener(), SentenceId.VWR);
sentenceReader.addSentenceListener(new MDASentenceListener(), SentenceId.MDA);
sentenceReader.addSentenceListener(new HeadingSentenceListener());
}
@Override
public void addWindListener(WindListener listener) {
listeners.put(listener, listener);
}
@Override
public void removeWindListener(WindListener listener) {
listeners.remove(listener);
}
private void notifyListeners(Wind wind) {
for (WindListener listener : listeners.values()) {
listener.windDataReceived(wind);
}
}
}
package com.sap.sailing.nmeaconnector.impl;
import java.time.ZoneId;
import java.util.Calendar;
import java.util.GregorianCalendar;
import java.util.TimeZone;
import java.util.concurrent.ConcurrentHashMap;
import com.sap.sailing.domain.common.Speed;
import com.sap.sailing.domain.common.Wind;
import com.sap.sailing.domain.common.impl.DegreeBearingImpl;
import com.sap.sailing.domain.common.impl.KilometersPerHourSpeedImpl;
import com.sap.sailing.domain.common.impl.KnotSpeedImpl;
import com.sap.sailing.domain.common.impl.MeterPerSecondSpeedImpl;
import com.sap.sailing.domain.common.impl.WindImpl;
import com.sap.sailing.domain.common.scalablevalue.impl.ScalablePosition;
import com.sap.sailing.domain.common.tracking.GPSFix;
import com.sap.sailing.domain.tracking.DynamicTrack;
import com.sap.sailing.domain.tracking.WindListener;
import com.sap.sailing.domain.tracking.impl.DynamicTrackImpl;
import com.sap.sailing.nmeaconnector.NMEAWindReceiver;
import com.sap.sailing.nmeaconnector.TimedBearing;
import com.sap.sailing.nmeaconnector.TimedSpeedWithBearing;
import com.sap.sse.common.TimePoint;
import com.sap.sse.common.impl.MillisecondsTimePoint;
import net.sf.marineapi.nmea.event.AbstractSentenceListener;
import net.sf.marineapi.nmea.io.SentenceReader;
import net.sf.marineapi.nmea.parser.DataNotAvailableException;
import net.sf.marineapi.nmea.sentence.DateSentence;
import net.sf.marineapi.nmea.sentence.HeadingSentence;
import net.sf.marineapi.nmea.sentence.MDASentence;
import net.sf.marineapi.nmea.sentence.MWDSentence;
import net.sf.marineapi.nmea.sentence.MWVSentence;
import net.sf.marineapi.nmea.sentence.SentenceId;
import net.sf.marineapi.nmea.sentence.TimeSentence;
import net.sf.marineapi.nmea.sentence.VWRSentence;
import net.sf.marineapi.nmea.sentence.ZDASentence;
import net.sf.marineapi.nmea.util.Date;
import net.sf.marineapi.nmea.util.Position;
import net.sf.marineapi.nmea.util.Time;
import net.sf.marineapi.nmea.util.Units;
public class NMEAWindReceiverImpl implements NMEAWindReceiver {
private final ConcurrentHashMap<WindListener, WindListener> listeners;
private final DynamicTrack<TimedSpeedWithBearing> trueWind;
private final DynamicTrack<TimedSpeedWithBearing> apparentWind;
private final DynamicTrack<TimedSpeedWithBearing> magneticWind;
private final DynamicTrack<GPSFix> sensorPositions;
private final DynamicTrack<TimedSpeedWithBearing> sensorSpeeds;
private final DynamicTrack<TimedBearing> magneticHeadings;
private final DynamicTrack<TimedBearing> trueHeadings;
/**
* Don't use the {@link GregorianCalendar#getTime() time point} of this calendar unless a {@link DateSentence} and a
* {@link TimeSentence} have been received which is indicated by the {@link #dateReceived} and the
* {@link #timeReceived} fields, respectively.
*/
private final GregorianCalendar utcCalendar;
/**
* Until a {@link DateSentence} has been received, the {@link #utcCalendar}'s {@link GregorianCalendar#getTime()}
* must not be used.
*/
private boolean dateReceived;
private boolean timeReceived;
private class MWVSentenceListener extends AbstractSentenceListener<MWVSentence> {
@Override
public void sentenceRead(MWVSentence sentence) {
final TimePoint timePoint = getLastTimePoint();
if (timePoint != null) {
try {
final Speed speed = createSpeed(sentence.getSpeedUnit(), sentence.getSpeed());
final KnotSpeedWithBearingAndTimepoint fix = new KnotSpeedWithBearingAndTimepoint(timePoint,
speed.getKnots(), new DegreeBearingImpl(sentence.getAngle()));
if (sentence.isTrue()) {
trueWind.add(fix);
tryToCreateWindFixFromTrueWind(fix);
} else {
apparentWind.add(fix);
tryToCreateWindFixFromApparentWind(fix);
}
// use the newly gained wind data to try to assemble a Wind fix if
// other values such as heading and position are known
} catch (DataNotAvailableException e) {
// can happen... simply ignore this incomplete sentence
}
}
}
}
private class MWDSentenceListener extends AbstractSentenceListener<MWDSentence> {
@Override
public void sentenceRead(MWDSentence sentence) {
// TODO Auto-generated method stub
}
}
private class VWRSentenceListener extends AbstractSentenceListener<VWRSentence> {
@Override
public void sentenceRead(VWRSentence sentence) {
// TODO Auto-generated method stub
}
}
private class MDASentenceListener extends AbstractSentenceListener<MDASentence> {
@Override
public void sentenceRead(MDASentence sentence) {
// TODO Auto-generated method stub
}
}
private class HeadingSentenceListener extends AbstractSentenceListener<HeadingSentence> {
@Override
public void sentenceRead(HeadingSentence sentence) {
// TODO Auto-generated method stub
}
}
private class DateListener extends AbstractSentenceListener<DateSentence> {
@Override
public void sentenceRead(DateSentence sentence) {
try {
final Date date = sentence.getDate();
utcCalendar.set(date.getYear(), date.getMonth() - 1, date.getDay());
dateReceived = true;
} catch (DataNotAvailableException | IllegalArgumentException e) {
// can happen; ignore sentence
}
}
}
private class TimeListener extends AbstractSentenceListener<TimeSentence> {
@Override
public void sentenceRead(TimeSentence sentence) {
try {
final Time time = sentence.getTime();
utcCalendar.set(utcCalendar.get(Calendar.YEAR), utcCalendar.get(Calendar.MONTH), utcCalendar.get(Calendar.DAY_OF_MONTH),
time.getHour(), time.getMinutes(), (int) time.getSeconds());
final int millisInSecond = (int) ((time.getSeconds()-(int) time.getSeconds())*1000);
utcCalendar.set(Calendar.MILLISECOND, millisInSecond);
timeReceived = true;
} catch (DataNotAvailableException e) {
// can happen; ignore sentence
}
}
}
/**
* @param sentenceReader
* The reader that this receiver will listen to for those NMEA sentences of interest for the construction
* of wind fixes
*/
public NMEAWindReceiverImpl(SentenceReader sentenceReader) {
super();
utcCalendar = new GregorianCalendar();
utcCalendar.setTimeZone(TimeZone.getTimeZone(ZoneId.of("UTC")));
this.listeners = new ConcurrentHashMap<>();
this.trueWind = new DynamicTrackImpl<>("trueWind in "+getClass().getName());
this.apparentWind = new DynamicTrackImpl<>("apparentWind in "+getClass().getName());
this.magneticWind = new DynamicTrackImpl<>("magneticWindDirection in "+getClass().getName());
this.sensorPositions = new DynamicTrackImpl<>("measurementPositions in "+getClass().getName());
this.sensorSpeeds = new DynamicTrackImpl<>("sensorSpeeds in "+getClass().getName());
this.magneticHeadings = new DynamicTrackImpl<>("headings in "+getClass().getName());
this.trueHeadings = new DynamicTrackImpl<>("trueHeadings in "+getClass().getName());
sentenceReader.addSentenceListener(new MWVSentenceListener(), SentenceId.MWV);
sentenceReader.addSentenceListener(new MWDSentenceListener(), SentenceId.MWD);
sentenceReader.addSentenceListener(new VWRSentenceListener(), SentenceId.VWR);
sentenceReader.addSentenceListener(new MDASentenceListener(), SentenceId.MDA);
sentenceReader.addSentenceListener(new HeadingSentenceListener());
sentenceReader.addSentenceListener(new DateListener());
sentenceReader.addSentenceListener(new TimeListener());
}
private Speed createSpeed(Units speedUnit, double amount) {
final Speed result;
switch (speedUnit) {
case KMH:
result = new KilometersPerHourSpeedImpl(amount);
break;
case KNOT:
result = new KnotSpeedImpl(amount);
break;
case METER:
result = new MeterPerSecondSpeedImpl(amount);
break;
default:
throw new RuntimeException("Unknown speed unit: "+speedUnit);
}
return result;
}
/**
* To be called when new true wind evidence has been received for {@code timePoint}. Creates a {@link Wind} fix when
* enough data is available, in particular position data. If this is the case, the wind fix generated will be passed
* to {@link #notifyListeners(Wind)}.
*/
private void tryToCreateWindFixFromTrueWind(TimedSpeedWithBearing trueWind) {
final com.sap.sailing.domain.common.Position position = getPosition(trueWind.getTimePoint());
if (position != null) {
final Wind wind = new WindImpl(position, trueWind.getTimePoint(), trueWind);
notifyListeners(wind);
}
}
/**
* To be called when new apparent wind evidence has been received for {@code timePoint}. Creates a {@link Wind} fix
* when enough data is available, such as motion and heading data required to turn the apparent wind fixe into a
* true wind fix, as well as position data. If this is the case, the wind fix generated will be passed to
* {@link #notifyListeners(Wind)}.
*/
private void tryToCreateWindFixFromApparentWind(TimedSpeedWithBearing apparentWind) {
// TODO tryToCreateWindFixFromApparentWind
}
/**
* Based on the sequence of messages received from the reader, a "current" time point is approximated whenever a
* {@link TimeSentence} or {@link DateSentence} or some time zone information as in a {@link ZDASentence} is
* received. Several messages that are of interest for wind fix construction do not carry their own time stamp
* information. For those, the time point inferred from the last {@link TimeSentence} is used which is what this
* method returns.
*
* @return the last {@link TimePoint} inferred from the combination of {@link TimeSentence}, {@link DateSentence}
* and {@link ZDASentence}, or {@code null} in case not enough information about timing has been received
* yet.
*/
private TimePoint getLastTimePoint() {
final TimePoint result;
if (dateReceived && timeReceived) {
result = new MillisecondsTimePoint(utcCalendar.getTime());
} else {
result = null;
}
return result;
}
private com.sap.sailing.domain.common.Position getPosition(TimePoint timePoint) {
return sensorPositions.getInterpolatedValue(timePoint, gpsFix->new ScalablePosition(gpsFix.getPosition()));
}
@Override
public void addWindListener(WindListener listener) {
listeners.put(listener, listener);
}
@Override
public void removeWindListener(WindListener listener) {
listeners.remove(listener);
}
private void notifyListeners(Wind wind) {
for (WindListener listener : listeners.values()) {
listener.windDataReceived(wind);
}
}
}