3 # ╔══════════════════════════════════════════════════════════════
5 # ║ Converts between various GPS formats
6 # ║ Character set: UTF-8
7 # ║ License: GNU General Public License
8 # ║ Made by Øyvind A. Holm — sunny@sunbase.org
9 # ╚══════════════════════════════════════════════════════════════
14 use Time
::Local qw
{ timegm_nocheck
};
20 our ($opt_c, $opt_d, $opt_e, $opt_h, $opt_o, $opt_s, $opt_t, $opt_u) =
21 ( 0, 0, 0, 0, "", 0, 0, 0);
26 my $PAUSE_LIMIT = 2 * 60; # Antall sekunder mellom to punkter det må til før en move legges inn.
27 my $Des = $opt_c ?
"," : ".";
28 my $in_dupskip = 0; # Er 1 hvis vi holder på med ignorering av duplikater
29 my $found_move = 0; # Settes til 1 hvis en /^# move$/ blir funnet.
31 my ($last_lon, $last_lat, $last_alt, $last_line) =
32 ( 1000, 1000, 100000, ""); # Vi kan jo teoretisk sett være i Greenwich eller på ekvator
35 # Syntax screen to stdout and exit gracefully {{{
38 Syntax: gptrans_conv [options] [files [...]]
39 gptrans_conv -u [files [...]]
41 -c Use comma instead of period as decimal point (For Gnumeric etc)
42 -d Skip duplicated coordinates, only print first and last
43 -e Use seconds since 1970-01-01 00:00:00 GMT as date format
45 -o x Use output format x:
51 -t Create breaks in track between points with a difference
52 more than $PAUSE_LIMIT seconds
53 -u Comment out following data with identical position values,
54 only print first entry
61 # Kunne vært et eget script på grunn av at det gjør sine helt egne greier, men like greit å samle det på en plass.
62 # FIXME: Fjerner ikke første duplikatentryen.
63 # FIXME: Se om det går å få flytta den inn i print_entry() så man slipper å ha to gptrans_conv’er i pipen.
64 # FIXME: Legg inn alle formatene.
66 # Comment out areas without reception {{{
67 my ($start_date, $end_date, $found_dup) = ("", "", 0);
70 if (m
#^1 (\S+) (\S+) (\S+) (\S+) (\d\d)/(\d\d)/(\d\d\d\d) (\d\d):(\d\d):(\d\d)#) {
72 my ($lat_val, $lon_val, $Speed, $Unkn, $Month, $Day, $Year, $Hour, $Min, $Sec) =
73 ( $1, $2, $3, $4, $5, $6, $7, $8, $9, $10);
74 if (($lat_val eq $last_lat) && ($lon_val eq $last_lon)) {
76 $start_date = "$Year$Month${Day}T$Hour$Min$Sec";
81 $end_date = "$Year$Month${Day}T$Hour$Min$Sec";
84 print("# $start_date-$end_date: CO: No signal \x7B\x7B\x7B\n");
88 print("# $start_date-$end_date: CO: No signal \x7D\x7D\x7D\n# move\n$_");
106 print("# $start_date-$end_date: CO: No signal \x7B\x7B\x7B\n");
110 print("# $start_date-$end_date: CO: No signal \x7D\x7D\x7D\n# move\n");
117 if ($opt_o eq "gpstrans") {
118 print("Format: DMS UTC Offset: 0.00 hrs Datum[100]: WGS 84\n");
119 } elsif ($opt_o eq "ps") {
120 print(ps_header
(532, 6034, 533, 6040));
125 # Scan through stdin or specified files and send every GPS entry to print_entry() {{{
128 } elsif (/^# move$/) {
131 print unless ($opt_o =~ /^(xgraph|gpstrans)$/);
132 } elsif (m
#^(\d+)\t([0-9\.\-,?]+)\t([0-9\.\-,?]+)\t([+\-\d?])#) {
133 # Author’s format, epoch style {{{
134 my ($ep_time, $lon_val, $lat_val, $Alt) =
136 my ($Sec, $Min, $Hour, $Day, $Month, $Year, $Wday, $Yday) = gmtime($ep_time);
138 $Year += 1900; # Urgh Ⅱ
139 print_entry
($Year, $Month, $Day, $Hour, $Min, $Sec, $lon_val, $lat_val, $Alt);
141 } elsif (m
#^(\d\d\d\d)-?(\d\d)-?(\d\d)[T ](\d\d):?(\d\d):?(\d\d)Z?\t([0-9\.\-,?]+)\t([0-9\.\-,?]+)\t([+\-\d?])#) {
142 # Author’s format, human-readable date format {{{
143 my ($Year, $Month, $Day, $Hour, $Min, $Sec, $lon_val, $lat_val, $Alt) =
144 ( $1, $2, $3, $4, $5, $6, $7, $8, $9);
145 print_entry
($Year, $Month, $Day, $Hour, $Min, $Sec, $lon_val, $lat_val, "?");
147 } elsif (m
#^T\t(\d\d)/(\d\d)/(\d\d\d\d) (\d\d):(\d\d):(\d\d)\t(.+)\xB0(.+)'(.+)"\t(.+)\xB0(.+)'(.+)"#) {
148 # T 09/01/2002 11:51:26 60°23'36.3" 5°19'35.9" {{{
149 my ($Month, $Day, $Year, $Hour, $Min, $Sec, $lat_d, $lat_m, $lat_s, $lon_d, $lon_m, $lon_s) =
150 ( $1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12);
151 my $lat_val = sprintf("%.5f", 1*($lat_d+($lat_m/60)+($lat_s/3600)));
152 my $lon_val = sprintf("%.5f", $lon_d+($lon_m/60)+($lon_s/3600));
153 print_entry
($Year, $Month, $Day, $Hour, $Min, $Sec, $lon_val, $lat_val, "?");
155 } elsif (m
#^1 (\S+) (\S+) (\S+) (\S+) (\d\d)/(\d\d)/(\d\d\d\d) (\d\d):(\d\d):(\d\d)#) {
156 # 1 60.3938222 5.3238754 17.3 0 09/01/2002 14:18:23 {{{
157 my ($lat_val, $lon_val, $Speed, $Unkn, $Month, $Day, $Year, $Hour, $Min, $Sec) =
158 ( $1, $2, $3, $4, $5, $6, $7, $8, $9, $10);
159 print_entry
($Year, $Month, $Day, $Hour, $Min, $Sec, $lon_val, $lat_val, "?");
162 # @020721221336N6048353E00701826S015-00001E4859N1673U0000 {{{
172 (..) # Latitude degree
173 (..) # Latitude minute
174 (\d\d\d
) # Latitude minute decimals
176 (\d\d\d
) # Longitude degree
177 (\d\d
) # Longitude minute
178 (\d\d\d
) # Longitude minute degree
184 my ($Alfa, $Year, $Month, $Day, $Hour, $Min, $Sec, $NS, $Y_deg, $Y_degmin, $Y_mindec, $EW, $X_deg, $X_degmin, $X_mindec, $Accur, $Alt, $Rest) =
185 ( $1, $2+2000, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15, $16, $17, $18);
186 my $ep_time = timegm_nocheck
($Sec, $Min, $Hour, $Day, $Month-1, $Year);
187 $last_time = $ep_time;
188 my $tmp_x = sprintf("%.5f", $X_deg + $X_degmin/60 + $X_mindec/60000);
189 my $tmp_y = sprintf("%.5f", $Y_deg + $Y_degmin/60 + $Y_mindec/60000);
190 $tmp_x =~ s/\./$Des/;
191 $tmp_y =~ s/\./$Des/;
192 print_entry
($Year, $Month, $Day, $Hour, $Min, $Sec, $tmp_x, $tmp_y, $Alt);
194 } elsif (/^(@)(\d\d)(\d\d)(\d\d)(\d\d)(\d\d)(\d\d)(__________________________________________)/) {
195 # @020721221336__________________________________________ {{{
196 my ($Alfa, $Year, $Month, $Day, $Hour, $Min, $Sec, $Rest) =
197 ( $1, $2+2000, $3, $4, $5, $6, $7, $8);
198 print("\n") unless ($opt_o =~ /^(xgraph|gpstrans)$/);
201 } elsif (/^xmaplog /) {
202 print("\n") unless ($opt_o =~ /^(xgraph|gpstrans)$/);
204 print("\n") unless ($opt_o =~ /^(xgraph|gpstrans)$/);
208 warn("Line $.: Unknown: \"$_\"\n");
213 if ($opt_o eq "ps") {
224 # Print a GPS entry with time, latitude, longitude and altitude in various formats {{{
225 my ($Year, $Month, $Day, $Hour, $Min, $Sec, $Lon, $Lat, $Alt) = @_;
226 my $ep_time = timegm_nocheck
($Sec, $Min, $Hour, $Day, $Month-1, $Year);
227 $Year = sprintf("%04u", $Year);
228 $Month = sprintf("%02u", $Month);
229 $Day = sprintf("%02u", $Day);
230 $Hour = sprintf("%02u", $Hour);
231 $Min = sprintf("%02u", $Min);
232 $Sec = sprintf("%02u", $Sec);
236 if ($opt_o eq "ps") {
240 if ($opt_d && ($Lon eq $last_lon) && ($Lat eq $last_lat) && ($Alt eq $last_alt)) {
249 $in_dupskip && print($last_line);
253 if ($opt_t && $ep_time-$last_time > $PAUSE_LIMIT && $last_time) {
254 $pause_len = $ep_time-$last_time;
257 if ($pause_len && ($opt_o !~ /^(xgraph|gpstrans)$/)) {
258 printf("# Pause: %s\n# move\n", sec_to_readable
($ep_time-$last_time));
262 # Valid data was found, send to stdout {{{
263 if ($opt_o eq "xgraph") {
264 $pause_len && ($Line = "move ");
265 ($Line .= "$Lon $Lat\n");
266 } elsif($opt_o eq "gpstrans") {
267 my ($gpt_lat, $gpt_lon) = (ddd_to_dms
($Lat), ddd_to_dms
($Lon));
268 $Line = "T\t$Month/$Day/$Year $Hour:$Min:$Sec\t$gpt_lat\t$gpt_lon\n";
269 } elsif ($opt_o eq "clean") {
270 $pause_len && ($Line = "\n");
271 ($Line .= "$Lon $Lat\n");
272 } elsif ($opt_o eq "ps") {
273 $Line = ($pause_len ?
"f\n$Lon $Lat m\n" : "$Lon $Lat l\n");
275 # Default format used by the author. Yes, the Universe evolves around him. ☺ {{{
276 # Seriously, up to "gptrans_conv,v 1.11 2003/08/03 02:35:51 sunny" the script was only
277 # used by myself, and I found this format was the easiest to live with — Perl scripts and
278 # shell utilities made conversion pretty painless. If GPX or something other useful is
279 # implemented, that will be the default.
282 # $do_print || print("skipping ");
284 $opt_e ?
$ep_time : ($opt_s ?
"${Year}${Month}${Day}T${Hour}${Min}${Sec}Z" : "$Year-$Month-$Day $Hour:$Min:$Sec"),
295 if (!$last_time && $opt_o eq "ps") {
296 print("$Lon $Lat m\n");
301 (!$pause_len && ($opt_o eq "xgraph")) && ($Line = "move $Line");
306 $last_time = $ep_time;
315 # Convert seconds since 1970 to "yyyy-mm-dd hh:mm:ss" with optional separator {{{
316 my ($Seconds, $Sep) = @_;
317 defined($Sep) || ($Sep = " ");
318 my @TA = gmtime($Seconds);
319 my($DateString) = sprintf("%04u-%02u-%02u%s%02u:%02u:%02u", $TA[5]+1900, $TA[4]+1, $TA[3], $Sep, $TA[2], $TA[1], $TA[0]);
324 sub sec_to_readable
{
325 # Convert seconds since 1970 to human-readable format (d:hh:mm:ss) {{{
327 my ($Day, $Hour, $Min, $Sec) =
330 $Day = int($secs/86400);
331 $secs -= $Day * 86400;
333 $Hour = int($secs/3600);
334 $secs -= $Hour * 3600;
336 $Min = int($secs/60);
341 return(sprintf("%u:%02u:%02u:%02u", $Day, $Hour, $Min, $Sec));
346 # Send a Postscript header to stdout {{{
347 my ($bl_x, $bl_y, $br_x, $br_y) = @_;
348 my $Date = sec_to_string
(time);
350 %!PS-Adobe-3.0 EPSF-3.0
353 %%CreationDate: $Date
354 %%BoundingBox: $bl_x $bl_y $br_x $br_y
355 %%DocumentData: Clean7Bit
358 /bd { bind def } bind def
359 /incompound false def
363 /F { incompound not {fill} if } bd
364 /f { closepath F } bd
366 /*u { /incompound true def } bd
367 /*U { /incompound false def f} bd
368 /k { setcmykcolor } bd
378 # Convert floating-point degrees into D°M'S.S" (ISO-8859-1). Necessary for import into GPSman. {{{
379 # Based on toDMS() from gpstrans-0.39 to ensure compatibility.
382 my ($Hour, $Min, $Sec) =
391 $ddd = ($ddd - $Hour) * 60.0;
393 $Sec = ($ddd - $Min) * 60.0;
406 D
("Neg = $Neg , D = $Hour , M = $Min , S = $Sec\n");
407 $Retval = sprintf("%s%.0f\xB0%02.0f'%04.1f\"", $Neg ?
"-" : "", $Hour, $Min, $Sec);
413 # Send debug messages to stderr if $Debug {{{
414 $Debug && print(STDERR
@_);
418 # vim: set fdm=marker ft=perl fenc=utf-8 :