Skip to content

Latest commit

 

History

History
210 lines (176 loc) · 9.94 KB

geofence.md

File metadata and controls

210 lines (176 loc) · 9.94 KB

Geofencing and Automatic Logger Control

One of the routine and error-prone tasks that the operator of a shipboard data acquisition system may have to manage is that of changing logger configurations depending on a ship's location: starting to log from certain instruments as a ship leaves port, and turning others on or off as it enters or leaves EEZs, or areas of special interests.

Much of this functionality can automated by combining the appropriate OpenRVDAS readers, transforms and writers to implement geofencing and other data-dependent logger control.

Geofencing

The GeofenceTransform listens to a stream of parsed DASRecord records or dictionaries of name:value pairs looking for fields that contain latitude/longitude pairs. When found, it compares them to a geofence boundary loaded at initialization time. If the lat/lon has crossed over the boundary since the last record, it emits a predefined 'leaving_boundary_message' or 'entering_boundary_message' as appropriate.

Pairing this with the LoggerManagerWriter provides a simple mechanism for changing cruise modes when the boundary in question is crossed. The simple logger definition below illustrates this:

# Read parsed DASRecords from UDP
readers:
  class: UDPReader
  kwargs:
    port: 6224
    
# Look for lat/lon values in the DASRecords and emit appropriate commands
# when entering/leaving EEZ. Note that EEZ files in GML format can be
# downloaded from https://marineregions.org/eezsearch.php.
transforms:
  - class: GeofenceTransform
    module: logger.transforms.geofence_transform
    kwargs:
      latitude_field_name: s330Latitude
      longitude_field_name: s330Longitude
      boundary_file_name: /tmp/eez.gml
      leaving_boundary_message: set_active_mode underway_mode
      entering_boundary_message: set_active_mode eez_mode
      
# Send the messages that we get from geofence to the LoggerManager
writers:
  - class: LoggerManagerWriter
    module: logger.writers.logger_manager_writer
    kwargs:
      database: django
      allowed_prefixes:
        - 'set_active_mode '
        - 'sleep '
  • The UDPReader listens for DASRecords on UDP port 6224
  • The GeofenceTransform receives these records, looking for latitude values named s330Latitude and longitudes named s330Longitude.
    • When it finds such a pair, it compares the lat/lon values with the boundary loaded from /tmp/eez.gml.
    • If the previous lat/lon values were inside the boundary and the current ones are outside, it emits the message set_active_mode underway_mode.
    • If the previous lat/lon values were outside the boundary and the current ones are inside, it emits the message set_active_mode eez_mode.
    • Otherwise, it emits nothing.
  • The LoggerManagerWriter receives any emitted messages, compares them against its list of allowed message prefixes and, if they are allowable, issues them as commands to the LoggerManager, switching the cruise mode between eez_mode and underway_mode and back, as appropriate.

Additional GeofenceTransform parameters

Additional optional GeofenceTransform parameters allow offsetting the switchover point from the boundary to provide a buffer, and limiting the lat/lon check to no more than once every N seconds, to decrease computational overhead.

distance_from_boundary_in_degrees
          Optional distance from boundary to place the fence, in degrees.
          Negative means inside the boundary.
          
seconds_between_checks
          Optional number of seconds to wait between doing checks,
          to minimize computational overhead

boundary_dir_name
          Optional name of a directory containing .gml files to use in-
          place of a single .gml file.  When this option is specified
          the transform will read all *.gml files contained in the
          directory, build a new polygon (union) and use this polygon as
          the boundary

          This option cannot be used if boundary_file_name is used and
          vice-versa.

(Note that the optional parameter distance_from_boundary is in degrees. Computing the appropriate value in km/nm is nontrivial and requires figuring out the right UTM projection for each location, recomputing it for each point and switching when lat/lon moved to a new UTM projection area, possibly resulting in discontinuities. For now, requiring offsets to be expressed in terms of degrees is simpler and less error-prone.)

Additional LoggerManagerWriter parameters

The LoggerManagerWriter needs to know how to communicate with the LoggerManager in question. This can be done by using the database parameter to tell it whether to try to connect to the Django-based, SQLite-based or in-memory LoggerManager. Alternatively, an instance of a ServerAPI may be passed in using the api parameter.

database
        String indicating which database the LoggerManager is using: django,
        sqlite, memory. Either this or 'api', but not both, must be specified.

api
        An instance of server_api.ServerAPI to use to communicate with the
        LoggerManager. Either this or 'database', but not both, must be specified.

allowed_prefixes
        Optional list of strings. If specified, only records whose prefixes match
        something in this list will be passed on as commands.

Multiple commands may be sent in a single record by separating them with semicolons.

In addition to the normally-accepted LoggerManager commands, an additional one: sleep N is recognized, which will pause the writer N seconds before writing the subsequent command. This allows time, if needed, for the effects of prior commands to settle.

Other Data-Based Logger Control

There are ways to implement data-based control of loggers, beyond the lat/lon-specific GeofenceTransform. The QCFilterTransform provides a more limited, but still powerful way to change logger state depending on the value of a specific data field:

# Read parsed DASRecords from UDP
readers:
  class: UDPReader
  kwargs:
    port: 6224
    
# If ship speed over ground is greater than 0.5 knots, declare us
# underway, and start logging.
transforms:
  - class: QCFilterTransform
    module: loggers.transforms.qc_filter_transform
    kwargs:
      bounds: 's330SpeedKt:0.0:0.5'
      message: set_active_mode underway_mode
      
# Send the messages that we get from geofence to the LoggerManager
writers:
  - class: LoggerManagerWriter
    module: logger.writers.logger_manager_writer
    kwargs:
      database: django
      allowed_prefixes:
        - 'set_active_mode '
        - 'sleep '
  • The bounds parameter takes a string of comma-separated triplets of the form <field_name>:<lower_bound>:<upper_bound>
    • <field_name> (in this case s330SpeedKts) says to look for values of that field in the incoming records
    • <lower_bound>:<upper_bound> (in this case 0.0:0.5), specify the "normal" range of values of this field. In this case, "normal" would be when the ship is moving less than 0.5 knots, or approximately at rest.
  • When the tested condition is no longer met (i.e. the ship is moving), the transform will output the string specified in the message parameter: set_active_mode underway_mode.

This message gets passed on to the LoggerManagerWriter which, as before, will interpret it as a command and set the system into underway mode.

Note that if you also wanted the system to switch out of underway mode when the ship's speed slowed back down below a certain value, you would need to create a second logger (or use a ComposedWriter) using a bounds line like s330SpeedKt:0.5:100 and an appropriate message to switch to the desired mode.

Example of using boundary_dir_name

In this scenario we will use the boundary_dir_name kwarg to build a geofence based on the combination of multiple gml files located within a directory. In this scenario we want to be able to easily update the gml files applied to the geofence with minimal distruption to logging operations.

This scenario assumes the complete logger_config.yaml file includes 2 configs for the geofence logger: geofence->off (does nothing) and geofence->on which is based on the example config described above.

Updated transform definition

Location of all gml files the vessel may want to apply to the transform:

/home/rvdas/available_gml_files

Location of all gml files the vessel does want to apply to the transform:

/home/rvdas/enabled_gml_files

Updated version of the geofence transform based on previous example:

transforms:
  - class: GeofenceTransform
    module: logger.transforms.geofence_transform
    kwargs:
      latitude_field_name: s330Latitude
      longitude_field_name: s330Longitude
      boundary_dir_name: /home/rvdas/enabled_gml_files
      leaving_boundary_message: set_active_mode underway_mode
      entering_boundary_message: set_active_mode eez_mode

Adding an gml to use with the transform:

  1. Download the new gml file to /home/rvdas/available_gml_files via:
curl -o /home/rvdas/available_gml_files/<country_name>.gml "<url from marineregions.org>"
  1. Symlink the new gml file to the enabled_gml_files directory via:
ln -s /home/rvdas/available_gml_files/<country_name>.gml /home/rvdas/enabled_gml_files/
  1. Cycle the "geofence" logger in OpenRVDAS WebUI to geofence->off (wait for the change to take affect) and back to geofence->on

If you need to remove an eez from the list:

  1. Delete the file from /home/rvdas/enabled_gml_files via:
rm /home/rvdas/enabled_gml_files/<country_name>.gml
  1. Cycle the "geofence" logger in OpenRVDAS WebUI to geofence->off (wait for the change to take affect) and back to geofence->on

Conclusion

Obviously, there is ample room for more powerful, or custom Transforms to allow more elaborate and/or precise control of logger states. We gratefully welcome any code contributed to OpenRVDAS toward that end.