1#!/usr/local/bin/perl
2
3
4# Written by Curt Mills, WE7U
5# Released to the public domain.
6#
7
8
9# Script to create Xastir "Overlay" files from "CSV" files of the
10# proper format (comma-delimited files).
11#
12# 1) Creates files in Xastir "log" format if you enter a callsign
13# below.  These files can then be put in your ~/.xastir/logs/
14# directory and brought in via the File->Open Log File menu option.
15# If you start with the CSV files in the ~/.xastir/logs/ directory
16# and process them there with this script, the output files will be
17# placed into the proper place for Xastir to find them.
18#
19# 2) If no callsign is entered, this script will create files in
20# Xastir's "~/.xastir/config/object.log" format.  You can then
21# replace or append the file to the object.log file, restart Xastir
22# or "Reload Object/Item History".
23#
24# NOTE: These APRS Items will become part of your locally
25# owned/transmitted objects, so if you don't want them transmitted,
26# turn of Object/Item transmit before bringing them in.
27#
28# Input:  Directory name.  If no directory name passed in, it will
29# operate on every CSV file in the current directory.
30#
31# Input format:
32# Name  N/S  lat  E/W  long   //  comment fields.............
33# SUPPLY,N,34.0000,W,78.0000,ICON,text1,text2,text3,text4,...
34#
35#
36# The name will have spaces removed if it is longer than nine
37# characters.  If it is still too long, vowels will be removed, then
38# it will be truncated to nine characters if still needed.  The full
39# name will be transmitted as a comment.  All other text fields will
40# also be transmitted as comments, so that they will all appear in
41# the Station Info dialog.
42#
43# Icons will be a default small red circle unless '/' or '\' is the
44# leading character in that field and the next specifies the APRS
45# symbol.  In that case the two-letter combination will get used as
46# the symbol for the Item.
47
48
49
50# Change this to match whatever callsign you're running Xastir as,
51# so that the APRS Items appear to have been generated locally.  You
52# can then suck this file in as a "log" file from within Xastir.  If
53# this field is empty, then instead write the packets out without a
54# header, as in Xastir's "object.log" format.
55$callsign = "";
56
57if ($callsign ne "") {
58  $callsign = uc($callsign) . '>APRS:';
59}
60
61
62
63# Main program.  Process every ".csv" file found in the directory.
64#
65$dirname = shift;
66if ($dirname == "") {
67  $dirname = ".";
68}
69
70opendir(DIR, $dirname) or die "Can't opendir $dirname: %!";
71
72while (defined($file = readdir(DIR))) {
73  chomp $file;
74
75  if ( $file =~ /\.csv$/ ) {
76
77    # Do something with "$dirname/$file"
78    &process_file($dirname, $file);
79  }
80  else {
81#    printf("$dirname/$file\n");
82  }
83}
84
85
86
87# Process one file.  Creates an output file that matches the
88# basename but changes the "csv" to "overlay".
89#
90sub process_file() {
91
92  $output_file = $_[1];
93  $output_file =~ s/\.csv$/.overlay/;
94
95  printf("$dirname/$file -> $output_file\n");
96
97  open(SOURCE, "< $_[1]")
98    or die "Couldn't open $path for reading: $!\n";
99
100  open(OUTPUT, "> $output_file")
101    or die "Couldn't open $output_file for writing: $!\n";
102
103  while (<SOURCE>) {
104    &process_line($_);
105  }
106
107  close(SOURCE);
108  close(OUTPUT);
109}
110
111
112
113# Process one line.  Write the formatted data to the OUTPUT file.
114#
115sub process_line() {
116  #printf("$_[0]");
117
118  # Parse the CSV line into an array
119  @list = parse_csv($_[0]);
120
121  if (!($list[0] =~ m/Location/i)) {
122    #print OUTPUT @list;
123
124    # As a temporary measure, create items out of each of the lines,
125    # with a status line for each extra data object so that they
126    # appear in the Station Info dialog.
127    &create_items(@list);
128  }
129}
130
131
132
133# Create an APRS Item out of each array.  Create a status line for
134# each extra column associated with a line so that the info shows up
135# in the Station Info dialog.
136#
137# Examples.  Name is 3-9 characters:
138# )AID #2!4903.50N/07201.75WA
139# )G/WB4APR!53  .  N\002  .  Wd
140#
141sub create_items {
142
143  $name = $_[0];
144  if ($name eq "") {
145    printf("Error, name column is empty\n");
146  }
147
148  # If too long, try removing spaces first
149  if (length($name) > 9) {
150    $name =~ s/\s//ig;
151  }
152
153  # If still too long, remove vowels
154  if (length($name) > 9) {
155    $name =~ s/a//ig;
156    $name =~ s/e//ig;
157    $name =~ s/i//ig;
158    $name =~ s/o//ig;
159    $name =~ s/u//ig;
160  }
161
162  # Extend to three characters if short
163  if (length($name) < 3) {
164    $name = $name . "   ";
165  }
166
167  $name = substr($name,0,9);
168  $name[9] = "\0";  # Terminate name at 9 characters, minimum 3
169
170  $n_s = uc( substr($_[1],0,1) );
171  $latitude = $_[2];
172  $e_w = uc( substr($_[3],0,1) );
173  $longitude = $_[4];
174  if ($_[5] =~ /ICON/i) {
175    $icon1 = "/";
176    $icon2 = "/";
177  }
178  else {
179    $icon1 = substr($_[5],0,1);
180    $icon2 = substr($_[5],1,1);
181  }
182
183  # Convert lat/long to APRS format (or Base-91 Compressed format)
184  $lat_deg = $latitude;
185  $lat_deg =~ s/\.\d+$//;
186  $lat_deg = sprintf("%02d", $lat_deg);
187  $lat_min = $latitude;
188  $lat_min =~ s/^\d+\./0./;
189  $lat_min = $lat_min * 60.0;
190  $lon_deg = $longitude;
191  $lon_deg =~ s/\.\d+$//;
192  $lon_deg = sprintf("%03d", $lon_deg);
193  $lon_min = $longitude;
194  $lon_min =~ s/^\d+\./0./;
195  $lon_min = $lon_min * 60;
196
197  # Create an APRS "Item" packet
198  $line = sprintf("%s)%s!%s%05.2f%s%s%s%05.2f%s%s",
199    $callsign,
200    $name,
201    $lat_deg,
202    $lat_min,
203    $n_s,
204    $icon1,
205    $lon_deg,
206    $lon_min,
207    $e_w,
208    $icon2);
209
210  # Go process the rest of the columns, if any.  Create APRS Item
211  # packets with comments from them.
212  for ($i = 6; $i < 106; $i++) {
213    chomp $_[$i];
214    if ($_[$i] ne "") {
215      #printf("$_[$i]");
216      printf(OUTPUT "%s%s\n",
217        $line,
218        $_[$i]);
219    }
220  }
221
222  # Write it out to the file, with the full name as a comment
223  printf(OUTPUT "%s%s\n",
224    $line,
225    $_[0]);
226}
227
228
229
230# Parse CSV line into an array, removing double-quotes and such if
231# found.
232#
233sub parse_csv {
234  my $text = shift; # Record containing comma-separated values
235  my @new = ();
236  push(@new, $+) while $text =~ m{
237    # The first part groups the phrase inside the quotes.
238    # See explanation of this pattern in MRE
239    "([^\"\\]*(?:\\.[^\"\\]*)*)",?
240      | ([^,]+),?
241      |  ,
242  }gx;
243  push(@new, undef) if substr($text, -1, 1) eq ',';
244  return @new;  # List of values that were comma-separated
245}
246
247
248