#!/usr/bin/perl -w
###############################################################################
# THIS SOFTWARE IS FREE SOFTWARE! IT IS LICENCED UNDER THE GNU PUBLIC LICENCE
#
#  This program is free software; you can redistribute it and/or modify
#  it under the terms of the GNU General Public License as published by
#  the Free Software Foundation; version 2 of the License
#
#  This program is distributed in the hope that it will be useful,
#  but WITHOUT ANY WARRANTY; without even the implied warranty of
#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
#  GNU General Public License for more details.
#
#  You should have received a copy of the GNU General Public License
#  along with this program; if not, write to the Free Software
#  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
###############################################################################
# (c) Hartmut Schimmel, 10.02.2008

# 07.10.2007 sml first release
# 10.02.2008 sml complete rewrite for better code readability
# 02.03.2008 sml added write settings support
# 09.03.2008 sml added write wav support
# 05.05.2008 sml support for DPL700 firmware 4.1, where $name changed from
#                "WP GPS+BT" to "WondeProud Tech. Co. BT-CD110", thanks to
#                Piotr Gapinski and Yuri Pirola
#                fixed swaped Serial No. and type output
#                /dev/usbdev\d\.\d_ep\d\d now blacklisted by autodetect
#                /dev/ttyACM now supported by autodetect
# 03.08.2008 sml velosity-velocity typo
#                added option -m for serial device cofiguration skip for
#                MacOS X, thanks to Glenn Hoffman
# 04.08.2008 sml skip CheckForStty() if -m is given
#                added usage description for -m
# 03.12.2008 sml removed 115200 Baud from stty cmd line option because setting
#                of boud rate not longer supported by stty for USB serial
#                devices (anyway, it was seemingly a NOP all the time)
# 11.05.2009 sml Whitelisted /dev/ttyUSB* and /dev/*acm* instead of any
#                blacklisting in device name autodetection



use IO::Handle;
use Fcntl;
use Getopt::Std;

use strict;

STDOUT->autoflush(1);



###############################################################################
# Settings
###############################################################################
# Names of the wav-Files, they are expected exactly in this order
my @WavFiles = qw( battery_low.wav
                   BT_close.wav
                   BT_open.wav
                   Error_Beep.wav
                   GPS_search.wav
                   GPS_search_finished.wav
                   OK_Beep.wav
                   OK1_Beep.wav );

# Dirs, where we should look for the wav-Files
my @WavDirs = ( "$ENV{'PWD'}", "$ENV{'HOME'}/wav" );



###############################################################################
# Declarations and definitions
###############################################################################
use constant NULL => chr  0;
use constant MEMSIZE => 3997680;
use constant SERIAL_MODE => "cs8 raw";
use constant TIMEOUT => 1;


sub CheckForStty();
sub InstallAlarmHandler();
sub AutoDetectUSBDeviceNames();
sub OpenAndTestUSBDevices(@);
sub ConfigureSerialDevice($);
sub OpenDevice($);
sub GetDeviceInfo();
sub GetDeviceSettings();
sub PrintDeviceSettings();
sub GetMemoryDump();
sub FindWavFiles();
sub CreateWavBin($);
sub CreateConfigBin($$$$$$$);
sub WriteConfigToDevice($);
sub WriteDumpToFile($$);
sub CreateSrFileName();
sub EraseDeviceMemory();
sub WriteDataToDevice($);
sub WriteStringToDevice($);
sub ReadDataFromDevice();
sub ReadStringFromDevice();
sub Usage();
sub CloseDevice();


my %o;
( my $prg = $0 ) =~ s/^.*\/([^\/]+)$/$1/g;



###############################################################################
# Does the given options make sense ?
###############################################################################
Usage() unless getopts('asd:fimo:ew:', \%o);
Usage() unless ( defined($o{a}) or defined($o{s}) or
                 defined($o{d}) or defined($o{w}));

die "It does not make sense to combine the options -a and -d\n"
                                      if ((defined $o{a}) and (defined $o{d}));

die "It does not make sense to combine the options -s and -d\n"
                                      if ((defined $o{s}) and (defined $o{d}));

die "It does not make sense to combine the options -w and -s\n"
                                      if ((defined $o{w}) and (defined $o{s}));

die "It does not make sense to combine the options -w and -i\n"
                                      if ((defined $o{w}) and (defined $o{i}));

die "It does not make sense to combine the options -w and -e\n"
                                      if ((defined $o{w}) and (defined $o{e}));

die "It does not make sense to combine the options -w and -o\n"
                                      if ((defined $o{w}) and (defined $o{o}));

die "Option -f does make sense only together with option -w\n"
                                  if ((defined $o{f}) and not (defined $o{w}));

die "Option -e (erase) is not accepted without memory reading for security\n" .
                     "reasons. So combine -e always with -a or -d <device>\n"
            if ( (defined $o{e}) and not ((defined $o{d}) or (defined $o{a})));



###############################################################################
# Main
###############################################################################
CheckForStty() unless defined $o{m};

InstallAlarmHandler();

my @device = $o{d} ? $o{d} : AutoDetectUSBDeviceNames();

OpenAndTestUSBDevices( @device);


if ( defined $o{s} ) {
  CloseDevice();
  print "\nAll done.\n";
  exit 0; # exit here if only scan was wanted
}


PrintDeviceSettings();


if ( defined $o{i} ) {
  CloseDevice();
  print "\nAll done.\n";
  exit 0; # exit here if only info was wanted
}




# Special operation: Write settings
if ( defined $o{w} ) {

  # option -w correct used ?
  if ( $o{w} =~ /^(\w+)=(\w+)$/ ) {

    my ( $param, $value ) = ( $1, $2 );

    my ( $time, $dist, $sens, $tag, $vMin ) = GetDeviceSettings();

    my $bin = CreateConfigBin(  $time, $dist, $sens, $tag, $vMin,
                                                               $param, $value);

    $bin .= CreateWavBin( FindWavFiles() );

    WriteConfigToDevice( $bin );
  }
  else { Usage() };


  CloseDevice();
  print "\nAll done.\n";
  exit 0; # exit here if writing settings was wanted
}



# Normal operation: Read memory
WriteDumpToFile( defined $o{o} ? $o{o} : CreateSrFileName(), GetMemoryDump() );
EraseDeviceMemory() if ( defined $o{e} );


CloseDevice();
print "\nAll done.\n";
exit 0;



###############################################################################
###############################################################################
###############################################################################
# Subroutines
###############################################################################
###############################################################################
###############################################################################

###############################################################################
# FindWavFiles
###############################################################################
sub FindWavFiles() {
  my $d;

  print "\nSearching wav-files in:\n";

  foreach my $Dir ( @WavDirs ) {
    print " $Dir:\n";

    if ( opendir DIR, $Dir ) {

      while (defined( my $FileInDir = readdir DIR )) {

        foreach my $Wav ( @WavFiles ) {
          next unless $FileInDir eq $Wav;
          $d->{"$Wav"} = $Dir;
          print "  Found $Wav\n";
          last;
        }
      }
      closedir DIR;
    }
    else {
      warn "Error while opening dir $Dir: $!\n";
    }
  }
  @_ = keys %{$d};
  if ( $#WavFiles == $#_ ) {
    print "Found all wav-files\n";
  }
  else {
    die "Found ", $#_ + 1, " wav-files, but it should be ", $#WavFiles + 1,
        "\nThe wav-files should be in one of the folders noted above.\n",
        "Abort\n";
  }
  return $d;
}



###############################################################################
# CreateWavBin
###############################################################################
sub CreateWavBin($) {
  my $Dir = shift;

  my $WavBin;

  # lt. Hexdump von Set_Log_Data.exe max. 128 kByte mit oder ohne Config ?
  # lt. Hexdump von Upgrade(BTCD110-V2_5).exe max. 1 MByte Wav-Daten

  foreach my $Wav ( @WavFiles ) {
    open WAV, "$Dir->{$Wav}/${Wav}" or
                           die "Error while reading $Dir->{$Wav}/${Wav}: $!\n";
    while ( read WAV, $_, 1 ) {
      $WavBin .= $_;
    }
    close WAV;
  }
  return ( $WavBin);
}



###############################################################################
# CreateConfigBin
###############################################################################
sub CreateConfigBin($$$$$$$) {
  my ( $time, $dist, $sens, $tag, $vMin, $param, $value ) = @_;

  $param = lc $param;

  # overwrite with new settings, if available
  if (( $param eq 't') or ( $param eq 'time' )) {
    $time = sprintf( "%d", $value);
  }
  elsif (( $param eq 'd') or ( $param eq 'distance' )) {
    $dist = sprintf( "%d", $value);
  }
  elsif (( $param eq 's') or ( $param eq 'sensitivity' )) {
    $sens = sprintf( "%d", $value);
  }
  elsif (( $param eq 'm') or ( $param eq 'marker' )) {
    $tag  = sprintf( "%d", $value);
  }
  elsif (( $param eq 'v' ) or (  $param eq 'vmin' )) {
    $vMin = sprintf( "%d", $value / 1.852);
  }
  else {
    die "Unknown option $param - Abort\n";
  }

  # time and dist are exclusive
  $dist= 0xFFFFFFFF if $param eq 't';
  $time      = 0xFFFFFFFF if $param eq 'd';

  # check, if values make sense
  unless ( defined $o{f} ) {
    die "Time should be greater 0\n"       if  ($time < 0);
    die "Distance should be greater 0\n"   if  ($dist < 0);
    die "Senisivity should be 0...3\n"     if (($sens < 0) or ($sens > 3));
    die "Tag should be 0 or 1\n"           if (( $tag < 0) or ( $tag > 1));
    die "Velocity should be 0...36 km/h\n" if (($vMin < 0) or ($vMin > 20));
  }

  # Time       : Byte 4-1: unsigned 32 bit
  # Distance   : Byte 8-5: unsigned 32 bit
  # Sensitivity: Byte   9: 2-high, 1-middle, 3-low, 0-disable
  # Tag        : Byte  25: 0-off, 1-on
  # v_min      : Byte  26: 0 km/h = 0x00 Knoten ( km/h = Knoten * 1.852)
  #              max. 36 km/h = 0x14 (20 Knoten), 0xFF = unused
  return( pack( "L L C A15 C C A870", $time, $dist, $sens, chr(0xFF) x 15,
                                                $tag, $vMin, chr(0xFF) x 870));
}



###############################################################################
# WriteConfigToDevice
###############################################################################
sub WriteConfigToDevice($) {
  print "\nWriting settings into the device\n";
  print " Erasing flash\n";
  # 0x55 0xAA 0x76 0x33 0x01 0x00 0x<chk_sum>
  # how the chk_sum is calculated ?
  # however, chk_sum error does not matter
  #
  # device answers not before at least 78720 bytes are sent, but so much data
  # is only needed, if the wav-files should be updated. For config only update
  # no answer is needed
  WriteStringToDevice( chr(0x55) . chr(0xAA) . chr(0x76) .  chr(0x33) .
                                                     chr(0x01) . NULL . NULL );
  sleep 2;

  WriteDataToDevice( shift );
  # device has to be closed after config writing or it not responds sensefull
  return 1;
}



###############################################################################
# CreateSrFileName
###############################################################################
sub CreateSrFileName() {
  my $now = time();
  my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime( $now);
  $mon++; $year += 1900;

  return sprintf( "iTU4l_%04d%02d%02d%02d%02d%02d.sr",
                                        $year, $mon, $mday, $hour, $min, $sec);
}



###############################################################################
# WriteDumpToFile
###############################################################################
sub WriteDumpToFile($$) {
  my $file = shift;
  my $data = shift;

  print "Writing ", length( $data), " Bytes to $file\n";
  open OUTFILE, ">$file" or die "Error while writing $file: $!\n";
  print OUTFILE $data;
  close OUTFILE;

  return 1;
}



###############################################################################
# EraseDeviceMemory
###############################################################################
sub EraseDeviceMemory() {
  print "\nErasing memory\n";
  WriteStringToDevice( chr(0x61) . chr(0xB6) .
                                            NULL . NULL . NULL . NULL . NULL );

  if ( ReadStringFromDevice() ne 'WP Update Over' ) {
    print " Unexpected answer\n";
  }

  return 1;
}



###############################################################################
# GetMemoryDump
###############################################################################
sub GetMemoryDump() {
  print "\nReading memory\n";
  WriteStringToDevice( chr(0x60) . chr(0xB5) .
                                            NULL . NULL . NULL . NULL . NULL );

  my $dump = ReadDataFromDevice();

  die <<EOF unless substr( $dump, -15) eq "WP Update Over" . NULL;

ERROR: Recieved date not complete, missing 'WP Update Over' ant the end - Abort

EOF

  # cut away the "WP Update Over" at the end
  $dump = substr( $dump, 0, (int( length($dump)/16 ) - 1) * 16);
  printf " %d Bytes read\n", length( $dump);

  return $dump;;
}



###############################################################################
# GetDeviceSettings
###############################################################################
sub GetDeviceSettings() {
  WriteStringToDevice( chr(0x62) . chr(0xB6) .
                                            NULL . NULL . NULL . NULL . NULL );

  # Time       : Byte 4-1: unsigned 32 bit
  # Distance   : Byte 8-5: unsigned 32 bit
  # Sensitivity: Byte   9: 2-high, 1-middle, 3-low, 0-disable
  # Tag        : Byte  25: 0-off, 1-on
  # v_min      : Byte  26: 0 km/h = 0x00 Knoten ( km/h = Knoten * 1.852)
  #                  max. 36 km/h = 0x14 (20 Knoten)
  my ( $time, $distance, $sensitivy, $dummy1, $tag, $vMin, $dummy2 ) =
                             unpack( "L L C A15 C C A*", ReadDataFromDevice());

  return( $time, $distance, $sensitivy, $tag, $vMin);
}



###############################################################################
# GetDeviceInfo
###############################################################################
sub GetDeviceInfo() {
  WriteStringToDevice( chr(0x5B) . chr(0xB0) .
                                            NULL . NULL . NULL . NULL . NULL );

  # Serial No. : Byte  8-5 : unsigned 32 bit
  # Type       : Byte 41-48: Bytestring
  my ( $dummy0, $serial_no, $dummy1, $type, $dummy2 ) =
                                unpack( "L L A32 A8 A*", ReadDataFromDevice());

  return( $type, $serial_no);
}



###############################################################################
# PrintDeviceSettings
###############################################################################
sub PrintDeviceSettings() {
  print "Current device settings:\n";
  printf " Type             : %s\n Serial No.       : %s\n", GetDeviceInfo();

  my ( $time, $distance, $sensitivy, $tag, $vMin ) = GetDeviceSettings();

  $time = "unused" if $time == 0xFFFFFFFF;
  $distance = "unused" if $distance == 0xFFFFFFFF;
  $vMin = 0 if $vMin == 0xFF;

  print " Sensitivity      : $sensitivy (";
  if ( $sensitivy == 0 ) { print "disabled)\n" };
  if ( $sensitivy == 1 ) { print "middle)\n" }; 
  if ( $sensitivy == 3 ) { print "low)\n" };
  if ( $sensitivy == 2 ) { print "high)\n" };

  print " Tag              : $tag (";
  if ( $tag == 0 ) { print "off)\n" };
  if ( $tag == 1 ) { print "on)\n" };

  printf " Minimal velocity : %d km/h\n", $vMin  * 1.852;

  print " Time-Step        : $time", $time eq 'unused' ? "\n":" sec\n";
  print " Distance-Step    : $distance", $distance eq 'unused' ? "\n":" meter\n";
          
  if ( $time eq 'unused' ) {
    printf "Based on Distance-Step there will fit about %d km into the logger\n",
                                               MEMSIZE / 16 * $distance / 1000;
  }
  elsif ( $distance eq 'unused' ) {
    printf "Based on Time-Step there will fit about %.1f days into the logger\n",
                                              MEMSIZE / 16 * $time / 3600 / 24;
  }
  return 1;
}



###############################################################################
# CheckForStty
###############################################################################
sub CheckForStty() {
die <<EOF unless !system('which stty > /dev/null');

We need the stty tool to set mode 8N1 raw 115200 to the serial device. It
should be available on most common Linux/Unix systems. However here it is
missing.  Please install it according to your distributions rules. For Debian
and Ubuntu it is included in the package 'coreutils'. To install it you have to
type as root:

apt-get install coreutils

Please tell the author of $prg, how stty is installed on other
distributions than Debian and Ubuntu.

EOF
}



###############################################################################
# Install an alarm handler to interrupt a pending access to the device
###############################################################################
sub InstallAlarmHandler() {
  $SIG{ALRM} = sub { die "timeout\n" }; # \n required
}



###############################################################################
# Autodetect USB device name magic ;-)
###############################################################################
sub AutoDetectUSBDeviceNames() {
  print "\nFound USB devices:\n";

  # Scan directories which typical contain USB device files
  foreach my $dir ( qw( /dev /dev/usb/tts )) {

    if ( opendir DH, $dir ) {
      while ( defined( $_ = readdir DH)) {

        # /dev/usb/tty device files all should match
        if ( $dir eq '/dev/usb/tts' ) {
          push @_, "$dir/$_";
          print " $dir/$_\n";
        }
        else {

          # other devices names should contain at least USB or ACM
          next unless /(ttyUSB)|(acm)/i;

          push @_, "$dir/$_";
          print " $dir/$_\n";
        }
      }
      close DH;
    }
  }

  die <<EOF unless $#_ >= 0;
 No USB devices found.
	
EOF

  return @_;
}


###############################################################################
# OpenAndTestUSBDevices
###############################################################################
sub OpenAndTestUSBDevices(@) {
  print "\nTrying to talk to the USB device\n";

  my $device_found = 0;
  foreach my $device ( sort shift) {

    unless ( defined $o{m} ) {
      unless ( ConfigureSerialDevice( $device) ) {
        print " Error while setting $device to " . SERIAL_MODE . "\n";
        next;
      }
      print " Device $device set to " . SERIAL_MODE . "\n";
    }


    unless ( OpenDevice( $device) ) {
      print " Error during access to $device - assuming that this\n" .
            " device is not the right one\n\n";
      next;
    }
    print " Opened $device\n";


    # try to Initialize the logger
    WriteStringToDevice( "WP AP-Exit" . NULL ); # close possibly pending session
    WriteStringToDevice( "W'P Camera Detect" . NULL ); # init new session


    my $name = ReadStringFromDevice();
    chomp($name);
    print " Device $device answered with \'$name\'\n";



    # 'WondeProud Tech. Co. BT-CD110' for DPL700 with firmware 4.1
    # 'WP GPS+BT' for all other supported devices/firmware
    if ( $name =~ /^(WP GPS\+BT)|(WondeProud)/ ) {
      print " *** fine, $device is it ***\n\n";
      $device_found = 1;
      last;
    }
    else {
      print " *** unexpected, assuming that this device is not the right " .
                                                                     "one\n\n";
      next;
    }


  }
  die <<EOF if $device_found == 0;

USB devices found, but not detected as the GPS logger we searched.
	
EOF
  return 1;

}



###############################################################################
# ConfigureSerialDevice
###############################################################################
sub ConfigureSerialDevice( $) {
  my $device = shift;
  my $cmd = "stty " . SERIAL_MODE;


  eval {
    alarm( TIMEOUT );
    
    !system( "$cmd < $device ") or die;
    alarm(0);
  };
  return 0 if $@;

  return 1;
}


  
###############################################################################
# OpenDevice
###############################################################################
sub OpenDevice( $) {
  my $device = shift;

  eval {
    alarm( TIMEOUT );
    #open( SERIAL, "+<$device");
    sysopen( SERIAL, "$device", O_RDWR );
    alarm(0);
  };
  return 0 if $@;

  SERIAL->autoflush(1); # not for sysopen/syswritr/sysread
  return 1;
}
  


###############################################################################
# CloseDevice
###############################################################################
sub CloseDevice() {
    WriteStringToDevice( "WP AP-Exit" . NULL )
                                  or die "Error while writing to device: $!\n";
  close SERIAL;
}



###############################################################################
# ReadDataFromDevice
###############################################################################
sub ReadDataFromDevice() {
  my $data;

  use constant BLK_SIZE => 64;

  eval {
    my $last_kByte = 0;

    while (1) {
      alarm( TIMEOUT );
      sysread( SERIAL, $_, BLK_SIZE);
      alarm(0);
      $data .= $_;

      if ( int( length( $data) / 1024) - BLK_SIZE + 1 > $last_kByte) {
        printf " % 4d kByte\r", int( length( $data) / 1024);
        $last_kByte = int( length( $data) / 1024);
      }
    }
  };

  if ($@) {
    die "Error while reading from device: $!\n" unless $@ eq "timeout\n";
  }
  return $data;
}



###############################################################################
# ReadStringFromDevice
###############################################################################
sub ReadStringFromDevice() {
  my $string = '';

  eval {
    alarm( TIMEOUT );
    while (1) {
      sysread( SERIAL, $_, 1);
      last if $_ eq NULL;
      $string .= $_;
    }
    alarm(0);
  };

  if ($@) {
    die "Error while reading from device: $!\n" unless $@ eq "timeout\n";
  }
  return $string;
}



###############################################################################
# WriteDataToDevice (16 Byte-blockwise)
###############################################################################
sub WriteDataToDevice( $ ) {
  my $data = shift;

  my $bytes_wrote;
  for ( my $i = 0; $i < length( $data); $i += 16 ) {
    $bytes_wrote += syswrite SERIAL, $data, 16, $i;
    print " Wrote $bytes_wrote bytes\r";
  }
  print "\n";

  return 1;
}



###############################################################################
# WriteStringToDevice
###############################################################################
sub WriteStringToDevice( $ ) {
  syswrite SERIAL, shift or die "Error while writing to device: $!\n";
  return 1;
}



###############################################################################
# Usage
###############################################################################
sub Usage() {
  die <<EOF;

$prg reads the memory from the XAIOX iTrackU and Gisteq PhotoTrackr GPS
logger to a file. The format of this file is the same as from the .sr file
of XAiOX Windows tool. If you are desperate enough to trust in this tool, it is
also possible to write the settings into the device.  Use writingi with care
and on your own risk ! Be warned, you can destroy your device with this option!

(c) Hartmut Schimmel, iTU4l\@schimmelnetz.de, 2008/08/03

Usage: $prg [options]

Options: -a          automatic mode: detect the right USB device and read
                     out the data from the logger. This should be the
                     normal way.

         -s          scan only for the USB device to which the logger is
                     connected, no memory reading will be done

         -d <device> USB device to use, if the automatic detection does
                     not work, includes memory reading

         -i          only print out information about the logger settings

         -o <file>   write output to this file instead of the default file
                     name, which is generated from the actual system time

         -e          erase logger data, for security only possible together
                     with reading the memory to disk, always combine with
                     -d <device> or -a

         -w <p>=<v> write settings into the device. p is the setting parameter
                    and v is the value for it. It is only possible to write one
                    parameter at once. So you have to call the tool multiple
                    times if you want to write more than one setting.
                    Use with care and on your own risk !
                    Be warned, you can destroy your device with this option !

                    p   Parameters        Values
                    --------------------------------------------------------
                    t or time        Time-Step         0... sec
                    d or distance    Distance-Step     0... meter
                    s or sensitivity Sensitivity       2-high, 1-middle, 3-low,
                                                       0-disable
                    m or marker      Marker (Tag)      0-off, 1-on
                    v or vmin        Velocity min.     0...36 km/h

                    For writing settings you also need the wav-files which are
                    played by the logger when he talks to you. Copy them to one
                    of the following folders, because the are exepted there by
                    this programm:

                    \$HOME/wav
                    or
                    current directory

          -f        force - skip sense checks for config values to write
                    Use with care and on your own risk !
                    Be warned, you can destroy your device with this option !

          -m        MacOS X - skip the serial device initialization with stty,
                    possibly you have to set up you serial device manually to
                    115200 Baud, 8N1 before you run this program



Example: $prg -s                  # scan

         $prg -d /dev/ttyUSB1     # use /dev/ttyUSB1 as device

         $prg -a -i               # print information about logger settings

         $prg -o my_gps_log.sr    # write to file named "my_gps_log.sr"

         $prg -e                  # erase all track data on the logger


         $prg -a -w t=5           # write setting: Time-Step 5 sec
         $prg -a -w time=5        # the same

         $prg -a -w d=1000        # write setting: Distance-Step 1000 meter
         $prg -a -w distance=1000 # the same

         $prg -a -w s=2           # write setting: Sensitivity high
         $prg -a -w sensitivity=2 # the same

         $prg -a -w m=1           # write setting: Marker (Tag) on
         $prg -a -w marker=1      # the same

         $prg -a -w v=5           # write setting: Minimal Velocity 5 km/h
         $prg -a -w vmin=5        # the same

       however, in most cases it should do what you want if you only type

         $prg -a


This software is published under the GPL V2.0

EOF
}

