Browse Source

Imported Upstream version 3.6

master
Bernd Zeimetz 8 years ago
parent
commit
13931410c7
100 changed files with 3595 additions and 2015 deletions
  1. +11
    -1
      NEWS
  2. +90
    -46
      SConstruct
  3. +8
    -6
      TODO
  4. +27
    -2
      ais_json.c
  5. +74
    -0
      ais_json.i
  6. +81
    -44
      bits.c
  7. +4
    -15
      bits.h
  8. +25
    -4
      build.txt
  9. +1
    -1
      cgps.1
  10. +4
    -2
      contrib/ashctl.c
  11. +1
    -1
      contrib/motosend.c
  12. +1
    -0
      daemon.c
  13. +0
    -5
      devtools/README
  14. +158
    -3
      devtools/ais.py
  15. +1
    -0
      devtools/gpsd-debian-regressions.sh
  16. +0
    -201
      devtools/ppstest.c
  17. +976
    -0
      driver_ais.c
  18. +0
    -1123
      driver_aivdm.c
  19. +4
    -3
      driver_evermore.c
  20. +4
    -4
      driver_garmin.c
  21. +4
    -5
      driver_geostar.c
  22. +3
    -1
      driver_italk.c
  23. +5
    -4
      driver_navcom.c
  24. +10
    -4
      driver_nmea0183.c
  25. +661
    -0
      driver_nmea2000.c
  26. +1
    -0
      driver_oncore.c
  27. +2
    -2
      driver_proto.c
  28. +8
    -5
      driver_rtcm2.c
  29. +4
    -4
      driver_rtcm3.c
  30. +1
    -1
      driver_superstar2.c
  31. +2
    -2
      driver_tsip.c
  32. +57
    -49
      driver_ubx.c
  33. +10
    -1
      driver_zodiac.c
  34. +180
    -5
      drivers.c
  35. +12
    -13
      geoid.c
  36. +3
    -3
      gps.1
  37. +69
    -5
      gps.h
  38. +2
    -2
      gps.xml
  39. +21
    -0
      gpscap.ini
  40. +1
    -1
      gpscat.1
  41. +1
    -1
      gpsctl.1
  42. +3
    -3
      gpsctl.c
  43. +26
    -10
      gpsd.8
  44. +31
    -9
      gpsd.c
  45. +44
    -17
      gpsd.h-tail
  46. +52
    -20
      gpsd.xml
  47. +10
    -9
      gpsd_json.5
  48. +238
    -107
      gpsd_json.c
  49. +11
    -10
      gpsd_json.xml
  50. +3
    -2
      gpsdclient.c
  51. +1
    -1
      gpsdctl.8
  52. +2
    -0
      gpsdctl.c
  53. +1
    -1
      gpsdecode.1
  54. +59
    -7
      gpsdecode.c
  55. +8
    -2
      gpsfake
  56. +1
    -1
      gpsfake.1
  57. +4
    -2
      gpsmon.1
  58. +21
    -6
      gpsmon.c
  59. +4
    -0
      gpsmon.xml
  60. +1
    -1
      gpspipe.1
  61. +2
    -2
      gpspipe.c
  62. +2
    -2
      gpsprof.1
  63. +1
    -1
      gpsprof.xml
  64. +5
    -3
      gpsutils.c
  65. +1
    -1
      gpxlogger.c
  66. +1
    -1
      hex.c
  67. +4
    -4
      isgps.c
  68. +10
    -5
      json.c
  69. +1
    -2
      json.h
  70. +43
    -0
      jsongen.py.in
  71. +1
    -1
      lcdgps.1
  72. +3
    -3
      lcdgps.c
  73. +1
    -1
      libQgpsmm.3
  74. +1
    -1
      libgps.3
  75. +1
    -1
      libgps_core.c
  76. +12
    -22
      libgps_sock.c
  77. +1
    -1
      libgpsd.3
  78. +71
    -10
      libgpsd_core.c
  79. +1
    -1
      libgpsmm.3
  80. +2
    -0
      libgpsmm.h
  81. +2
    -0
      monitor_garmin.c
  82. +24
    -15
      net_ntrip.c
  83. +1
    -1
      netlib.c
  84. +35
    -17
      ntpshm.c
  85. +1
    -1
      packaging/readme.txt
  86. +1
    -1
      packaging/rpm/gpsd.spec
  87. +29
    -0
      packet.c
  88. +3
    -0
      packet_names.h
  89. +3
    -0
      packet_states.h
  90. +4
    -3
      pseudonmea.c
  91. +1
    -1
      revision.h
  92. +2
    -1
      serial.c
  93. +1
    -1
      srec.5
  94. +22
    -15
      subframe.c
  95. +60
    -50
      test/daemon/tnt-revolution.log.chk
  96. +106
    -0
      test/sample.aivdm
  97. +2
    -0
      test/sample.aivdm.chk
  98. +2
    -0
      test/synthetic-ais.json
  99. +84
    -69
      test_bits.c
  100. +1
    -1
      timebase.c

+ 11
- 1
NEWS View File

@@ -1,4 +1,14 @@
* Fri 13 Apr 2012 Eric S. Raymond <esr@snark.thyrsus.com> - 3.5
* Wed 23 May 2012 Eric S. Raymond <esr@snark.thyrsus.com>
It's the Fernando Poo Day release. Code has zero detectible defects
under Coverity scanning and cppcheck 1.52; this is mainly a cleanup
release to get those minor fixes into the field. If a leap-second
warning is available from GPS subframe information it is passed to
ntpd. NMEA2000 is now supported via the Linux kernel CAN interface.
There's a chrpath=no config option for distribution makers, so
chrpath is no longer a build dependency; see build.txt for
explanation.

* Sat 14 Apr 2012 Eric S. Raymond <esr@snark.thyrsus.com> - 3.5
Use pselect when it's available to cut down on wakeups and improve
signal handling. New {PPS} message exporting clock drift. The AIVDM
driver now handles up to 16 interleaved 24A and 24B pair-halves.


+ 90
- 46
SConstruct View File

@@ -19,7 +19,7 @@
# * Out-of-directory builds: see http://www.scons.org/wiki/UsingBuildDir

# Release identification begins here
gpsd_version = "3.5"
gpsd_version = "3.6"

# library version
libgps_version_current = 20
@@ -113,6 +113,7 @@ boolopts = (
("tsip", True, "Trimble TSIP support"),
("ubx", True, "UBX Protocol support"),
("fury", True, "Jackson Labs Fury and Firefly support"),
("nmea2000", True, "NMEA2000/CAN support"),
# Non-GPS protocols
("aivdm", True, "AIVDM support"),
("gpsclock", True, "GPSClock support"),
@@ -155,6 +156,7 @@ boolopts = (
("debug", False, "include debug information in build"),
("profiling", False, "build with profiling enabled"),
("strip", True, "build with stripping of binaries enabled"),
("chrpath", True, "use chrpath to edit library load paths"),
)
for (name, default, help) in boolopts:
opts.Add(BoolVariable(name, help, default))
@@ -197,7 +199,7 @@ import_env = (
"LOGNAME", # LOGNAME is required for the flocktest production.
'PATH', # Required for ccache and Coverity scan-build
'PKG_CONFIG_PATH', # Set .pc file directory in a crossbuild
'STAGING_PREFIX', # Required by the OpenWRT build.
'STAGING_DIR', # Required by the OpenWRT build.
)
envs = {}
for var in import_env:
@@ -235,6 +237,11 @@ for flags in ["LDFLAGS", "LINKFLAGS", "SHLINKFLAGS", "CPPFLAGS"]:
env.MergeFlags({flags : [os.getenv(flags)]})


# Keep scan-build options in the environment
for key, value in os.environ.iteritems():
if key.startswith('CCC_'):
env.Append(ENV={key:value})

# Placeholder so we can kluge together something like VPATH builds.
# $SRCDIR replaces occurrences for $(srcdir) in the autotools build.
env['SRCDIR'] = '.'
@@ -243,18 +250,10 @@ def announce(msg):
if not env.GetOption("silent"):
print msg

# GCC isn't always named gcc, alas.
if env['CC'] == 'gcc' or (sys.platform.startswith('freebsd') and env['CC'] == 'cc'):
# Enable all GCC warnings except uninitialized and
# missing-field-initializers, which we can't help triggering because
# of the way some of the JSON-parsing code is generated.
# Also not including -Wcast-qual and -Wimplicit-function-declaration,
# because we can't seem to keep scons from passing it to g++.
env.Append(CFLAGS=Split('''-Wextra -Wall -Wno-uninitialized
-Wno-missing-field-initializers -Wcast-align
-Wmissing-declarations -Wmissing-prototypes
-Wstrict-prototypes -Wpointer-arith -Wreturn-type
-D_GNU_SOURCE'''))
# We need to define -D_GNU_SOURCE
env.Append(CFLAGS='-D_GNU_SOURCE')



# DESTDIR environment variable means user wants to prefix the installation root.
DESTDIR = os.environ.get('DESTDIR', '')
@@ -384,22 +383,48 @@ def CheckXsltproc(context):
context.Result( ret )
return ret

def CheckCompilerOption(context, option):
context.Message( 'Checking if compiler accepts %s ...' %(option,) )
old_CFLAGS=context.env['CFLAGS']
context.env.Append(CFLAGS=option)
ret = context.TryLink("""
int main(int argc, char **argv) {
return 0;
}
""",'.c')
if not ret:
context.env.Replace(CFLAGS=old_CFLAGS)
context.Result(ret)
return ret

config = Configure(env, custom_tests = { 'CheckPKG' : CheckPKG,
'CheckExecutable' : CheckExecutable,
'CheckXsltproc' : CheckXsltproc})
'CheckXsltproc' : CheckXsltproc,
'CheckCompilerOption' : CheckCompilerOption})


# If supported by the compiler, enable all warnings except uninitialized and
# missing-field-initializers, which we can't help triggering because
# of the way some of the JSON-parsing code is generated.
# Also not including -Wcast-qual and -Wimplicit-function-declaration,
# because we can't seem to keep scons from passing it to g++.
for option in ('-Wextra','-Wall', '-Wno-uninitialized','-Wno-missing-field-initializers',
'-Wcast-align','-Wmissing-declarations', '-Wmissing-prototypes',
'-Wstrict-prototypes', '-Wpointer-arith', '-Wreturn-type'):
config.CheckCompilerOption(option)


env.Prepend(LIBPATH=[os.path.realpath(os.curdir)])
if config.CheckExecutable('$CHRPATH -v', 'chrpath'):
# Tell generated binaries to look in the current directory for
# shared libraries so we can run tests without hassle. Should be
# handled sanely by scons on all systems. Not good to use '.' or
# a relative path here; it's a security risk. At install time we
# use chrpath to edit this out of RPATH.
if env["shared"]:
if env["shared"]:
if env["chrpath"] and config.CheckExecutable('$CHRPATH -v', 'chrpath'):
# Tell generated binaries to look in the current directory for
# shared libraries so we can run regression tests without
# hassle. Should be handled sanely by scons on all systems. Not
# good to use '.' or a relative path here; it's a security risk.
# At install time we use chrpath to edit this out of RPATH.
env.Prepend(RPATH=[os.path.realpath(os.curdir)])
else:
print "chrpath is not available, forcing static linking."
env["shared"] = False
else:
print "chrpath is not available or use of it has been disabled."

confdefs = ["/* gpsd_config.h. Generated by scons, do not hand-hack. */\n"]

@@ -449,6 +474,7 @@ if env['usb']:
else:
confdefs.append("/* #undef HAVE_LIBUSB */\n")
usblibs = []
env["usb"] = False

if config.CheckLib('librt'):
confdefs.append("#define HAVE_LIBRT 1\n")
@@ -461,10 +487,10 @@ else:
if config.CheckLib('libcap'):
confdefs.append("#define HAVE_LIBCAP 1\n")
# System library - no special flags
rtlibs = ["-lcap"]
caplibs = ["-lcap"]
else:
confdefs.append("/* #undef HAVE_LIBCAP */\n")
rtlibs = []
caplibs = []

if env['dbus_export'] and config.CheckPKG('dbus-1'):
confdefs.append("#define HAVE_DBUS 1\n")
@@ -472,6 +498,7 @@ if env['dbus_export'] and config.CheckPKG('dbus-1'):
else:
confdefs.append("/* #undef HAVE_DBUS */\n")
dbus_libs = []
env["dbus_export"] = False

if env['bluez'] and config.CheckPKG('bluez'):
confdefs.append("#define HAVE_BLUEZ 1\n")
@@ -479,6 +506,7 @@ if env['bluez'] and config.CheckPKG('bluez'):
else:
confdefs.append("/* #undef HAVE_BLUEZ */\n")
bluezlibs = []
env["bluez"] = False

if config.CheckHeader("sys/timepps.h"):
confdefs.append("#define HAVE_SYS_TIMEPPS_H 1\n")
@@ -486,6 +514,15 @@ if config.CheckHeader("sys/timepps.h"):
else:
confdefs.append("/* #undef HAVE_SYS_TIMEPPS_H */\n")
announce("You do not have kernel PPS available.")
# Don't turn off PPS here, we might be using the non-kernel version

if config.CheckHeader(["bits/sockaddr.h", "linux/can.h"]):
confdefs.append("#define HAVE_LINUX_CAN_H 1\n")
announce("You have kernel CANbus available.")
else:
confdefs.append("/* #undef HAVE_LINUX_CAN_H */\n")
announce("You do not have kernel CANbus available.")
env["nmea2000"] = False

# check function after libraries, because some function require library
# for example clock_gettime() require librt on Linux
@@ -620,6 +657,7 @@ libgps_version = "%d.%d.%d" %(libgps_version_soname, libgps_version_age, libgps_

libgps_sources = [
"ais_json.c",
"bits.c",
"daemon.c",
"gpsutils.c",
"gpsdclient.c",
@@ -641,7 +679,6 @@ if cxx and env['libgpsmm']:
libgps_sources.append("libgpsmm.cpp")

libgpsd_sources = [
"bits.c",
"bsd_base64.c",
"crc24q.c",
"gpsd_json.c",
@@ -654,18 +691,18 @@ libgpsd_sources = [
"packet.c",
"pseudonmea.c",
"serial.c",
#"srecord.c",
"subframe.c",
"timebase.c",
"drivers.c",
"driver_aivdm.c",
"driver_ais.c",
"driver_evermore.c",
"driver_garmin.c",
"driver_garmin_txt.c",
"driver_geostar.c",
"driver_italk.c",
"driver_navcom.c",
"driver_nmea.c",
"driver_nmea0183.c",
"driver_nmea2000.c",
"driver_oncore.c",
"driver_rtcm2.c",
"driver_rtcm3.c",
@@ -772,7 +809,7 @@ compiled_gpslib = Library(env=env,
target="gps",
sources=libgps_sources,
version=libgps_version,
parse_flags=dbus_libs)
parse_flags=dbus_libs + rtlibs)
env.Clean(compiled_gpslib, "gps_maskdump.c")

compiled_gpsdlib = Library(env=env,
@@ -810,7 +847,7 @@ if qt_env:
# The libraries have dependencies on system libraries

gpslibs = ["-lgps", "-lm"]
gpsdlibs = ["-lgpsd"] + usblibs + bluezlibs + gpslibs
gpsdlibs = ["-lgpsd"] + usblibs + bluezlibs + gpslibs + caplibs

# Source groups

@@ -880,9 +917,6 @@ binaries = [gpsd, gpsdecode, gpsctl, gpsdctl, gpspipe, gpxlogger, lcdgps]
if ncurseslibs:
binaries += [cgps, gpsmon]

clockwatcher = env.Program('clockwatcher', ['clockwatcher.c'], parse_flags=gpslibs)
env.Depends(clockwatcher, compiled_gpslib)

# Test programs
test_float = env.Program('test_float', ['test_float.c'])
test_geoid = env.Program('test_geoid', ['test_geoid.c'], parse_flags=gpsdlibs)
@@ -894,7 +928,7 @@ env.Depends(test_mkgmtime, compiled_gpslib)
test_trig = env.Program('test_trig', ['test_trig.c'], parse_flags=["-lm"])
test_packet = env.Program('test_packet', ['test_packet.c'], parse_flags=gpsdlibs)
env.Depends(test_packet, [compiled_gpsdlib, compiled_gpslib])
test_bits = env.Program('test_bits', ['test_bits.c'], parse_flags=gpsdlibs)
test_bits = env.Program('test_bits', ['test_bits.c'], parse_flags=gpslibs)
env.Depends(test_bits, [compiled_gpsdlib, compiled_gpslib])
test_gpsmm = env.Program('test_gpsmm', ['test_gpsmm.cpp'], parse_flags=gpslibs)
env.Depends(test_gpsmm, compiled_gpslib)
@@ -1147,7 +1181,7 @@ if qt_env:
binaryinstall.append(LibraryInstall(qt_env, installdir('libdir'), compiled_qgpsmmlib))

# We don't use installdir here in order to avoid having DESTDIR affect the rpath
if env["shared"]:
if env["shared"] and env["chrpath"]:
env.AddPostAction(binaryinstall, '$CHRPATH -r "%s" "$TARGET"' \
% (installdir('libdir', False), ))

@@ -1202,6 +1236,14 @@ uninstall = env.Command('uninstall', '', Flatten(Uninstall(Alias("install"))) or
env.AlwaysBuild(uninstall)
env.Precious(uninstall)

# Target selection for '.' is badly broken. This is a general scons problem,
# not a glitch in this particular recipe. Avoid triggering the bug.

def error_action(target, source, env):
from SCons.Errors import UserError
raise UserError, "Target selection for '.' is broken."
AlwaysBuild(Alias(".", [], error_action))

# Utility productions

def Utility(target, source, action):
@@ -1243,7 +1285,7 @@ for (target,sources,description,params) in splint_table:
env.Alias('splint',Splint(target,sources,description,params))

Utility("cppcheck", ["gpsd.h", "packet_names.h"],
"cppcheck --template gcc --all --force $SRCDIR")
"cppcheck --template gcc --enable=all --inline-suppr --suppress='*:driver_proto.c' --force $SRCDIR")

# Sanity-check Python code. TODO: add xgps for the complete set.
Utility("pychecker", ["jsongen.py", "maskaudit.py"],
@@ -1277,6 +1319,11 @@ audit = env.Alias('audit',
# Note that the *-makeregress targets re-create the *.log.chk source
# files from the *.log source files.

# Unit-test the bitfield extractor
bits_regress = Utility('bits-regress', [test_bits], [
'$SRCDIR/test_bits --quiet'
])

# Check that all Python modules compile properly
if env['python']:
def check_compile(target, source, env):
@@ -1404,11 +1451,6 @@ json_regress = Utility('json-regress', [test_json], [
'$SRCDIR/test_json'
])

# Unit-test the bitfield extractor - not in normal tests
bits_regress = Utility('bits-regress', [test_bits], [
'$SRCDIR/test_bits'
])

# Run a valgrind audit on the daemon - not in normal tests
valgrind_audit = Utility('valgrind-audit', ['valgrind-audit.py'], 'valgrind-audit.py')

@@ -1418,6 +1460,7 @@ flocktest = Utility("flocktest", [], "cd devtools; flocktest " + gitrepo)
# Run all normal regression tests
check = env.Alias('check', [
python_compilation_regress,
bits_regress,
gps_regress,
rtcm_regress,
aivdm_regress,
@@ -1448,6 +1491,7 @@ webpages = Split('''www/installation.html
www/hardware.html
www/performance/performance.html
www/internals.html
www/cycle.png
''') + map(lambda f: f[:-3], glob.glob("www/*.in"))

www = env.Alias('www', webpages)
@@ -1464,7 +1508,8 @@ Utility("validation-list", [www], validation_list)

# How to update the website
upload_web = Utility("upload_web", [www],
['rsync --exclude="*.in" -avz www/ ' + webupload])
['rsync --exclude="*.in" -avz www/ ' + webupload,
'scp README TODO NEWS ' + webupload])

# When the URL declarations change, so must the generated web pages
for fn in glob.glob("www/*.in"):
@@ -1622,7 +1667,6 @@ if os.path.exists("gpsd.c") and os.path.exists(".gitignore"):
ship_release,
upload_tags])


# The following sets edit modes for GNU EMACS
# Local Variables:
# mode:python


+ 8
- 6
TODO View File

@@ -28,14 +28,15 @@ month is quite low.

To reproduce, "gpsd -D 5 -N -n udp://192.168.1.3:12346" (only
works inside thyrsus.com) and run xgps. Probably some kind of
packet aggregation issue.
packet aggregation issue as it doesn't happen with test logs.

*** Driver issues

**** There are architecture issues in the binary drivers.
**** There are portability issues in the rtcm2 driver

Some fail regression tests on non-x86 architectures (not SiRF or
NMEA, thankfully). Bernd Zeimetz has the check logs.
The rtcm2 binary driver fails port testing on the following
architectures: s390, s390x, and sparc. This is probably due
to the compiler not honoring #pragma pack(1).

**** gpsctl -b should work on UBX, but does not.

@@ -53,7 +54,8 @@ different types in them, and a copy of the RTCM3 standard at latest revision

This one is mine and Kurt Schwehr's. Support is currently nearly
complete; the only missing cases are a handful of IMO 236 and IMO 289
message 6 and 8 subtypes.
message 6 and 8 subtypes - specifically, 6/23, 8/21. 8/22, 8/24, and
8/26.

**** Addressed case of AIS Types 25 and 26 is not handled.

@@ -86,7 +88,7 @@ shouldn't be difficult.

*** Enable flocktest on the Debian server farm

Debian server farm boxes have a screwy chrooted envoronment setup.
Debian server farm boxes have a screwy chrooted envorinment setup.
flocktest needs to be modified to deal with it.

*** Finish gpssim


+ 27
- 2
ais_json.c View File

@@ -97,6 +97,7 @@ int json_ais_read(const char *buf,
ais->type4.hour = AIS_HOUR_NOT_AVAILABLE;
ais->type4.minute = AIS_MINUTE_NOT_AVAILABLE;
ais->type4.second = AIS_SECOND_NOT_AVAILABLE;
// cppcheck-suppress uninitvar
(void)sscanf(timestamp, "%4u-%02u-%02uT%02u:%02u:%02uZ",
&ais->type4.year,
&ais->type4.month,
@@ -112,6 +113,7 @@ int json_ais_read(const char *buf,
ais->type5.day = AIS_DAY_NOT_AVAILABLE;
ais->type5.hour = AIS_HOUR_NOT_AVAILABLE;
ais->type5.minute = AIS_MINUTE_NOT_AVAILABLE;
// cppcheck-suppress uninitvar
(void)sscanf(eta, "%02u-%02uT%02u:%02uZ",
&ais->type5.month,
&ais->type5.day,
@@ -128,6 +130,7 @@ int json_ais_read(const char *buf,
ais->type6.dac1fid12.lday = AIS_DAY_NOT_AVAILABLE;
ais->type6.dac1fid12.lhour = AIS_HOUR_NOT_AVAILABLE;
ais->type6.dac1fid12.lminute = AIS_MINUTE_NOT_AVAILABLE;
// cppcheck-suppress uninitvar
(void)sscanf(departure, "%02u-%02uT%02u:%02uZ",
&ais->type6.dac1fid12.lmonth,
&ais->type6.dac1fid12.lday,
@@ -137,6 +140,7 @@ int json_ais_read(const char *buf,
ais->type6.dac1fid12.nday = AIS_DAY_NOT_AVAILABLE;
ais->type6.dac1fid12.nhour = AIS_HOUR_NOT_AVAILABLE;
ais->type6.dac1fid12.nminute = AIS_MINUTE_NOT_AVAILABLE;
// cppcheck-suppress uninitvar
(void)sscanf(eta, "%02u-%02uT%02u:%02uZ",
&ais->type6.dac1fid12.nmonth,
&ais->type6.dac1fid12.nday,
@@ -159,6 +163,7 @@ int json_ais_read(const char *buf,
ais->type6.dac1fid18.day = AIS_DAY_NOT_AVAILABLE;
ais->type6.dac1fid18.hour = AIS_HOUR_NOT_AVAILABLE;
ais->type6.dac1fid18.minute = AIS_MINUTE_NOT_AVAILABLE;
// cppcheck-suppress uninitvar
(void)sscanf(arrival, "%02uT%02u:%02uZ",
&ais->type6.dac1fid18.day,
&ais->type6.dac1fid18.hour,
@@ -173,6 +178,7 @@ int json_ais_read(const char *buf,
ais->type6.dac1fid20.day = AIS_DAY_NOT_AVAILABLE;
ais->type6.dac1fid20.hour = AIS_HOUR_NOT_AVAILABLE;
ais->type6.dac1fid20.minute = AIS_MINUTE_NOT_AVAILABLE;
// cppcheck-suppress uninitvar
(void)sscanf(arrival, "%02u-%02uT%02u:%02uZ",
&ais->type6.dac1fid20.month,
&ais->type6.dac1fid20.day,
@@ -192,6 +198,7 @@ int json_ais_read(const char *buf,
ais->type6.dac1fid28.day = AIS_DAY_NOT_AVAILABLE;
ais->type6.dac1fid28.hour = AIS_HOUR_NOT_AVAILABLE;
ais->type6.dac1fid28.minute = AIS_MINUTE_NOT_AVAILABLE;
// cppcheck-suppress uninitvar
(void)sscanf(start, "%02u-%02uT%02u:%02uZ",
&ais->type6.dac1fid28.month,
&ais->type6.dac1fid28.day,
@@ -221,13 +228,28 @@ int json_ais_read(const char *buf,
} else if (strstr(buf, "\"type\":8,") != NULL) {
bool imo = false;
if (strstr(buf, "\"dac\":1,") != NULL) {
if (strstr(buf, "\"fid\":13,") != NULL) {
if (strstr(buf, "\"fid\":11,") != NULL) {
status = json_read_object(buf, json_ais8_fid11, endptr);
if (status == 0) {
ais->type8.dac1fid11.day = AIS_DAY_NOT_AVAILABLE;
ais->type8.dac1fid11.hour = AIS_HOUR_NOT_AVAILABLE;
ais->type8.dac1fid11.minute = AIS_MINUTE_NOT_AVAILABLE;
// cppcheck-suppress uninitvar
(void)sscanf(timestamp, "%02uT%02u:%02uZ",
&ais->type8.dac1fid11.day,
&ais->type8.dac1fid11.hour,
&ais->type8.dac1fid11.minute);
}
imo = true;
}
else if (strstr(buf, "\"fid\":13,") != NULL) {
status = json_read_object(buf, json_ais8_fid13, endptr);
if (status == 0) {
ais->type8.dac1fid13.fmonth = AIS_MONTH_NOT_AVAILABLE;
ais->type8.dac1fid13.fday = AIS_DAY_NOT_AVAILABLE;
ais->type8.dac1fid13.fhour = AIS_HOUR_NOT_AVAILABLE;
ais->type8.dac1fid13.fminute = AIS_MINUTE_NOT_AVAILABLE;
// cppcheck-suppress uninitvar
(void)sscanf(departure, "%02u-%02uT%02u:%02uZ",
&ais->type8.dac1fid13.fmonth,
&ais->type8.dac1fid13.fday,
@@ -237,6 +259,7 @@ int json_ais_read(const char *buf,
ais->type8.dac1fid13.tday = AIS_DAY_NOT_AVAILABLE;
ais->type8.dac1fid13.thour = AIS_HOUR_NOT_AVAILABLE;
ais->type8.dac1fid13.tminute = AIS_MINUTE_NOT_AVAILABLE;
// cppcheck-suppress uninitvar
(void)sscanf(eta, "%02u-%02uT%02u:%02uZ",
&ais->type8.dac1fid13.tmonth,
&ais->type8.dac1fid13.tday,
@@ -264,6 +287,7 @@ int json_ais_read(const char *buf,
ais->type8.dac1fid27.day = AIS_DAY_NOT_AVAILABLE;
ais->type8.dac1fid27.hour = AIS_HOUR_NOT_AVAILABLE;
ais->type8.dac1fid27.minute = AIS_MINUTE_NOT_AVAILABLE;
// cppcheck-suppress uninitvar
(void)sscanf(start, "%02u-%02uT%02u:%02uZ",
&ais->type8.dac1fid27.month,
&ais->type8.dac1fid27.day,
@@ -276,12 +300,13 @@ int json_ais_read(const char *buf,
status = json_read_object(buf, json_ais8_fid29, endptr);
imo = true;
}
else if (strstr(buf, "\"fid\":31,") != NULL || strstr(buf, "\"fid\":11,") != NULL) {
else if (strstr(buf, "\"fid\":31,") != NULL) {
status = json_read_object(buf, json_ais8_fid31, endptr);
if (status == 0) {
ais->type8.dac1fid31.day = AIS_DAY_NOT_AVAILABLE;
ais->type8.dac1fid31.hour = AIS_HOUR_NOT_AVAILABLE;
ais->type8.dac1fid31.minute = AIS_MINUTE_NOT_AVAILABLE;
// cppcheck-suppress uninitvar
(void)sscanf(eta, "%02uT%02u:%02uZ",
&ais->type8.dac1fid31.day,
&ais->type8.dac1fid31.hour,


+ 74
- 0
ais_json.i View File

@@ -342,6 +342,80 @@
{NULL}
};

const struct json_attr_t json_ais8_fid11[] = {
AIS_HEADER
AIS_TYPE8
{"lat", t_integer, .addr.integer = &ais->type8.dac1fid11.lat,
.dflt.integer = DAC1FID11_LAT_NOT_AVAILABLE},
{"lon", t_integer, .addr.integer = &ais->type8.dac1fid11.lon,
.dflt.integer = DAC1FID11_LON_NOT_AVAILABLE},
{"timestamp", t_string, .addr.string = timestamp,
.len = sizeof(timestamp)},
{"wspeed", t_uinteger, .addr.uinteger = &ais->type8.dac1fid11.wspeed,
.dflt.uinteger = DAC1FID11_WSPEED_NOT_AVAILABLE},
{"wgust", t_uinteger, .addr.uinteger = &ais->type8.dac1fid11.wgust,
.dflt.uinteger = DAC1FID11_WSPEED_NOT_AVAILABLE},
{"wdir", t_uinteger, .addr.uinteger = &ais->type8.dac1fid11.wdir,
.dflt.uinteger = DAC1FID11_WDIR_NOT_AVAILABLE},
{"wgustdir", t_uinteger, .addr.uinteger = &ais->type8.dac1fid11.wgustdir,
.dflt.uinteger = DAC1FID11_WDIR_NOT_AVAILABLE},
{"airtemp", t_integer, .addr.integer = &ais->type8.dac1fid11.airtemp,
.dflt.integer = DAC1FID11_AIRTEMP_NOT_AVAILABLE},
{"humidity", t_uinteger, .addr.uinteger = &ais->type8.dac1fid11.humidity,
.dflt.uinteger = DAC1FID11_HUMIDITY_NOT_AVAILABLE},
{"dewpoint", t_integer, .addr.integer = &ais->type8.dac1fid11.dewpoint,
.dflt.integer = DAC1FID11_DEWPOINT_NOT_AVAILABLE},
{"pressure", t_uinteger, .addr.uinteger = &ais->type8.dac1fid11.pressure,
.dflt.uinteger = DAC1FID11_PRESSURE_NOT_AVAILABLE},
{"pressuretend", t_uinteger, .addr.uinteger = &ais->type8.dac1fid11.pressuretend,
.dflt.uinteger = DAC1FID11_PRESSURETREND_NOT_AVAILABLE},
{"visibility", t_uinteger, .addr.uinteger = &ais->type8.dac1fid11.visibility,
.dflt.uinteger = DAC1FID11_VISIBILITY_NOT_AVAILABLE},
{"waterlevel", t_integer, .addr.integer = &ais->type8.dac1fid11.waterlevel,
.dflt.integer = DAC1FID11_WATERLEVEL_NOT_AVAILABLE},
{"leveltrend", t_uinteger, .addr.uinteger = &ais->type8.dac1fid11.leveltrend,
.dflt.uinteger = DAC1FID11_LEVELTREND_NOT_AVAILABLE},
{"cspeed", t_uinteger, .addr.uinteger = &ais->type8.dac1fid11.cspeed,
.dflt.uinteger = DAC1FID11_CSPEED_NOT_AVAILABLE},
{"cdir", t_uinteger, .addr.uinteger = &ais->type8.dac1fid11.cdir,
.dflt.uinteger = DAC1FID11_CDIR_NOT_AVAILABLE},
{"cspeed2", t_uinteger, .addr.uinteger = &ais->type8.dac1fid11.cspeed2,
.dflt.uinteger = DAC1FID11_CSPEED_NOT_AVAILABLE},
{"cdir2", t_uinteger, .addr.uinteger = &ais->type8.dac1fid11.cdir2,
.dflt.uinteger = DAC1FID11_CDIR_NOT_AVAILABLE},
{"cdepth2", t_uinteger, .addr.uinteger = &ais->type8.dac1fid11.cdepth2,
.dflt.uinteger = DAC1FID11_CDEPTH_NOT_AVAILABLE},
{"cspeed3", t_uinteger, .addr.uinteger = &ais->type8.dac1fid11.cspeed3,
.dflt.uinteger = DAC1FID11_CSPEED_NOT_AVAILABLE},
{"cdir3", t_uinteger, .addr.uinteger = &ais->type8.dac1fid11.cdir3,
.dflt.uinteger = DAC1FID11_CDIR_NOT_AVAILABLE},
{"cdepth3", t_uinteger, .addr.uinteger = &ais->type8.dac1fid11.cdepth3,
.dflt.uinteger = DAC1FID11_CDEPTH_NOT_AVAILABLE},
{"waveheight", t_uinteger, .addr.uinteger = &ais->type8.dac1fid11.waveheight,
.dflt.uinteger = DAC1FID11_WAVEHEIGHT_NOT_AVAILABLE},
{"waveperiod", t_uinteger, .addr.uinteger = &ais->type8.dac1fid11.waveperiod,
.dflt.uinteger = DAC1FID11_WAVEPERIOD_NOT_AVAILABLE},
{"wavedir", t_uinteger, .addr.uinteger = &ais->type8.dac1fid11.wavedir,
.dflt.uinteger = DAC1FID11_WAVEDIR_NOT_AVAILABLE},
{"swellheight", t_uinteger, .addr.uinteger = &ais->type8.dac1fid11.swellheight,
.dflt.uinteger = DAC1FID11_WAVEHEIGHT_NOT_AVAILABLE},
{"swellperiod", t_uinteger, .addr.uinteger = &ais->type8.dac1fid11.swellperiod,
.dflt.uinteger = DAC1FID11_WAVEPERIOD_NOT_AVAILABLE},
{"swelldir", t_uinteger, .addr.uinteger = &ais->type8.dac1fid11.swelldir,
.dflt.uinteger = DAC1FID11_WAVEDIR_NOT_AVAILABLE},
{"seastate", t_uinteger, .addr.uinteger = &ais->type8.dac1fid11.seastate,
.dflt.uinteger = DAC1FID11_SEASTATE_NOT_AVAILABLE},
{"watertemp", t_integer, .addr.integer = &ais->type8.dac1fid11.watertemp,
.dflt.integer = DAC1FID11_WATERTEMP_NOT_AVAILABLE},
{"preciptype", t_uinteger, .addr.uinteger = &ais->type8.dac1fid11.preciptype,
.dflt.uinteger = DAC1FID11_PRECIPTYPE_NOT_AVAILABLE},
{"salinity", t_uinteger, .addr.uinteger = &ais->type8.dac1fid11.salinity,
.dflt.uinteger = DAC1FID11_SALINITY_NOT_AVAILABLE},
{"ice", t_uinteger, .addr.uinteger = &ais->type8.dac1fid11.ice,
.dflt.uinteger = DAC1FID11_ICE_NOT_AVAILABLE},
{NULL}
};

const struct json_attr_t json_ais8_fid13[] = {
AIS_HEADER
AIS_TYPE8


+ 81
- 44
bits.c View File

@@ -7,84 +7,121 @@
* a byte index - and width is a bit width. The width is bounded above by
* 64 bits.
*
* The sbits() function assumes twos-complement arithmetic.
* The sbits() function assumes twos-complement arithmetic. ubits()
* and sbits() assume no padding in integers.
*/
#include <assert.h>
#include <stdint.h>
#include <stdbool.h>
#include <limits.h>

#include "bits.h"
#ifdef DEBUG
#include <stdio.h>
#include "gpsd.h"
#endif /* DEBUG */

#define BITS_PER_BYTE 8

uint64_t ubits(char buf[], unsigned int start, unsigned int width)
uint64_t ubits(char buf[], unsigned int start, unsigned int width, bool le)
/* extract a (zero-origin) bitfield from the buffer as an unsigned big-endian uint64_t */
{
uint64_t fld = 0;
unsigned int i;
unsigned end;

/*@i1@*/ assert(width <= sizeof(uint64_t) * BITS_PER_BYTE);
for (i = start / BITS_PER_BYTE;
i < (start + width + BITS_PER_BYTE - 1) / BITS_PER_BYTE; i++) {
fld <<= BITS_PER_BYTE;
/*@i1@*/ assert(width <= sizeof(uint64_t) * CHAR_BIT);
for (i = start / CHAR_BIT;
i < (start + width + CHAR_BIT - 1) / CHAR_BIT; i++) {
/*@i1@*/fld <<= CHAR_BIT;
fld |= (unsigned char)buf[i];
}
#ifdef DEBUG
(void)printf("%d:%d from %s:\n", start, width, gpsd_hexdump(buf, 32));
#endif

#ifdef DEBUG
(void)printf(" segment=0x%llx,", fld);
#endif /* DEBUG */
end = (start + width) % BITS_PER_BYTE;
end = (start + width) % CHAR_BIT;
if (end != 0) {
fld >>= (BITS_PER_BYTE - end);
#ifdef DEBUG
(void)printf(" after downshifting by %d bits: 0x%llx",
BITS_PER_BYTE - end, fld);
#endif /* UDEBUG */
/*@i1@*/fld >>= (CHAR_BIT - end);
}
#ifdef DEBUG
(void)printf(" = %lld\n", fld);
#endif /* UDEBUG */

/*@ -shiftimplementation @*/
fld &= ~(-1LL << width);
/*@ +shiftimplementation @*/
#ifdef DEBUG
(void)
printf(" after selecting out the bottom %u bits: 0x%llx = %lld\n",
width, fld, fld);
#endif /* DEBUG */

/* was extraction as a little-endian requested? */
if (le)
{
uint64_t reversed = 0;

for (i = width; i; --i)
{
reversed <<= 1;
if (fld & 1)
reversed |= 1;
fld >>= 1;
}
fld = reversed;
}

return fld;
}

int64_t sbits(char buf[], unsigned int start, unsigned int width)
int64_t sbits(char buf[], unsigned int start, unsigned int width, bool le)
/* extract a bitfield from the buffer as a signed big-endian long */
{
uint64_t fld = ubits(buf, start, width);
uint64_t fld = ubits(buf, start, width, le);

#ifdef __UNUSED_DEBUG__
(void)fprintf(stderr, "sbits(%d, %d) extracts %llx\n", start, width, fld);
#endif /* __UNUSED_DEBUG__ */
/*@ +relaxtypes */
if (fld & (1LL << (width - 1))) {
#ifdef __UNUSED_DEBUG__
(void)fprintf(stderr, "%llx is signed\n", fld);
#endif /* __UNUSED_DEBUG__ */
/*@ -shiftimplementation @*/
fld |= (-1LL << (width - 1));
/*@ +shiftimplementation @*/
}
#ifdef __UNUSED_DEBUG__
(void)fprintf(stderr, "sbits(%d, %d) returns %lld\n", start, width,
(int64_t)fld);
#endif /* __UNUSED_DEBUG__ */
return (int64_t)fld;
/*@ -relaxtypes */
}

#ifdef __UNUSED__
// cppcheck-suppress unusedFunction
u_int16_t swap_u16(u_int16_t i)
/* byte-swap a 16-bit unsigned int */
{
u_int8_t c1, c2;
c1 = i & 255;
c2 = (i >> 8) & 255;
return (c1 << 8) + c2;
}
// cppcheck-suppress unusedFunction
u_int32_t swap_u32(u_int32_t i)
/* byte-swap a 32-bit unsigned int */
{
u_int8_t c1, c2, c3, c4;
c1 = i & 255;
c2 = (i >> 8) & 255;
c3 = (i >> 16) & 255;
c4 = (i >> 24) & 255;
return ((u_int32_t)c1 << 24) + ((u_int32_t)c2 << 16) + ((u_int32_t)c3 << 8) + c4;
}
// cppcheck-suppress unusedFunction
u_int64_t swap_u64(u_int64_t i)
/* byte-swap a 64-bit unsigned int */
{
u_int8_t c1, c2, c3, c4, c5, c6, c7, c8;
c1 = i & 255;
c2 = (i >> 8) & 255;
c3 = (i >> 16) & 255;
c4 = (i >> 24) & 255;
c5 = (i >> 32) & 255;
c6 = (i >> 40) & 255;
c7 = (i >> 48) & 255;
c8 = (i >> 56) & 255;
return ((u_int64_t)c1 << 56) +
((u_int64_t)c2 << 48) +
((u_int64_t)c3 << 40) +
((u_int64_t)c4 << 32) +
((u_int64_t)c5 << 24) +
((u_int64_t)c6 << 16) +
((u_int64_t)c7 << 8) +
c8;
}
#endif /* __UNUSED__ */

+ 4
- 15
bits.h View File

@@ -63,25 +63,14 @@ union long_double {
#define getbes64(buf, off) ((int64_t)(((uint64_t)getbeu32(buf, (off)) << 32) | getbeu32(buf, (off)+4)))
#define getbeu64(buf, off) ((uint64_t)(((uint64_t)getbeu32(buf, (off)) << 32) | getbeu32(buf, (off)+4)))

#define putbe16(buf,off,w) do {putbyte(buf, (off) ,(w) >> 8); putbyte(buf, (off)+1, (w));} while (0)
#define putbe32(buf,off,l) do {putbe16(buf, (off) ,(l) >> 16); putbe16(buf, (off)+2, (l));} while (0)
#define putbe16(buf,off,w) do {putbyte(buf, (off), (w) >> 8); putbyte(buf, (off)+1, (w));} while (0)
#define putbe32(buf,off,l) do {putbe16(buf, (off), (l) >> 16); putbe16(buf, (off)+2, (l));} while (0)

#define getbef(buf, off) (i_f.i = getbes32(buf, off), i_f.f)
#define getbed(buf, off) (l_d.l = getbes64(buf, off), l_d.d)


/* Zodiac protocol description uses 1-origin indexing by little-endian word */
#define get16z(buf, n) ( (buf[2*(n)-2]) \
| (buf[2*(n)-1] << 8))
#define get32z(buf, n) ( (buf[2*(n)-2]) \
| (buf[2*(n)-1] << 8) \
| (buf[2*(n)+0] << 16) \
| (buf[2*(n)+1] << 24))
#define getstringz(to, from, s, e) \
(void)memcpy(to, from+2*(s)-2, 2*((e)-(s)+1))

/* bitfield extraction */
extern uint64_t ubits(char buf[], unsigned int, unsigned int);
extern int64_t sbits(char buf[], unsigned int, unsigned int);
extern uint64_t ubits(char buf[], unsigned int, unsigned int, bool);
extern int64_t sbits(char buf[], unsigned int, unsigned int, bool);

#endif /* _GPSD_BITS_H_ */

+ 25
- 4
build.txt View File

@@ -70,16 +70,37 @@ autotools build from 2.96 and earlier versions has been dropped.
chrpath is a tool for editing RPATH in object files.

libtool is a messy pile of crocks, and throwing it out of our build
chain was a good thing, but it had consguences. Whatever its other
flawes, libtool successfully hid some tricky problems related to
chain was a good thing, but it had consequences. Whatever its other
flaws, libtool successfully hid some tricky problems related to
dynamic-linkage paths; to address these in the scons build, you need
chrpath present to edit those paths.

Ubuntu users can do 'apt-get install chrpath'
CentOS users can do 'yum install chrpath' from extras.

If you do not have chrpath available, GPSD binaries will be built
statically.
Some distribution makers have considered the use of chrpath to be a
wart on the build recipe.

Here's the problem. Ideally, we want to build build binaries that (a)
link dynamically, (b) can be tested in the build directory without
installing to system space (in particular, so we can run the regression
tests without disturbing a production installation) and (c) won't
carry a potential exploit into system space when the binaries are
installed.

The potential exploit is the remnant presence of the build directory in
the binary's internal list of places it will look for shared libraries.
We need that to be there for testing purposes, but we want it gone
in the version of the binary that's copied to /usr/lib. Otherwise
there are threat scenarios with a maliciously crafted library.

Without chrpath we can get any two of those three, but we can't get
all three. Choosing static linking we get (b) and (c), choosing
dynamic linking without chrpath we get (a) and (b).

If you configure with chrpath=no, you will suppress use of chrpath,
but this also means you will lose the ability to run regression tests
in the build directory

=== Optional build components ===



+ 1
- 1
cgps.1 View File

@@ -1 +1 @@
.so man1/gps.1
.so gps.1

+ 4
- 2
contrib/ashctl.c View File

@@ -135,10 +135,12 @@ static void config_raw(int fd){
static void nmea_add_checksum(char *sentence)
/* add NMEA checksum to a possibly *-terminated sentence */
{
unsigned char sum = '\0';
char c, *p = sentence;
char *p = sentence;

if (*p == '$') {
unsigned char sum = '\0';
char c;

p++;
while ( ((c = *p) != '*') && (c != '\0')) {
sum ^= c;


+ 1
- 1
contrib/motosend.c View File

@@ -115,7 +115,7 @@ char moto_gen_checksum(char *buf, int len){
static int moto_send(int fd, char *type, char *body ) {
size_t status;
char *buf;
unsigned short l, ck;
unsigned short l;

l = strlen(body) / 2;
if ((buf = malloc(l+7)) == NULL)


+ 1
- 0
daemon.c View File

@@ -45,6 +45,7 @@ int daemon(int nochdir, int noclose)
(void)close(fd);
}
/*@ +nullpass @*/
/* coverity[leaked_handle] Intentional handle duplication */
return 0;
}



+ 0
- 5
devtools/README View File

@@ -63,11 +63,6 @@ list of failed regression tests, sorted by architecture.
Extract pure NMEA from an emailed gpsd error log. The output can be fed
to gpsfake.

== ppstest ==

Simple program to check for PPS coming from a serial or USB device.
Compile with "make ppstest". See the header comment for instructions.

== regress-builder ==

This script runs an exhaustive test on combinations of compilation options,


+ 158
- 3
devtools/ais.py View File

@@ -328,11 +328,155 @@ type7 = (
spare(2),
)

#
# Type 8 have subtypes identified by DAC (Designated Area Code) and FID (Functional ID)
#

def type8_latlon_format(n):
return str(n / 60000.0)

type8_dac_or_fid_unknown = (
bitfield("data", 952, 'raw', None, "Data"),
)

type8_dispatch = {}
type8_dispatch[0] = type8_dac_or_fid_unknown

# DAC 1 (international)
type8_dac1_dispatch = {}
type8_dac1_dispatch[0] = type8_dac_or_fid_unknown

# DAC 1, FID 11: IMO236 Met/Hydro message
def type8_dac1_fid11_airtemp_format(n):
return str(n * 0.1 - 60)

def type8_dac1_fid11_dewpoint_format(n):
return str(n * 0.1 - 20)

def type8_dac1_fid11_pressure_format(n):
return str(n + 800)

def type8_dac1_fid11_visibility_format(n):
return str(n * 0.1)

def type8_dac1_fid11_waterlevel_format(n):
return str(n * 0.1 - 10)

def type8_dac1_fid11_cspeed_format(n):
return str(n * 0.1)

def type8_dac1_fid11_waveheight_format(n):
return str(n * 0.1)

type8_dac1_fid11_seastate_legend = (
"Calm",
"Light air",
"Light breeze"
"Gentle breeze",
"Moderate breeze",
"Fresh breeze",
"Strong breeze",
"High wind",
"Gale",
"Strong gale",
"Storm",
"Violent storm",
"Hurricane force",
"Reserved",
"Reserved",
"Reserved"
)

def type8_dac1_fid11_watertemp_format(n):
return str(n * 0.1 - 10)

type8_dac1_fid11_preciptype_legend = (
"Reserved",
"Rain",
"Thunderstorm",
"Freezing rain",
"Mixed/ice",
"Snow",
"Reserved",
"Reserved"
)

def type8_dac1_fid11_salinity_format(n):
return str(n * 0.1)

type8_dac1_fid11_ice_legend = (
"Yes",
"No"
)

type8_dac1_fid11 = (
bitfield("lat", 24, "signed", 2**24-1, "Latitude",
formatter=type8_latlon_format),
bitfield("lon", 25, "signed", 2**25-1, "Longitude",
formatter=type8_latlon_format),
bitfield("day", 5, 'unsigned', 0, "ETA day"),
bitfield("hour", 5, 'unsigned', 24, "ETA hour"),
bitfield("minute", 6, 'unsigned', 60, "ETA minute"),
bitfield("wspeed", 7, 'unsigned', 127, "Wind speed"),
bitfield("wgust", 7, 'unsigned', 127, "Wind gust"),
bitfield("wdir", 9, 'unsigned', 511, "Wind direction"),
bitfield("wgustdir", 9, 'unsigned', 511, "Wind gust direction"),
bitfield("airtemp", 11, 'unsigned', 2047, "Air temperature",
formatter=type8_dac1_fid11_airtemp_format),
bitfield("humidity", 7, 'unsigned', 127, "Relative humidity"),
bitfield("dewpoint", 10, 'unsigned', 1023, "Dew point",
formatter=type8_dac1_fid11_dewpoint_format),
bitfield("pressure", 9, 'unsigned', 511, "Atmospheric pressure",
formatter=type8_dac1_fid11_pressure_format),
bitfield("pressuretend", 2, 'unsigned', 3, "Atmospheric pressure tendency"),
bitfield("visibility", 8, 'unsigned', 255, "Horizontal visibility",
formatter=type8_dac1_fid11_visibility_format),
bitfield("waterlevel", 9, 'unsigned', 511, "Water level",
formatter=type8_dac1_fid11_waterlevel_format),
bitfield("leveltrend", 2, 'unsigned', 3, "Water level trend"),
bitfield("cspeed", 8, 'unsigned', 255, "Surface current speed",
formatter=type8_dac1_fid11_cspeed_format),
bitfield("cdir", 9, 'unsigned', 511, "Surface current direction"),
bitfield("cspeed2", 8, 'unsigned', 255, "Current speed #2",
formatter=type8_dac1_fid11_cspeed_format),
bitfield("cdir2", 9, 'unsigned', 511, "Current direction #2"),
bitfield("cdepth2", 5, 'unsigned', 31, "Current measuring level #2"),
bitfield("cspeed3", 8, 'unsigned', 255, "Current speed #3",
formatter=type8_dac1_fid11_cspeed_format),
bitfield("cdir3", 9, 'unsigned', 511, "Current direction #3"),
bitfield("cdepth3", 5, 'unsigned', 31, "Current measuring level #3"),
bitfield("waveheight", 8, 'unsigned', 255, "Significant wave height",
formatter=type8_dac1_fid11_waveheight_format),
bitfield("waveperiod", 6, 'unsigned', 63, "Significant wave period"),
bitfield("wavedir", 9, 'unsigned', 511, "Significant wave direction"),
bitfield("swellheight", 8, 'unsigned', 255, "Swell height",
formatter=type8_dac1_fid11_waveheight_format),
bitfield("swellperiod", 6, 'unsigned', 63, "Swell period"),
bitfield("swelldir", 9, 'unsigned', 511, "Swell direction"),
bitfield("seastate", 4, 'unsigned', 15, "Sea state",
formatter=type8_dac1_fid11_seastate_legend),
bitfield("watertemp", 10, 'unsigned', 1023, "Water temperature",
formatter=type8_dac1_fid11_watertemp_format),
bitfield("preciptype", 3, 'unsigned', 7, "Precipitation type",
formatter=type8_dac1_fid11_preciptype_legend),
bitfield("salinity", 9, 'unsigned', 511, "Salinity",
formatter=type8_dac1_fid11_salinity_format),
bitfield("ice", 2, 'unsigned', 3, "Ice?",
formatter=type8_dac1_fid11_ice_legend),
spare(6)
)
type8_dac1_dispatch[11] = type8_dac1_fid11

type8_dac1 = (
dispatch("fid", type8_dac1_dispatch, lambda m: m if m in type8_dac1_dispatch else 0),
)
type8_dispatch[1] = type8_dac1

type8 = (
spare(2),
bitfield("dac", 10, 'unsigned', 0, "DAC"),
bitfield("fid", 6, 'unsigned', 0, "Functional ID"),
bitfield("data", 952, 'raw', None, "Data"),
dispatch("dac", type8_dispatch, lambda m: m if m in type8_dispatch else 0),
)

def type9_alt_format(n):
@@ -1067,7 +1211,18 @@ if __name__ == "__main__":
elif dsv:
print "|".join(map(lambda x: str(x[1]), parsed))
elif histogram:
frequencies[msgtype] = frequencies.get(msgtype, 0) + 1
key = "%02d" % msgtype
frequencies[key] = frequencies.get(key, 0) + 1
if msgtype == 6 or msgtype == 8:
dac = 0; fid = 0
if msgtype == 8:
dac = parsed[3][1]
fid = parsed[4][1]
elif msgtype == 6:
dac = parsed[6][1]
fid = parsed[7][1]
key = "%02d_%04d_%02d" % (msgtype, dac, fid)
frequencies[key] = frequencies.get(key, 0) + 1
elif dump:
for (inst, value) in parsed:
print "%-25s: %s" % (inst.legend, value)
@@ -1077,7 +1232,7 @@ if __name__ == "__main__":
keys = frequencies.keys()
keys.sort()
for msgtype in keys:
print "%d\t%d" % (msgtype, frequencies[msgtype])
print "%-33s\t%d" % (msgtype, frequencies[msgtype])
except KeyboardInterrupt:
pass
# End

+ 1
- 0
devtools/gpsd-debian-regressions.sh View File

@@ -20,6 +20,7 @@ OLDPWD=`pwd`

cd ${TMPDIR}
getbuildlog gpsd $logs || true
grep -c -- '--- test' * | grep -E ':0$' | sed 's,^gpsd_[^_]*_\([^.]*\)\.log:0,\1: no regressions,'
grep -- '--- test' * | sed 's,^gpsd_[^_]*_\([^.]*\).*\./test/\([^.]*\).*,\1 \2,' | sort -u
cd ${OLDPWD}
rm -rf ${TMPDIR}


+ 0
- 201
devtools/ppstest.c View File

@@ -1,201 +0,0 @@
#include <termios.h>
#include <sys/types.h>
#include <stdio.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <string.h>
#include <stdlib.h>
#include <getopt.h>

/*
* Test to see if TIOCMGET/TIOCMIWAIT can be made to work
* Call with the serial device name argument, and possibly -p or -w options
*
* Based on code fond here:
* http://tech.groups.yahoo.com/group/ts-7000/message/803
*/
int main(int argc, char **argv)
{
int fd;
struct termios newtio;
unsigned char rx[132];
enum {dump, poll, wait} mode = dump;
int option;
char *device;

struct {char *name; int value;} pin_map[] = {
{"CTS", TIOCM_CTS}, /* Clear to send */
{"CAR", TIOCM_CAR}, /* Carrier detect */
{"DCD", TIOCM_CD}, /* Carrier detect (synonym - default) */
{"RI", TIOCM_RI}, /* Ring Indicator */
{"RNG", TIOCM_RNG}, /* Ring Indicator (synonym) */
{"DSR", TIOCM_DSR}, /* Data Set Ready */
{NULL, 0},
}, *pp = &pin_map[2];

while ((option = getopt(argc, argv, "dl:pw")) != -1) {
switch (option) {
case 'd': /* dump data from the TTY */
mode = dump;
break;
case 'p': /* poll for PPS */
mode = poll;
break;
case 'w': /* wait for PPS state change */
mode = wait;
break;
case 'l': /* set the handshake line to use by name */
for (pp = pin_map; pp->name != NULL; pp++)
if (strcmp(pp->name, optarg) == 0) {
break;
}
if (pp->name == NULL) {
(void)fprintf(stderr,
"Didn't recognize %s as a handshake.\n",
optarg);
exit(1);
}
break;
case '?':
case 'h':
(void)fprintf(stderr, "usage: ppstest [-p] [-w] [-l CTS|CAR|RI|RNG|DSR] device\n");
exit(0);
}
}
device = argv[optind];

// try to open the serial port
fd = open(device, O_RDWR | O_NOCTTY | O_NDELAY);
// how'd that go?
if (fd < 0) {
// not so good
perror("Unable to open device /dev/ttyAM0\n");
return 1;
}
fprintf(stderr, "Successfully opened serial device %s\n", device);

/* initialize the serial port */
memset(&newtio, '\0', sizeof(newtio));
newtio.c_cflag = CS8 | CREAD | CRTSCTS;
tcflush(fd, TCIOFLUSH);
tcsetattr(fd,TCSANOW,&newtio);

// Figure out which mode we are operating in, based on the command
// line argument.
//
// Operating Modes:
// mode = dump - Dump out serial data from port
// mode = wait - Use TIOCMIWAIT to detect changes on the line -
// mode = poll - Use TIOCMGET to detect changes on the line via polling

// Select operation based on mode
switch (mode)
{
case dump:
// just dump out any characters arriving on the serial port
fprintf(stderr, "Testing Serial Interface. Dumping data from %s\n", device);
int readCnt;
while (1) {
while (read(fd, rx, 132) > 0) {
rx[131] = 0;
fprintf(stderr, "%s", rx);
}
sleep(1);
}
break;

case wait:
// wait for transition to be reported via interrupt
(void)fprintf(stderr,
"Testing TIOCMIWAIT. Waiting for %s on %s\n",
pp->name, device);
while (ioctl(fd, TIOCMIWAIT, pp->value) == 0) {
(void)fprintf(stderr, "%s Transition on %s\n", pp->name, device);
}
(void)fprintf(stderr,
"TIOCMIWAIT returns nonzero value on %s!\n",
device);
break;

case poll:
{
struct timeval tv_jw;
int state, lastState;
double lastTime = 0;
// for computing average length of a second, as derived from the
// rising edge of the pulse
double total = 0;
int samples = 0;

// poll selected line looking for transition; when found, report
// time and various intervals between successive transitions.
(void)fprintf(stderr,
"Testing TIOCMGET. Polling %s on %s\n",
pp->name, device);

// get the current state of the line
if (ioctl(fd, TIOCMGET, &lastState) != 0) {
(void)fprintf(stderr, "TIOCMGET fails on %s\n", device);
exit(1);
}
lastState = (int)((lastState & pp->value) != 0);

// loop forever
while (1) {
// get the value of the serial lines
if (ioctl(fd, TIOCMGET, &state) != 0) {
// abort on error
(void)fprintf(stderr, "TIOCMGET fails on %s\n", device);
exit(1);
}
// recover line state
state = (int)((state & pp->value) != 0);

// Transition?
if (state != lastState) {
// yes. Update the last state
lastState = state;
// Is this a leading (rising) edge?
if (state == 1) {
// yes. let's call this the top of the second
// note the system time
(void)gettimeofday(&tv_jw,NULL);
// turn it into a double
double curTime = tv_jw.tv_sec + tv_jw.tv_usec/1.0e6;
// how long since the last transition?
double diff = curTime - lastTime;
// Update time of 'last' state transition
lastTime = curTime;
// diff should be (really close to) one second
// is diff within reason? (Sometimes transitions appear to be missed)
if (diff < 1.5) {
// update for averaging
total += diff;
samples++;
// report on the times associated with this transition
(void)fprintf(stderr,
"%s transition on %s: %d: %.6f, %6f, %6f\n",
pp->name, device, state, curTime, diff, total/samples);
}
else {
(void)fprintf(stderr,
"%s transition on %s: %d: %.6f, %6f - wacky diff\n",
pp->name, device, state, curTime, diff);
}
}
}
// now sleep for a (very) little while
tv_jw.tv_sec = 0;
tv_jw.tv_usec = 1;
int retval_jw = select(1, NULL, NULL, NULL, &tv_jw);
}
}
break;

default:
(void)fprintf(stderr, "Unknown mode\n");
}

(void)close(fd);
return 0;
}

+ 976
- 0
driver_ais.c View File

@@ -0,0 +1,976 @@
/*
* Driver for AIS messages.
*
* See the file AIVDM.txt on the GPSD website for documentation and references.
* AIVDM de-armoring is handled elsewhere; this is the binary-packet driver.
*
* Code for message types 1-15, 18-21, and 24 has been tested against
* live data with known-good decodings. Code for message types 16-17,
* 22-23, and 25-27 has not.
* For the special IMO messages (types 6 and 8), only the following have been
* tested against known-good decodings:
* - IMO236 met/hydro message: Type=8, DAC=1, FI=11
*
* This file is Copyright (c) 2010 by the GPSD project
* BSD terms apply: see the file COPYING in the distribution root for details.
*/
#include <stdlib.h>
#include <string.h>
#include <ctype.h>

#include "gpsd.h"
#include "bits.h"

/*
* Parse the data from the device
*/

#define DAC1FID11_AIRTEMP_OFFSET 600
#define DAC1FID11_DEWPOINT_OFFSET 200
#define DAC1FID11_PRESSURE_OFFSET -800
#define DAC1FID11_LEVEL_OFFSET 100
#define DAC1FID11_WATERTEMP_OFFSET 100

#define DAC1FID31_AIRTEMP_OFFSET 600
#define DAC1FID31_DEWPOINT_OFFSET 200
#define DAC1FID31_PRESSURE_OFFSET -800
#define DAC1FID31_LEVEL_OFFSET 100
#define DAC1FID31_WATERTEMP_OFFSET 100

static void from_sixbit(char *bitvec, uint start, int count, char *to)
{
/*@ +type @*/
#ifdef S_SPLINT_S
/* the real string causes a splint internal error */
const char sixchr[] = "abcd";
#else
const char sixchr[64] =
"@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^- !\"#$%&'()*+,-./0123456789:;<=>?";
#endif /* S_SPLINT_S */
int i;
char newchar;

/* six-bit to ASCII */
for (i = 0; i < count - 1; i++) {
newchar = sixchr[ubits(bitvec, start + 6 * i, 6U, false)];
if (newchar == '@')
break;
else
to[i] = newchar;
}
to[i] = '\0';
/* trim spaces on right end */
for (i = count - 2; i >= 0; i--)
if (to[i] == ' ' || to[i] == '@')
to[i] = '\0';
else
break;
/*@ -type @*/
}

/*@ +charint @*/
bool ais_binary_decode(struct ais_t *ais,
const unsigned char *bits, size_t bitlen,
struct ais_type24_queue_t *type24_queue)
/* decode an AIS binary packet */
{
bool imo;
unsigned int u;
int i;

#define BITS_PER_BYTE 8
#define UBITS(s, l) ubits((char *)bits, s, l, false)
#define SBITS(s, l) sbits((char *)bits, s, l, false)
#define UCHARS(s, to) from_sixbit((char *)bits, s, sizeof(to), to)
ais->type = UBITS(0, 6);
ais->repeat = UBITS(6, 2);
ais->mmsi = UBITS(8, 30);
gpsd_report(LOG_INF, "AIVDM message type %d, MMSI %09d:\n",
ais->type, ais->mmsi);
/*
* Something about the shape of this switch statement confuses
* GNU indent so badly that there is no point in trying to be
* finer-grained than leaving it all alone.
*/
/* *INDENT-OFF* */
switch (ais->type) {
case 1: /* Position Report */
case 2:
case 3:
if (bitlen != 168) {
gpsd_report(LOG_WARN, "AIVDM message type %d size not 168 bits (%zd).\n",
ais->type,
bitlen);
return false;
}
ais->type1.status = UBITS(38, 4);
ais->type1.turn = SBITS(42, 8);
ais->type1.speed = UBITS(50, 10);
ais->type1.accuracy = UBITS(60, 1)!=0;
ais->type1.lon = SBITS(61, 28);
ais->type1.lat = SBITS(89, 27);
ais->type1.course = UBITS(116, 12);
ais->type1.heading = UBITS(128, 9);
ais->type1.second = UBITS(137, 6);
ais->type1.maneuver = UBITS(143, 2);
//ais->type1.spare = UBITS(145, 3);
ais->type1.raim = UBITS(148, 1)!=0;
ais->type1.radio = UBITS(149, 20);
break;
case 4: /* Base Station Report */
case 11: /* UTC/Date Response */
if (bitlen != 168) {
gpsd_report(LOG_WARN, "AIVDM message type %d size not 168 bits (%zd).\n",
ais->type,
bitlen);
return false;
}
ais->type4.year = UBITS(38, 14);
ais->type4.month = UBITS(52, 4);
ais->type4.day = UBITS(56, 5);
ais->type4.hour = UBITS(61, 5);
ais->type4.minute = UBITS(66, 6);
ais->type4.second = UBITS(72, 6);
ais->type4.accuracy = UBITS(78, 1)!=0;
ais->type4.lon = SBITS(79, 28);
ais->type4.lat = SBITS(107, 27);
ais->type4.epfd = UBITS(134, 4);
//ais->type4.spare = UBITS(138, 10);
ais->type4.raim = UBITS(148, 1)!=0;
ais->type4.radio = UBITS(149, 19);
break;
case 5: /* Ship static and voyage related data */
if (bitlen != 424) {
gpsd_report(LOG_WARN, "AIVDM message type 5 size not 424 bits (%zd).\n",
bitlen);
return false;
}
ais->type5.ais_version = UBITS(38, 2);
ais->type5.imo = UBITS(40, 30);
UCHARS(70, ais->type5.callsign);
UCHARS(112, ais->type5.shipname);
ais->type5.shiptype = UBITS(232, 8);
ais->type5.to_bow = UBITS(240, 9);
ais->type5.to_stern = UBITS(249, 9);
ais->type5.to_port = UBITS(258, 6);
ais->type5.to_starboard = UBITS(264, 6);
ais->type5.epfd = UBITS(270, 4);
ais->type5.month = UBITS(274, 4);
ais->type5.day = UBITS(278, 5);
ais->type5.hour = UBITS(283, 5);
ais->type5.minute = UBITS(288, 6);
ais->type5.draught = UBITS(294, 8);
UCHARS(302, ais->type5.destination);
ais->type5.dte = UBITS(422, 1);
//ais->type5.spare = UBITS(423, 1);
break;
case 6: /* Addressed Binary Message */
if (bitlen < 88 || bitlen > 1008) {
gpsd_report(LOG_WARN, "AIVDM message type 6 size is out of range (%zd).\n",
bitlen);
return false;
}
ais->type6.seqno = UBITS(38, 2);
ais->type6.dest_mmsi = UBITS(40, 30);
ais->type6.retransmit = UBITS(70, 1)!=0;
//ais->type6.spare = UBITS(71, 1);
ais->type6.dac = UBITS(72, 10);
ais->type6.fid = UBITS(82, 6);
ais->type6.bitcount = bitlen - 88;
imo = false;
if (ais->type6.dac == 1)
switch (ais->type6.fid) {
case 12: /* IMO236 - Dangerous cargo indication */
UCHARS(88, ais->type6.dac1fid12.lastport);
ais->type6.dac1fid12.lmonth = UBITS(118, 4);
ais->type6.dac1fid12.lday = UBITS(122, 5);
ais->type6.dac1fid12.lhour = UBITS(127, 5);
ais->type6.dac1fid12.lminute = UBITS(132, 6);
UCHARS(138, ais->type6.dac1fid12.nextport);
ais->type6.dac1fid12.nmonth = UBITS(168, 4);
ais->type6.dac1fid12.nday = UBITS(172, 5);
ais->type6.dac1fid12.nhour = UBITS(177, 5);
ais->type6.dac1fid12.nminute = UBITS(182, 6);
UCHARS(188, ais->type6.dac1fid12.dangerous);
UCHARS(308, ais->type6.dac1fid12.imdcat);
ais->type6.dac1fid12.unid = UBITS(332, 13);
ais->type6.dac1fid12.amount = UBITS(345, 10);
ais->type6.dac1fid12.unit = UBITS(355, 2);
/* skip 3 bits */
break;
case 14: /* IMO236 - Tidal Window */
ais->type6.dac1fid32.month = UBITS(88, 4);
ais->type6.dac1fid32.day = UBITS(92, 5);
#define ARRAY_BASE 97
#define ELEMENT_SIZE 93
for (u = 0; ARRAY_BASE + (ELEMENT_SIZE*u) <= bitlen; u++) {
int a = ARRAY_BASE + (ELEMENT_SIZE*u);
struct tidal_t *tp = &ais->type6.dac1fid32.tidals[u];
tp->lat = SBITS(a + 0, 27);
tp->lon = SBITS(a + 27, 28);
tp->from_hour = UBITS(a + 55, 5);
tp->from_min = UBITS(a + 60, 6);
tp->to_hour = UBITS(a + 66, 5);
tp->to_min = UBITS(a + 71, 6);
tp->cdir = UBITS(a + 77, 9);
tp->cspeed = UBITS(a + 86, 7);
}
ais->type6.dac1fid32.ntidals = u;
#undef ARRAY_BASE
#undef ELEMENT_SIZE
break;
case 15: /* IMO236 - Extended Ship Static and Voyage Related Data */
ais->type6.dac1fid15.airdraught = UBITS(56, 11);
break;
case 16: /* IMO236 - Number of persons on board */
if (ais->type6.bitcount == 136)
ais->type6.dac1fid16.persons = UBITS(88, 13);/* 289 */
else
ais->type6.dac1fid16.persons = UBITS(55, 13);/* 236 */
imo = true;
break;
case 18: /* IMO289 - Clearance time to enter port */
ais->type6.dac1fid18.linkage = UBITS(88, 10);
ais->type6.dac1fid18.month = UBITS(98, 4);
ais->type6.dac1fid18.day = UBITS(102, 5);
ais->type6.dac1fid18.hour = UBITS(107, 5);
ais->type6.dac1fid18.minute = UBITS(112, 6);
UCHARS(118, ais->type6.dac1fid18.portname);
UCHARS(238, ais->type6.dac1fid18.destination);
ais->type6.dac1fid18.lon = SBITS(268, 25);
ais->type6.dac1fid18.lat = SBITS(293, 24);
/* skip 43 bits */
break;
case 20: /* IMO289 - Berthing data - addressed */
ais->type6.dac1fid20.linkage = UBITS(88, 10);
ais->type6.dac1fid20.berth_length = UBITS(98, 9);
ais->type6.dac1fid20.berth_depth = UBITS(107, 8);
ais->type6.dac1fid20.position = UBITS(115, 3);
ais->type6.dac1fid20.month = UBITS(118, 4);
ais->type6.dac1fid20.day = UBITS(122, 5);
ais->type6.dac1fid20.hour = UBITS(127, 5);
ais->type6.dac1fid20.minute = UBITS(132, 6);
ais->type6.dac1fid20.availability = UBITS(138, 1);
ais->type6.dac1fid20.agent = UBITS(139, 2);
ais->type6.dac1fid20.fuel = UBITS(141, 2);
ais->type6.dac1fid20.chandler = UBITS(143, 2);
ais->type6.dac1fid20.stevedore = UBITS(145, 2);
ais->type6.dac1fid20.electrical = UBITS(147, 2);
ais->type6.dac1fid20.water = UBITS(149, 2);
ais->type6.dac1fid20.customs = UBITS(151, 2);
ais->type6.dac1fid20.cartage = UBITS(153, 2);
ais->type6.dac1fid20.crane = UBITS(155, 2);
ais->type6.dac1fid20.lift = UBITS(157, 2);
ais->type6.dac1fid20.medical = UBITS(159, 2);
ais->type6.dac1fid20.navrepair = UBITS(161, 2);
ais->type6.dac1fid20.provisions = UBITS(163, 2);
ais->type6.dac1fid20.shiprepair = UBITS(165, 2);
ais->type6.dac1fid20.surveyor = UBITS(167, 2);
ais->type6.dac1fid20.steam = UBITS(169, 2);
ais->type6.dac1fid20.tugs = UBITS(171, 2);
ais->type6.dac1fid20.solidwaste = UBITS(173, 2);
ais->type6.dac1fid20.liquidwaste = UBITS(175, 2);
ais->type6.dac1fid20.hazardouswaste = UBITS(177, 2);
ais->type6.dac1fid20.ballast = UBITS(179, 2);
ais->type6.dac1fid20.additional = UBITS(181, 2);
ais->type6.dac1fid20.regional1 = UBITS(183, 2);
ais->type6.dac1fid20.regional2 = UBITS(185, 2);
ais->type6.dac1fid20.future1 = UBITS(187, 2);
ais->type6.dac1fid20.future2 = UBITS(189, 2);
UCHARS(191, ais->type6.dac1fid20.berth_name);
ais->type6.dac1fid20.berth_lon = SBITS(311, 25);
ais->type6.dac1fid20.berth_lat = SBITS(336, 24);
break;
case 23: /* IMO289 - Area notice - addressed */
break;
case 25: /* IMO289 - Dangerous cargo indication */
ais->type6.dac1fid25.unit = UBITS(88, 2);
ais->type6.dac1fid25.amount = UBITS(90, 10);
for (u = 0; 100 + u*17 < bitlen; u++) {
ais->type6.dac1fid25.cargos[u].code = UBITS(100+u*17,4);
ais->type6.dac1fid25.cargos[u].subtype = UBITS(104+u*17,13);
}
ais->type6.dac1fid25.ncargos = u;
break;
case 28: /* IMO289 - Route info - addressed */
ais->type6.dac1fid28.linkage = UBITS(88, 10);
ais->type6.dac1fid28.sender = UBITS(98, 3);
ais->type6.dac1fid28.rtype = UBITS(101, 5);
ais->type6.dac1fid28.month = UBITS(106, 4);
ais->type6.dac1fid28.day = UBITS(110, 5);
ais->type6.dac1fid28.hour = UBITS(115, 5);
ais->type6.dac1fid28.minute = UBITS(120, 6);
ais->type6.dac1fid28.duration = UBITS(126, 18);
ais->type6.dac1fid28.waycount = UBITS(144, 5);
#define ARRAY_BASE 149
#define ELEMENT_SIZE 55
for (u = 0; u < (unsigned char)ais->type6.dac1fid28.waycount; u++) {
int a = ARRAY_BASE + (ELEMENT_SIZE*u);
ais->type6.dac1fid28.waypoints[u].lon = SBITS(a+0, 28);
ais->type6.dac1fid28.waypoints[u].lat = SBITS(a+28,27);
}
#undef ARRAY_BASE
#undef ELEMENT_SIZE
break;
case 30: /* IMO289 - Text description - addressed */
ais->type6.dac1fid30.linkage = UBITS(88, 10);
from_sixbit((char *)bits,
98, bitlen-98,
ais->type6.dac1fid30.text);
break;
case 32: /* IMO289 - Tidal Window */
ais->type6.dac1fid32.month = UBITS(88, 4);
ais->type6.dac1fid32.day = UBITS(92, 5);
#define ARRAY_BASE 97
#define ELEMENT_SIZE 88
for (u = 0; ARRAY_BASE + (ELEMENT_SIZE*u) <= bitlen; u++) {
int a = ARRAY_BASE + (ELEMENT_SIZE*u);
struct tidal_t *tp = &ais->type6.dac1fid32.tidals[u];
tp->lon = SBITS(a + 0, 25);
tp->lat = SBITS(a + 25, 24);
tp->from_hour = UBITS(a + 49, 5);
tp->from_min = UBITS(a + 54, 6);
tp->to_hour = UBITS(a + 60, 5);
tp->to_min = UBITS(a + 65, 6);
tp->cdir = UBITS(a + 71, 9);
tp->cspeed = UBITS(a + 80, 8);
}
ais->type6.dac1fid32.ntidals = u;
#undef ARRAY_BASE
#undef ELEMENT_SIZE
break;
}
if (!imo)
(void)memcpy(ais->type6.bitdata,
(char *)bits + (88 / BITS_PER_BYTE),
(ais->type6.bitcount + 7) / 8);
break;
case 7: /* Binary acknowledge */
case 13: /* Safety Related Acknowledge */
{
unsigned int mmsi[4];
if (bitlen < 72 || bitlen > 158) {
gpsd_report(LOG_WARN, "AIVDM message type %d size is out of range (%zd).\n",
ais->type,
bitlen);
return false;
}
for (u = 0; u < sizeof(mmsi)/sizeof(mmsi[0]); u++)
if (bitlen > 40 + 32*u)
mmsi[u] = UBITS(40 + 32*u, 30);
else
mmsi[u] = 0;
/*@ -usedef @*/
ais->type7.mmsi1 = mmsi[0];
ais->type7.mmsi2 = mmsi[1];
ais->type7.mmsi3 = mmsi[2];
ais->type7.mmsi4 = mmsi[3];
/*@ +usedef @*/
break;
}
case 8: /* Binary Broadcast Message */
if (bitlen < 56 || bitlen > 1008) {
gpsd_report(LOG_WARN, "AIVDM message type 8 size is out of range (%zd).\n",
bitlen);
return false;
}
//ais->type8.spare = UBITS(38, 2);
ais->type8.dac = UBITS(40, 10);
ais->type8.fid = UBITS(50, 6);
ais->type8.bitcount = bitlen - 56;
imo = false;
if (ais->type8.dac == 1)
switch (ais->type8.fid) {
case 11: /* IMO236 - Meteorological/Hydrological data */
/* layout is almost identical to FID=31 from IMO289 */
ais->type8.dac1fid11.lat = SBITS(56, 24);
ais->type8.dac1fid11.lon = SBITS(80, 25);
ais->type8.dac1fid11.day = UBITS(105, 5);
ais->type8.dac1fid11.hour = UBITS(110, 5);
ais->type8.dac1fid11.minute = UBITS(115, 6);
ais->type8.dac1fid11.wspeed = UBITS(121, 7);
ais->type8.dac1fid11.wgust = UBITS(128, 7);
ais->type8.dac1fid11.wdir = UBITS(135, 9);
ais->type8.dac1fid11.wgustdir = UBITS(144, 9);
ais->type8.dac1fid11.airtemp = UBITS(153, 11)
- DAC1FID11_AIRTEMP_OFFSET;
ais->type8.dac1fid11.humidity = UBITS(164, 7);
ais->type8.dac1fid11.dewpoint = UBITS(171, 10)
- DAC1FID11_DEWPOINT_OFFSET;
ais->type8.dac1fid11.pressure = UBITS(181, 9)
- DAC1FID11_PRESSURE_OFFSET;
ais->type8.dac1fid11.pressuretend = UBITS(190, 2);
ais->type8.dac1fid11.visibility = UBITS(192, 8);
ais->type8.dac1fid11.waterlevel = UBITS(200, 9)
- DAC1FID11_LEVEL_OFFSET;
ais->type8.dac1fid11.leveltrend = UBITS(209, 2);
ais->type8.dac1fid11.cspeed = UBITS(211, 8);
ais->type8.dac1fid11.cdir = UBITS(219, 9);
ais->type8.dac1fid11.cspeed2 = UBITS(228, 8);
ais->type8.dac1fid11.cdir2 = UBITS(236, 9);
ais->type8.dac1fid11.cdepth2 = UBITS(245, 5);
ais->type8.dac1fid11.cspeed3 = UBITS(250, 8);
ais->type8.dac1fid11.cdir3 = UBITS(258, 9);
ais->type8.dac1fid11.cdepth3 = UBITS(267, 5);
ais->type8.dac1fid11.waveheight = UBITS(272, 8);
ais->type8.dac1fid11.waveperiod = UBITS(280, 6);
ais->type8.dac1fid11.wavedir = UBITS(286, 9);
ais->type8.dac1fid11.swellheight = UBITS(295, 8);
ais->type8.dac1fid11.swellperiod = UBITS(303, 6);
ais->type8.dac1fid11.swelldir = UBITS(309, 9);
ais->type8.dac1fid11.seastate = UBITS(318, 4);
ais->type8.dac1fid11.watertemp = UBITS(322, 10)
- DAC1FID11_WATERTEMP_OFFSET;
ais->type8.dac1fid11.preciptype = UBITS(332, 3);
ais->type8.dac1fid11.salinity = UBITS(335, 9);
ais->type8.dac1fid11.ice = UBITS(344, 2);
imo = true;
break;
case 13: /* IMO236 - Fairway closed */
UCHARS(56, ais->type8.dac1fid13.reason);
UCHARS(176, ais->type8.dac1fid13.closefrom);
UCHARS(296, ais->type8.dac1fid13.closeto);
ais->type8.dac1fid13.radius = UBITS(416, 10);
ais->type8.dac1fid13.extunit = UBITS(426, 2);
ais->type8.dac1fid13.fday = UBITS(428, 5);
ais->type8.dac1fid13.fmonth = UBITS(433, 4);
ais->type8.dac1fid13.fhour = UBITS(437, 5);
ais->type8.dac1fid13.fminute = UBITS(442, 6);
ais->type8.dac1fid13.tday = UBITS(448, 5);
ais->type8.dac1fid13.tmonth = UBITS(453, 4);
ais->type8.dac1fid13.thour = UBITS(457, 5);
ais->type8.dac1fid13.tminute = UBITS(462, 6);
/* skip 4 bits */
break;
case 15: /* IMO236 - Extended ship and voyage */
ais->type8.dac1fid15.airdraught = UBITS(56, 11);
/* skip 5 bits */
break;
case 17: /* IMO289 - VTS-generated/synthetic targets */
#define ARRAY_BASE 56
#define ELEMENT_SIZE 122
for (u = 0; ARRAY_BASE + (ELEMENT_SIZE*u) <= bitlen; u++) {
struct target_t *tp = &ais->type8.dac1fid17.targets[u];
int a = ARRAY_BASE + (ELEMENT_SIZE*u);
tp->idtype = UBITS(a + 0, 2);
switch (tp->idtype) {
case DAC1FID17_IDTYPE_MMSI:
tp->id.mmsi = UBITS(a + 2, 42);
break;
case DAC1FID17_IDTYPE_IMO:
tp->id.imo = UBITS(a + 2, 42);
break;
case DAC1FID17_IDTYPE_CALLSIGN:
UCHARS(a+2, tp->id.callsign);
break;
default:
UCHARS(a+2, tp->id.other);
break;
}
/* skip 4 bits */
tp->lat = SBITS(a + 48, 24);
tp->lon = SBITS(a + 72, 25);
tp->course = UBITS(a + 97, 9);
tp->second = UBITS(a + 106, 6);
tp->speed = UBITS(a + 112, 10);
}
ais->type8.dac1fid17.ntargets = u;
#undef ARRAY_BASE
#undef ELEMENT_SIZE
break;
case 19: /* IMO289 - Marine Traffic Signal */
ais->type8.dac1fid19.linkage = UBITS(56, 10);
UCHARS(66, ais->type8.dac1fid19.station);
ais->type8.dac1fid19.lon = SBITS(186, 25);
ais->type8.dac1fid19.lat = SBITS(211, 24);
ais->type8.dac1fid19.status = UBITS(235, 2);
ais->type8.dac1fid19.signal = UBITS(237, 5);
ais->type8.dac1fid19.hour = UBITS(242, 5);
ais->type8.dac1fid19.minute = UBITS(247, 6);
ais->type8.dac1fid19.nextsignal = UBITS(253, 5);
/* skip 102 bits */
break;
case 21: /* IMO289 - Weather obs. report from ship */
break;
case 22: /* IMO289 - Area notice - broadcast */
break;
case 24: /* IMO289 - Extended ship static & voyage-related data */
break;
case 26: /* IMO289 - Environmental */
break;
case 27: /* IMO289 - Route information - broadcast */
ais->type8.dac1fid27.linkage = UBITS(56, 10);
ais->type8.dac1fid27.sender = UBITS(66, 3);
ais->type8.dac1fid27.rtype = UBITS(69, 5);
ais->type8.dac1fid27.month = UBITS(74, 4);
ais->type8.dac1fid27.day = UBITS(78, 5);
ais->type8.dac1fid27.hour = UBITS(83, 5);
ais->type8.dac1fid27.minute = UBITS(88, 6);
ais->type8.dac1fid27.duration = UBITS(94, 18);
ais->type8.dac1fid27.waycount = UBITS(112, 5);
#define ARRAY_BASE 117
#define ELEMENT_SIZE 55
for (i = 0; i < ais->type8.dac1fid27.waycount; i++) {
int a = ARRAY_BASE + (ELEMENT_SIZE*i);
ais->type8.dac1fid27.waypoints[i].lon = SBITS(a + 0, 28);
ais->type8.dac1fid27.waypoints[i].lat = SBITS(a + 28, 27);
}
#undef ARRAY_BASE
#undef ELEMENT_SIZE
break;
case 29: /* IMO289 - Text Description - broadcast */
ais->type8.dac1fid29.linkage = UBITS(56, 10);
from_sixbit((char *)bits,
66, bitlen-66,
ais->type8.dac1fid29.text);
break;
case 31: /* IMO289 - Meteorological/Hydrological data */
ais->type8.dac1fid31.lat = SBITS(56, 24);
ais->type8.dac1fid31.lon = SBITS(80, 25);
ais->type8.dac1fid31.accuracy = (bool)UBITS(105, 1);
ais->type8.dac1fid31.day = UBITS(106, 5);
ais->type8.dac1fid31.hour = UBITS(111, 5);
ais->type8.dac1fid31.minute = UBITS(116, 6);
ais->type8.dac1fid31.wspeed = UBITS(122, 7);
ais->type8.dac1fid31.wgust = UBITS(129, 7);
ais->type8.dac1fid31.wdir = UBITS(136, 9);
ais->type8.dac1fid31.wgustdir = UBITS(145, 9);
ais->type8.dac1fid31.airtemp = SBITS(154, 11)
- DAC1FID31_AIRTEMP_OFFSET;
ais->type8.dac1fid31.humidity = UBITS(165, 7);
ais->type8.dac1fid31.dewpoint = UBITS(172, 10)
- DAC1FID31_DEWPOINT_OFFSET;
ais->type8.dac1fid31.pressure = UBITS(182, 9)
- DAC1FID31_PRESSURE_OFFSET;
ais->type8.dac1fid31.pressuretend = UBITS(191, 2);
ais->type8.dac1fid31.visgreater = UBITS(193, 1);
ais->type8.dac1fid31.visibility = UBITS(194, 7);
ais->type8.dac1fid31.waterlevel = UBITS(200, 12)
- DAC1FID31_LEVEL_OFFSET;
ais->type8.dac1fid31.leveltrend = UBITS(213, 2);
ais->type8.dac1fid31.cspeed = UBITS(215, 8);
ais->type8.dac1fid31.cdir = UBITS(223, 9);
ais->type8.dac1fid31.cspeed2 = UBITS(232, 8);
ais->type8.dac1fid31.cdir2 = UBITS(240, 9);
ais->type8.dac1fid31.cdepth2 = UBITS(249, 5);
ais->type8.dac1fid31.cspeed3 = UBITS(254, 8);
ais->type8.dac1fid31.cdir3 = UBITS(262, 9);
ais->type8.dac1fid31.cdepth3 = UBITS(271, 5);
ais->type8.dac1fid31.waveheight = UBITS(276, 8);
ais->type8.dac1fid31.waveperiod = UBITS(284, 6);
ais->type8.dac1fid31.wavedir = UBITS(290, 9);
ais->type8.dac1fid31.swellheight = UBITS(299, 8);
ais->type8.dac1fid31.swellperiod = UBITS(307, 6);
ais->type8.dac1fid31.swelldir = UBITS(313, 9);
ais->type8.dac1fid31.seastate = UBITS(322, 4);
ais->type8.dac1fid31.watertemp = UBITS(326, 10)
- DAC1FID31_WATERTEMP_OFFSET;
ais->type8.dac1fid31.preciptype = UBITS(336, 3);
ais->type8.dac1fid31.salinity = UBITS(339, 9);
ais->type8.dac1fid31.ice = UBITS(348, 2);
imo = true;
break;
}
/* land here if we failed to match a known DAC/FID */
if (!imo)
(void)memcpy(ais->type8.bitdata,
(char *)bits + (56 / BITS_PER_BYTE),
(ais->type8.bitcount + 7) / 8);
break;
case 9: /* Standard SAR Aircraft Position Report */
if (bitlen != 168) {
gpsd_report(LOG_WARN, "AIVDM message type 9 size not 168 bits (%zd).\n",
bitlen);
return false;
}
ais->type9.alt = UBITS(38, 12);
ais->type9.speed = UBITS(50, 10);
ais->type9.accuracy = (bool)UBITS(60, 1);
ais->type9.lon = SBITS(61, 28);
ais->type9.lat = SBITS(89, 27);
ais->type9.course = UBITS(116, 12);
ais->type9.second = UBITS(128, 6);
ais->type9.regional = UBITS(134, 8);
ais->type9.dte = UBITS(142, 1);
//ais->type9.spare = UBITS(143, 3);
ais->type9.assigned = UBITS(146, 1)!=0;
ais->type9.raim = UBITS(147, 1)!=0;
ais->type9.radio = UBITS(148, 19);
break;
case 10: /* UTC/Date inquiry */
if (bitlen != 72) {
gpsd_report(LOG_WARN, "AIVDM message type 10 size not 72 bits (%zd).\n",
bitlen);
return false;
}
//ais->type10.spare = UBITS(38, 2);
ais->type10.dest_mmsi = UBITS(40, 30);
//ais->type10.spare2 = UBITS(70, 2);
break;
case 12: /* Safety Related Message */
if (bitlen < 72 || bitlen > 1008) {
gpsd_report(LOG_WARN, "AIVDM message type 12 size is out of range (%zd).\n",
bitlen);
return false;
}
ais->type12.seqno = UBITS(38, 2);
ais->type12.dest_mmsi = UBITS(40, 30);
ais->type12.retransmit = (bool)UBITS(70, 1);
//ais->type12.spare = UBITS(71, 1);
from_sixbit((char *)bits,
72, bitlen-72,
ais->type12.text);
break;
case 14: /* Safety Related Broadcast Message */
if (bitlen < 40 || bitlen > 1008) {