File Coverage

blib/lib/Ham/WSJTX/Logparse.pm
Criterion Covered Total %
statement 8 54 14.8
branch 0 22 0.0
condition 0 6 0.0
subroutine 3 7 42.8
pod 3 4 75.0
total 14 93 15.0


line stmt bran cond sub pod time code
1             package Ham::WSJTX::Logparse;
2              
3 1     1   12502 use 5.006;
  1         3  
4 1     1   3 use strict;
  1         1  
  1         14  
5 1     1   3 use warnings;
  1         3  
  1         509  
6              
7             =head1 NAME
8              
9             Ham::WSJTX::Logparse - Parses ALL.TXT log files from Joe Taylor K1JT's WSJT-X, to extract CQ and calling station
10             information for all entries in a given amateur band.
11              
12             =head1 ACKNOWLEDGEMENTS
13              
14             Much inspiration was gained from povaX's ALLmon at
15             https://github.com/poxaV/ALLmon/blob/master/ALLmon
16             Thank you, povaX!
17              
18             =head1 VERSION
19              
20             Version 0.01
21              
22             =head1 SYNOPSIS
23              
24             Extract all log entries for a given band:
25              
26             use Ham::WSJTX::Logparse;
27              
28             my $log = Ham::WSJTX::Logparse->new();
29             # Looks in the default location for the ALL.TXT file
30             # or...
31             my $log = Ham::WSJTX::Logparse->new("/path/to/an/ALL.TXT");
32             # or, can parse multiple logs...
33             my $log = Ham::WSJTX::Logparse->new("/tmp/ALL.TXT.one", "/tmp/ALL.TXT.two"); # etc., etc....
34             ...
35              
36             # Define a callback
37              
38             my $callback = sub {
39             my $date = shift;
40             my $time = shift;
41             my $power = shift;
42             my $offset = shift;
43             my $mode = shift;
44             my $callsign = shift;
45             my $grid = shift;
46             print "date $date time $time power $power offset $offset mode $mode callsign $callsign grid $grid\n";
47             # sure you can do something interesting with this!
48             };
49              
50             $log->parseForBand("20m", $callback);
51             # many entries are printed....
52              
53             =head1 EXPORT
54              
55             No functions exported; this has a purely object-oriented module.
56              
57             =head1 SUBROUTINES/METHODS
58              
59             =head2 new(optional list of files)
60              
61             Constructs a new parser, given an optional list of files. If no files are given, the default locations will be checked
62             for a WSJT-X ALL.TXT file. Returns a blessed hash.
63              
64             Note that this module has only been tested on OSX, and the location of default files on non-OSX/Windows platforms is not
65             known to the author at this time; if you know, please inform me as new will die if it tries to load a default file on
66             a platform I haven't coded for.
67              
68             =head2 files($self)
69              
70             Returns the list of files that the parser was configured with, or the default file as discovered if no files were
71             supplied in the constructor.
72              
73             =head2 parseForBand($self, $bandOfInterest, $callback)
74              
75             Parses all discovered or supplied files, correctly determining the date of each 'heard station' entry, and if the entry
76             relates to the band of interest, calls the callback with the entry details.
77              
78             The 'band of interest' is of the form nnnm, e.g. 20m, 2m, 160m, 2200m. Only one band can be filtered at any time.
79              
80             The callback is a sub as shown above in the synopsis.
81              
82             Take care with the 'grid' data in your callback: This is extracted from the logged content of a message, and must be
83             two characers followed by two digits - but if the message was 'M0CUV SV2XYZ RR73', then the grid would be decoded as
84             'RR73', which is not a valid grid square (of course, this is 'RR 73', but has been concatenated by the SV2 station).
85             Similarly with TU73. In my callback, I use:
86              
87             if ($grid =~ /(TU|RR)73/) {
88             warn "dodgy data from $date $time $callsign\n";
89             return;
90             }
91              
92             Better validation may be considered for a later release.
93              
94             An entry is considered a 'heard station' entry if it has some text (maybe CQ or a callsign), followed by some text
95             (most likely a callsign), followed by a grid square (two characters, two digits - see the note of caution in the
96             previous paragragh).
97              
98             =head1 AUTHOR
99              
100             Matt Gumbley, C<< devzendo at cpan.org> >>
101              
102             =head1 BUGS
103              
104             Please report any bugs or feature requests to C, or through
105             the web interface at L. I will be notified, and then you'll
106             automatically be notified of progress on your bug as I make changes.
107              
108             =head1 SUPPORT
109              
110             You can find documentation for this module with the perldoc command.
111              
112             perldoc Ham::WSJTX::Logparse
113              
114              
115             You can also look for information at:
116              
117             =over 4
118              
119             =item * RT: CPAN's request tracker (report bugs here)
120              
121             L
122              
123             =item * AnnoCPAN: Annotated CPAN documentation
124              
125             L
126              
127             =item * CPAN Ratings
128              
129             L
130              
131             =item * Search CPAN
132              
133             L
134              
135             =back
136              
137              
138             =head1 LICENSE AND COPYRIGHT
139              
140             Copyright 2016 Matt Gumbley.
141              
142             Licensed under the Apache License, Version 2.0 (the "License");
143             you may not use this file except in compliance with the License.
144             You may obtain a copy of the License at
145              
146             L
147              
148             Unless required by applicable law or agreed to in writing, software
149             distributed under the License is distributed on an "AS IS" BASIS,
150             WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
151             See the License for the specific language governing permissions and
152             limitations under the License.
153              
154              
155             =cut
156              
157             our $VERSION = '0.01';
158              
159             our %freqToBand = (
160             '144.491' => '2m', # +2
161             '144.489' => '2m',
162             '70.093' => '4m', # +2
163             '70.091' => '4m',
164             '50.278' => '6m', # +2
165             '50.276' => '6m',
166             '28.078' => '10m', # +2
167             '28.076' => '10m',
168             '24.919' => '12m', # +2
169             '24.917' => '12m',
170             '21.078' => '15m', # +2
171             '21.076' => '15m',
172             '18.104' => '17m', # +2
173             '18.102' => '17m',
174             '14.078' => '20m', # +2
175             '14.076' => '20m',
176             '10.14' => '30m', # +2
177             '10.138' => '30m',
178             '7.078' => '40m', # +2
179             '7.076' => '40m',
180             '5.359' => '60m', # +2
181             '5.357' => '60m',
182             '3.578' => '80m', # +2
183             '3.576' => '80m',
184             '1.84' => '160m', # +2
185             '1.838' => '160m',
186             '0.4762' => '630m', # +2
187             '0.4742' => '630m',
188             '0.13813' => '2200m', # +2
189             '0.13613' => '2200m',
190             );
191              
192             sub new {
193 0     0 1   my $class = shift;
194 0           my @files = @_;
195              
196 0 0         my @filesToUse = scalar(@files) == 0 ? (defaultAllTxtFile()) : @files;
197 0           my $obj = {
198             files => [ @filesToUse ],
199             };
200              
201 0           foreach my $file (@{$obj->{files}}) {
  0            
202 0 0         die "File '" . $file . "' not found" unless (-f $file);
203             }
204 0           bless $obj, $class;
205 0           return $obj;
206             }
207              
208             sub defaultAllTxtFile {
209 0 0 0 0 0   if ($^O eq 'darwin') {
    0          
210             # povaX' ALLmon suggests >= v1.4 uses standard OSX location:
211 0           my $homeAll = "$ENV{HOME}/Library/Application Support/WSJT-X/ALL.TXT";
212 0           my $globalAll = "/Applications/WSJT-X/ALL.TXT"; # povaX' ALLmon suggests it was here in WSJT-X <= v1.3
213 0 0         return $homeAll if (-f $homeAll);
214 0 0         return $globalAll if (-f $globalAll);
215 0           die "Could not find default ALL.TXT";
216             } elsif ($^O =~ /^MSWin/ or $^O eq 'cygwin') {
217 0           die "I don't have a Windows system to find the default location of ALL.TXT";
218             } else {
219             # It has to be some sane kind of UNIX-like, right?
220 0           die "I haven't tried this on non-OSX UNIX-likes";
221             }
222             }
223              
224             sub files {
225 0     0 1   my $self = shift;
226 0           return (@{$self->{files}});
  0            
227             }
228              
229             sub parseForBand {
230 0     0 1   my ($self, $bandOfInterest, $callback) = @_;
231 0           foreach my $file ($self->files()) {
232 0           local *F;
233 0 0         unless (open F, "<$file") {
234 0           die "Cannot open $file: $!\n";
235             }
236 0           my $currentBand = undef;
237 0           my $currentDate = undef;
238 0           while () {
239 0           chomp;
240             #print "line [$_]\n";
241             # Only interested in data from a specific band, and the indicator for changing band/mode looks like:
242             # 2015-Apr-15 20:13 14.076 MHz JT9
243             # So extract the frequency, and look up the band. This also gives us the date. Records like this are always
244             # written at startup, mode change, and at midnight.
245 0 0         if (/^(\d{4}-\S{3}-\d{2}) \d{2}:\d{2}\s+(\d+\.\d+) MHz\s+\S+\s*$/) {
246 0           $currentDate = $1;
247 0           my $frequency = $2;
248 0           $currentBand = $freqToBand{$frequency};
249             #print "data being received for $currentBand (filtering on $bandOfInterest)\n";
250 0           next;
251             }
252             # Time/Power/Freq offset/Mode/Call/Square can be extracted from records like these:
253             # 0000 -9 1.5 1259 # CQ TI4DJ EK70
254             # 0001 -1 0.5 404 # DX K1RI FN41
255             # 0001 -8 0.2 560 # KC0EFQ WA3ETR FN10
256             # 0001 -15 0.1 628 # KK7X K8MDA EN80
257             # 0002 -13 1.1 1322 # CQ YV5FRD FK60
258             # 0003 -3 0.5 1002 # TF2MSN K1RI FN41
259 0 0         if (/^(\d{4})\s+(-\d+)\s+[-\d.]+\s+(\d+)\s([#@])\s\w+\s+(\w+)\s+([A-Z]{2}\d{2})\s*$/) {
260 0           my $ctime = $1;
261 0           my $cpower = $2;
262 0           my $coffset = $3;
263 0           my $cmode = $4;
264 0           my $ccallsign = $5;
265 0           my $cgrid = $6;
266             # callsigns must have at least one digit.
267 0 0         next unless ($ccallsign =~ /\d/);
268 0 0 0       if (defined $currentDate && $bandOfInterest eq $currentBand) {
269 0           $callback->($currentDate, $ctime, $cpower, $coffset, $cmode, $ccallsign, $cgrid);
270             }
271 0           next;
272             }
273             }
274 0           close F;
275             }
276             }
277              
278             1; # End of Ham::WSJTX::Logparse