File Coverage

blib/lib/Parse/SpectrumDirect/RadioFrequency.pm
Criterion Covered Total %
statement 79 82 96.3
branch 18 22 81.8
condition n/a
subroutine 12 12 100.0
pod 4 4 100.0
total 113 120 94.1


line stmt bran cond sub pod time code
1             package Parse::SpectrumDirect::RadioFrequency;
2             BEGIN {
3 2     2   35954 $Parse::SpectrumDirect::RadioFrequency::VERSION = '0.101';
4             }
5 2     2   22 use warnings;
  2         4  
  2         74  
6 2     2   16 use strict;
  2         4  
  2         96  
7 2     2   84 use 5.008;
  2         10  
  2         9116  
8              
9             # ABSTRACT: Parse Industry Canada "Spectrum Direct" radio frequency search output
10              
11              
12             sub new
13             {
14 1     1 1 684 my ($class) = @_;
15 1         5 return bless({},$class);
16             }
17              
18              
19             sub parse
20             {
21 4     4 1 387 my ($self, $raw) = @_;
22 4         8 delete $self->{legend};
23 4         6 delete $self->{stations};
24              
25 4 100       18 return undef unless $raw;
26              
27 2 100       4 if( ! $self->_extract_legend( $raw ) ) {
28 1         2 delete $self->{legend};
29 1         4 return undef;
30             }
31 1 50       3 if( ! $self->_extract_stations( $raw ) ) {
32 0         0 delete $self->{legend};
33 0         0 delete $self->{stations};
34 0         0 return undef;
35             }
36              
37 1         11 return 1;
38             }
39              
40              
41             sub get_legend
42             {
43 1     1 1 4 my ($self) = @_;
44 1         6 return $self->{legend};
45             }
46              
47              
48             sub get_stations
49             {
50 3     3 1 2434 my ($self) = @_;
51 3         23 return $self->{stations};
52             }
53              
54             # Extract legend as an arrayref of hashrefs.
55             sub _extract_legend
56             {
57 2     2   6 my ($self, $raw) = @_;
58              
59 2         131 my ($raw_legend) = $raw =~ m/Field Position Legend(.*)/sm;
60 2 100       11 return undef unless $raw_legend;
61              
62 1         4 $self->{legend} = [];
63 1         27 foreach my $line (split(/\n/, $raw_legend)) {
64              
65             # Lines are in the format of:
66             # name start - end
67             # with the columns starting at 1.
68              
69 73         394 my ($name, $start, $end) = $line =~ m/(.*?)\s+(\d+) - (\d+)/;
70 73 100       132 next unless $name;
71 69         130 $name =~ s/\s+$//;
72              
73             # Pull off units
74 69         61 my $units = undef;
75 69 100       132 if( $name =~ m/\((.*?)\)$/ ) {
76 27         43 $units = $1;
77             }
78              
79 69         67 my $key = $name;
80 69         105 $key =~ s/\(.*?\)//g;
81 69         127 $key =~ s/\s+$//;
82 69         193 $key =~ s/\s+/_/g;
83              
84 69         265 my $col = {
85             key => $key,
86             units => $units,
87             name => $name,
88             start => $start - 1,
89             len => $end - $start + 1,
90             };
91 69         67 push @{$self->{legend}},$col;
  69         162  
92              
93             }
94              
95 1         9 return $self->{legend};
96             }
97              
98             # Return station data as an arrayref of hashrefs, one per row.
99             sub _extract_stations
100             {
101 1     1   2 my ($self, $raw) = @_;
102              
103 1         526 my ($data) = $raw =~ m/\[DATA\](.*)\[\/DATA\]/sm;
104 1 50       5 return undef unless $data;
105              
106 1         2 my $regex = join('\s', map { "(.{$_->{len},$_->{len}})" } @{$self->{legend}} );
  69         121  
  1         2  
107 1         6 my @key_ary = map { $_->{key} } @{$self->{legend}};
  69         83  
  1         3  
108              
109 1         6 $self->{stations} = [];
110 1         907 foreach my $line (split(/\n/,$data)) {
111 415         19071 my (@tmprow) = $line =~ /$regex/o;
112              
113 415         1486 my %row;
114 415         1273 @row{@key_ary} = map { s/^\s+|\s+$//g; $_ } @tmprow;
  28566         108315  
  28566         65180  
115 415         3492 push @{$self->{stations}}, \%row;
  415         4155  
116             }
117 1         105 $self->_fixup_station_data();
118              
119 1         14 return $self->{stations};
120             }
121              
122             # Fix some common stupidity with station data
123             sub _fixup_station_data
124             {
125 1     1   3 my ($self) = @_;
126              
127              
128             # Convert to decimal degrees from ddmmss. Also, force
129             # longitude to west (negative), since this is Canada we're
130             # dealing with.
131 1         2 foreach my $s (@{$self->{stations}}) {
  1         5  
132 415 50       1496 $s->{Latitude} = _dd_from_dms( $s->{Latitude} ) if exists $s->{Latitude};
133 415 50       1282 $s->{Longitude} = 0 - _dd_from_dms( $s->{Longitude} ) if exists $s->{Longitude};
134             }
135              
136             # Change units in legend, too
137 1         5 foreach my $l (@{$self->{legend}}) {
  1         5  
138 69 100       177 if( $l->{key} =~ /^(?:Latitude|Longitude)$/ ) {
139 2         6 $l->{units} = 'decimal degrees';
140             }
141             }
142             }
143              
144             sub _dd_from_dms
145             {
146 830     830   1073 my ($dms) = @_;
147              
148 830 100       1275 return 0.0 unless $dms;
149              
150 816         1206 my $ss = substr( $dms, -2, 2, '');
151 816         1001 my $mm = substr( $dms, -2, 2, '');
152 816         832 my $dd = $dms;
153              
154 816         4949 return sprintf('%.6f', $dd + ($mm * 60 + $ss)/3600);
155             }
156              
157              
158             1;
159              
160              
161             =pod
162              
163             =head1 NAME
164              
165             Parse::SpectrumDirect::RadioFrequency - Parse Industry Canada "Spectrum Direct" radio frequency search output
166              
167             =head1 VERSION
168              
169             version 0.101
170              
171             =head1 SYNOPSIS
172              
173             my $parser = Parse::SpectrumDirect::RadioFrequency->new();
174              
175             $parser->parse( $prefetched_output );
176              
177             my $legend_hash = $parser->get_legend();
178              
179             my $stations = $parser->get_stations();
180              
181             =head1 DESCRIPTION
182              
183             This module provides a parser for the "Radio Frequency Search" text-format
184             output from Industry Canada's Spectrum Direct service. This service provides
185             information on the location of RF spectrum licensing, transmitter locations,
186             etc.
187              
188             The service is available at http://www.ic.gc.ca/eic/site/sd-sd.nsf/eng/home
189              
190             The text export is a series of fixed-width fields, with field locations and
191             descriptions present in a legend at the end of the data file.
192              
193             =head1 METHODS
194              
195             =head2 new ( )
196              
197             Creates a new parser.
198              
199             =head2 parse ( $raw )
200              
201             Parses the raw data provided. Returns a true value if successful, a false if
202             not.
203              
204             Parsed data can be obtained with get_legend() and get_stations() (see below).
205              
206             =head2 get_legend ()
207              
208             Returns the description of fields as parsed from the input data.
209              
210             Return value is an array reference containing one hash reference per field.
211              
212             Each hashref contains:
213              
214             =over 4
215              
216             =item name
217              
218             As in source legend, stripped of trailing spaces
219              
220             =item units
221              
222             Units for data value, if determinable from legend.
223              
224             =item key
225              
226             Key used in station hashes. Generated from name stripped of unit information, and whitespaces converted to _.
227              
228             =item start
229              
230             Column index to start extracting data value
231              
232             =item len
233              
234             Column width, used for data extraction.
235              
236             =back
237              
238             =head2 get_stations ()
239              
240             Returns station information as parsed from the input data.
241              
242             =head1 SUPPORT
243              
244             You can find documentation for this module with the perldoc command.
245              
246             perldoc Parse::SpectrumDirect::RadioFrequency
247              
248             You can also look for information at:
249              
250             =over 4
251              
252             =item * RT: CPAN's request tracker
253              
254             L
255              
256             =item * Search CPAN
257              
258             L
259              
260             =item * Github
261              
262             L
263              
264             =back
265              
266             =head1 AUTHOR
267              
268             Dave O'Neill
269              
270             =head1 COPYRIGHT AND LICENSE
271              
272             This software is copyright (c) 2010 by Dave O'Neill.
273              
274             This is free software; you can redistribute it and/or modify it under
275             the same terms as perl itself.
276              
277             =cut
278              
279              
280             __END__