mirror of
https://github.com/eclipse-sailing-analytics/sailing-analytics.git
synced 2026-09-21 21:25:38 +00:00
using a different sample file for NMEA tests
Change-Id: I599fc44ed169e4cd72ddcca8f75831d099855a59
This commit is contained in:
+33
-28
@@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
+258
-102
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user