Changeset 12421 in josm


Ignore:
Timestamp:
2017-06-22T00:29:46+02:00 (8 years ago)
Author:
Don-vip
Message:

see #14924 - improve NMEA documentation thanks to gpsd (http://www.catb.org/gpsd/NMEA.html) + add support for NMEA sentences coming from GLONASS, Galileo or Beidu receivers

Location:
trunk
Files:
5 added
2 edited
2 moved

Legend:

Unmodified
Added
Removed
  • trunk/src/org/openstreetmap/josm/io/GpxImporter.java

    r10615 r12421  
    166166    }
    167167
     168    /**
     169     * Replies the new GPX and marker layers corresponding to the specified GPX file.
     170     * @param is input stream to GPX data
     171     * @param associatedFile GPX file
     172     * @param gpxLayerName The GPX layer name
     173     * @param markerLayerName The marker layer name
     174     * @param progressMonitor The progress monitor
     175     * @return the new GPX and marker layers corresponding to the specified GPX file
     176     * @throws IOException if an I/O error occurs
     177     */
    168178    public static GpxImporterData loadLayers(InputStream is, final File associatedFile,
    169179            final String gpxLayerName, String markerLayerName, ProgressMonitor progressMonitor) throws IOException {
  • trunk/src/org/openstreetmap/josm/io/NMEAImporter.java

    r10621 r12421  
    2020import org.openstreetmap.josm.gui.util.GuiHelper;
    2121import org.openstreetmap.josm.io.GpxImporter.GpxImporterData;
     22import org.openstreetmap.josm.io.nmea.NmeaReader;
    2223
    2324/**
     
    8889    }
    8990
     91    /**
     92     * Replies the new GPX and marker layers corresponding to the specified NMEA file.
     93     * @param is input stream to NMEA 0183 data
     94     * @param associatedFile NMEA file
     95     * @param gpxLayerName The GPX layer name
     96     * @param markerLayerName The marker layer name
     97     * @return the new GPX and marker layers corresponding to the specified NMEA file
     98     * @throws IOException if an I/O error occurs
     99     */
    90100    public static GpxImporterData loadLayers(InputStream is, final File associatedFile,
    91101            final String gpxLayerName, String markerLayerName) throws IOException {
  • trunk/src/org/openstreetmap/josm/io/nmea/NmeaReader.java

    r12418 r12421  
    11// License: GPL. For details, see LICENSE file.
    2 package org.openstreetmap.josm.io;
     2package org.openstreetmap.josm.io.nmea;
    33
    44import java.io.BufferedReader;
     
    2121import org.openstreetmap.josm.data.gpx.ImmutableGpxTrack;
    2222import org.openstreetmap.josm.data.gpx.WayPoint;
     23import org.openstreetmap.josm.io.IllegalDataException;
    2324import org.openstreetmap.josm.tools.JosmRuntimeException;
    2425import org.openstreetmap.josm.tools.date.DateUtils;
    2526
    2627/**
    27  * Reads a NMEA file. Based on information from
    28  * <a href="http://www.kowoma.de/gps/zusatzerklaerungen/NMEA.htm">http://www.kowoma.de</a>
     28 * Reads a NMEA 0183 file. Based on information from
     29 * <a href="http://www.catb.org/gpsd/NMEA.html">http://www.catb.org/gpsd</a>.
     30 *
     31 * NMEA files are in printable ASCII form and may include information such as position,
     32 * speed, depth, frequency allocation, etc.
     33 * Typical messages might be 11 to a maximum of 79 characters in length.
     34 *
     35 * NMEA standard aims to support one-way serial data transmission from a single "talker"
     36 * to one or more "listeners". The type of talker is identified by a 2-character mnemonic.
     37 *
     38 * NMEA information is encoded through a list of "sentences".
    2939 *
    3040 * @author cbrill
     
    3242public class NmeaReader {
    3343
    34     // GPVTG
    35     public enum GPVTG {
     44    public enum VTG {
    3645        COURSE(1), COURSE_REF(2), // true course
    3746        COURSE_M(3), COURSE_M_REF(4), // magnetic course
     
    4251        public final int position;
    4352
    44         GPVTG(int position) {
     53        VTG(int position) {
    4554            this.position = position;
    4655        }
    4756    }
    4857
    49     // The following only applies to GPRMC
    50     public enum GPRMC {
     58    public enum RMC {
    5159        TIME(1),
    5260        /** Warning from the receiver (A = data ok, V = warning) */
     
    5765        MAGNETIC_DECLINATION(10), UNKNOWN(11),  // magnetic declination
    5866        /**
    59          * Mode (A = autonom; D = differential; E = estimated; N = not valid; S
    60          * = simulated)
     67         * Mode (A = autonom; D = differential; E = estimated; N = not valid; S = simulated)
    6168         *
    6269         * @since NMEA 2.3
     
    6673        public final int position;
    6774
    68         GPRMC(int position) {
     75        RMC(int position) {
    6976            this.position = position;
    7077        }
    7178    }
    7279
    73     // The following only applies to GPGGA
    74     public enum GPGGA {
     80    public enum GGA {
    7581        TIME(1), LATITUDE(2), LATITUDE_NAME(3), LONGITUDE(4), LONGITUDE_NAME(5),
    7682        /**
    77          * Quality (0 = invalid, 1 = GPS, 2 = DGPS, 6 = estimanted (@since NMEA
    78          * 2.3))
     83         * Quality (0 = invalid, 1 = GPS, 2 = DGPS, 6 = estimanted (@since NMEA 2.3))
    7984         */
    8085        QUALITY(6), SATELLITE_COUNT(7),
     
    8691
    8792        public final int position;
    88         GPGGA(int position) {
     93        GGA(int position) {
    8994            this.position = position;
    9095        }
    9196    }
    9297
    93     public enum GPGSA {
     98    public enum GSA {
    9499        AUTOMATIC(1),
    95100        FIX_TYPE(2), // 1 = not fixed, 2 = 2D fixed, 3 = 3D fixed)
     
    102107
    103108        public final int position;
    104         GPGSA(int position) {
     109        GSA(int position) {
    105110            this.position = position;
    106111        }
     
    200205    }
    201206
     207    /**
     208     * Determines if the given address denotes the given NMEA sentence formatter of a known talker.
     209     * @param address first tag of an NMEA sentence
     210     * @param formatter sentence formatter mnemonic code
     211     * @return {@code true} if the {@code address} denotes the given NMEA sentence formatter of a known talker
     212     */
     213    static boolean isSentence(String address, Sentence formatter) {
     214        for (TalkerId talker : TalkerId.values()) {
     215            if (address.equals('$' + talker.name() + formatter.name())) {
     216                return true;
     217            }
     218        }
     219        return false;
     220    }
     221
    202222    // Parses split up sentences into WayPoints which are stored
    203223    // in the collection in the NMEAParserState object.
    204     // Returns true if the input made sence, false otherwise.
     224    // Returns true if the input made sense, false otherwise.
    205225    private boolean parseNMEASentence(String s, NMEAParserState ps) throws IllegalDataException {
    206226        try {
     
    236256
    237257            // handle the packet content
    238             if ("$GPGGA".equals(e[0]) || "$GNGGA".equals(e[0])) {
     258            if (isSentence(e[0], Sentence.GGA)) {
    239259                // Position
    240260                LatLon latLon = parseLatLon(
    241                         e[GPGGA.LATITUDE_NAME.position],
    242                         e[GPGGA.LONGITUDE_NAME.position],
    243                         e[GPGGA.LATITUDE.position],
    244                         e[GPGGA.LONGITUDE.position]
     261                        e[GGA.LATITUDE_NAME.position],
     262                        e[GGA.LONGITUDE_NAME.position],
     263                        e[GGA.LATITUDE.position],
     264                        e[GGA.LONGITUDE.position]
    245265                );
    246266                if (latLon == null) {
     
    254274
    255275                // time
    256                 accu = e[GPGGA.TIME.position];
     276                accu = e[GGA.TIME.position];
    257277                Date d = readTime(currentDate+accu);
    258278
     
    269289                }
    270290                // elevation
    271                 accu = e[GPGGA.HEIGHT_UNTIS.position];
     291                accu = e[GGA.HEIGHT_UNTIS.position];
    272292                if ("M".equals(accu)) {
    273293                    // Ignore heights that are not in meters for now
    274                     accu = e[GPGGA.HEIGHT.position];
     294                    accu = e[GGA.HEIGHT.position];
    275295                    if (!accu.isEmpty()) {
    276296                        Double.parseDouble(accu);
     
    282302                    }
    283303                }
    284                 // number of sattelites
    285                 accu = e[GPGGA.SATELLITE_COUNT.position];
     304                // number of satellites
     305                accu = e[GGA.SATELLITE_COUNT.position];
    286306                int sat = 0;
    287307                if (!accu.isEmpty()) {
     
    290310                }
    291311                // h-dilution
    292                 accu = e[GPGGA.HDOP.position];
     312                accu = e[GGA.HDOP.position];
    293313                if (!accu.isEmpty()) {
    294314                    currentwp.put(GpxConstants.PT_HDOP, Float.valueOf(accu));
    295315                }
    296316                // fix
    297                 accu = e[GPGGA.QUALITY.position];
     317                accu = e[GGA.QUALITY.position];
    298318                if (!accu.isEmpty()) {
    299319                    int fixtype = Integer.parseInt(accu);
     
    316336                    }
    317337                }
    318             } else if ("$GPVTG".equals(e[0]) || "$GNVTG".equals(e[0])) {
     338            } else if (isSentence(e[0], Sentence.VTG)) {
    319339                // COURSE
    320                 accu = e[GPVTG.COURSE_REF.position];
     340                accu = e[VTG.COURSE_REF.position];
    321341                if ("T".equals(accu)) {
    322342                    // other values than (T)rue are ignored
    323                     accu = e[GPVTG.COURSE.position];
     343                    accu = e[VTG.COURSE.position];
    324344                    if (!accu.isEmpty()) {
    325345                        Double.parseDouble(accu);
     
    328348                }
    329349                // SPEED
    330                 accu = e[GPVTG.SPEED_KMH_UNIT.position];
     350                accu = e[VTG.SPEED_KMH_UNIT.position];
    331351                if (accu.startsWith("K")) {
    332                     accu = e[GPVTG.SPEED_KMH.position];
     352                    accu = e[VTG.SPEED_KMH.position];
    333353                    if (!accu.isEmpty()) {
    334354                        double speed = Double.parseDouble(accu);
     
    337357                    }
    338358                }
    339             } else if ("$GPGSA".equals(e[0]) || "$GNGSA".equals(e[0])) {
     359            } else if (isSentence(e[0], Sentence.GSA)) {
    340360                // vdop
    341                 accu = e[GPGSA.VDOP.position];
     361                accu = e[GSA.VDOP.position];
    342362                if (!accu.isEmpty() && currentwp != null) {
    343363                    currentwp.put(GpxConstants.PT_VDOP, Float.valueOf(accu));
    344364                }
    345365                // hdop
    346                 accu = e[GPGSA.HDOP.position];
     366                accu = e[GSA.HDOP.position];
    347367                if (!accu.isEmpty() && currentwp != null) {
    348368                    currentwp.put(GpxConstants.PT_HDOP, Float.valueOf(accu));
    349369                }
    350370                // pdop
    351                 accu = e[GPGSA.PDOP.position];
     371                accu = e[GSA.PDOP.position];
    352372                if (!accu.isEmpty() && currentwp != null) {
    353373                    currentwp.put(GpxConstants.PT_PDOP, Float.valueOf(accu));
    354374                }
    355             } else if ("$GPRMC".equals(e[0]) || "$GNRMC".equals(e[0])) {
     375            } else if (isSentence(e[0], Sentence.RMC)) {
    356376                // coordinates
    357377                LatLon latLon = parseLatLon(
    358                         e[GPRMC.WIDTH_NORTH_NAME.position],
    359                         e[GPRMC.LENGTH_EAST_NAME.position],
    360                         e[GPRMC.WIDTH_NORTH.position],
    361                         e[GPRMC.LENGTH_EAST.position]
     378                        e[RMC.WIDTH_NORTH_NAME.position],
     379                        e[RMC.LENGTH_EAST_NAME.position],
     380                        e[RMC.WIDTH_NORTH.position],
     381                        e[RMC.LENGTH_EAST.position]
    362382                );
    363383                if (LatLon.ZERO.equals(latLon)) {
     
    366386                }
    367387                // time
    368                 currentDate = e[GPRMC.DATE.position];
    369                 String time = e[GPRMC.TIME.position];
     388                currentDate = e[RMC.DATE.position];
     389                String time = e[RMC.TIME.position];
    370390
    371391                Date d = readTime(currentDate+time);
     
    379399                currentwp.setTime(d);
    380400                // speed
    381                 accu = e[GPRMC.SPEED.position];
     401                accu = e[RMC.SPEED.position];
    382402                if (!accu.isEmpty() && !currentwp.attr.containsKey("speed")) {
    383403                    double speed = Double.parseDouble(accu);
     
    386406                }
    387407                // course
    388                 accu = e[GPRMC.COURSE.position];
     408                accu = e[RMC.COURSE.position];
    389409                if (!accu.isEmpty() && !currentwp.attr.containsKey("course")) {
    390410                    Double.parseDouble(accu);
  • trunk/test/unit/org/openstreetmap/josm/io/nmea/NmeaReaderTest.java

    r12418 r12421  
    11// License: GPL. For details, see LICENSE file.
    2 package org.openstreetmap.josm.io;
     2package org.openstreetmap.josm.io.nmea;
    33
    44import static org.junit.Assert.assertEquals;
     5import static org.junit.Assert.assertFalse;
     6import static org.junit.Assert.assertTrue;
    57
    68import java.io.FileInputStream;
     
    2022import org.openstreetmap.josm.data.gpx.GpxTrackSegment;
    2123import org.openstreetmap.josm.data.gpx.WayPoint;
     24import org.openstreetmap.josm.io.GpxReaderTest;
    2225import org.openstreetmap.josm.testutils.JOSMTestRules;
    2326import org.openstreetmap.josm.tools.date.DateUtils;
     
    9295
    9396    /**
     97     * Unit test of {@link NmeaReader#isSentence}.
     98     */
     99    @Test
     100    public void testIsSentence() {
     101        assertTrue(NmeaReader.isSentence("$GPVTG", Sentence.VTG));
     102        assertTrue(NmeaReader.isSentence("$GAVTG", Sentence.VTG));
     103        assertTrue(NmeaReader.isSentence("$GNVTG", Sentence.VTG));
     104        assertFalse(NmeaReader.isSentence("XGAVTG", Sentence.VTG));
     105        assertFalse(NmeaReader.isSentence("$GPXXX", Sentence.VTG));
     106        assertFalse(NmeaReader.isSentence("$XXVTG", Sentence.VTG));
     107    }
     108
     109    /**
    94110     * Non-regression test for <a href="https://josm.openstreetmap.de/ticket/1433">Bug #1433</a>.
    95111     * @throws Exception if an error occurs
Note: See TracChangeset for help on using the changeset viewer.