File Coverage

blib/lib/Ham/APRS/DeviceID.pm
Criterion Covered Total %
statement 58 83 69.8
branch 33 60 55.0
condition n/a
subroutine 5 8 62.5
pod 2 2 100.0
total 98 153 64.0


line stmt bran cond sub pod time code
1              
2             package Ham::APRS::DeviceID;
3              
4             =head1 NAME
5              
6             Ham::APRS::DeviceID - APRS device identifier
7              
8             =head1 SYNOPSIS
9              
10             use Ham::APRS::FAP qw(parseaprs);
11             use Ham::APRS::DeviceID;
12             use Data::Dumper;
13            
14             my $aprspacket = 'OH2RDP>APZMDR,OH2RDG*,WIDE:!6028.51N/02505.68E#PHG7220/RELAY,WIDE, OH2AP Jarvenpaa';
15             my %packet;
16             my $retval = parseaprs($aprspacket, \%packet);
17             if ($retval == 1) {
18             Ham::APRS::DeviceID::identify(\%packet);
19            
20             if (defined $packet{'deviceid'}) {
21             print Dumper($packet{'deviceid'});
22             }
23             }
24              
25             =head1 ABSTRACT
26              
27             This module attempts to identify the manufacturer, model and
28             software version of an APRS transmitter. It looks at details found
29             in the parsed APRS packet (as provided by Ham::APRS::FAP) and updates
30             the hash with the identification information, if possible.
31              
32             =head1 DESCRIPTION
33              
34             Unless a debugging mode is enabled, all errors and warnings are reported
35             through the API (as opposed to printing on STDERR or STDOUT), so that
36             they can be reported nicely on the user interface of an application.
37              
38             This module requires a reasonably recent L module.
39              
40             =head1 EXPORT
41              
42             None by default.
43              
44             =head1 FUNCTION REFERENCE
45              
46             =cut
47              
48 3     3   498333 use strict;
  3         7  
  3         146  
49 3     3   21 use warnings;
  3         7  
  3         24871  
50              
51             #use Data::Dumper;
52              
53             require Exporter;
54              
55             our @ISA = qw(Exporter);
56              
57             # Items to export into callers namespace by default. Note: do not export
58             # names by default without a very good reason. Use EXPORT_OK instead.
59             # Do not simply export all your public functions/methods/constants.
60              
61             # This allows declaration use Ham::APRS::FAP ':all';
62             # If you do not need this, moving things directly into @EXPORT or @EXPORT_OK
63             # will save memory.
64             ##our %EXPORT_TAGS = (
65             ## 'all' => [ qw(
66             ##
67             ## ) ],
68             ##);
69              
70             our @EXPORT_OK = (
71             ## @{ $EXPORT_TAGS{'all'} },
72             '&identify',
73             );
74              
75             ##our @EXPORT = qw(
76             ##
77             ##);
78              
79             our $VERSION = '1.07';
80              
81             # Preloaded methods go here.
82              
83             # no debugging by default
84             my $debug = 0;
85              
86             my %result_messages = (
87             'unknown' => 'Unsupported packet format',
88             'no_dstcall' => 'Packet has no destination callsign',
89             'no_format' => 'Packet has no defined format',
90             'mice_no_comment' => 'Mic-e packet with no comment defined',
91             'mice_no_deviceid' => 'Mic-e packet with no device identifier in comment',
92             'no_id' => 'No device identification found',
93             );
94              
95             # these functions are used to report warnings and parser errors
96             # from the module
97              
98             sub _a_err($$;$)
99             {
100 0     0   0 my ($rethash, $errcode, $val) = @_;
101            
102 0         0 $rethash->{'deviceid_resultcode'} = $errcode;
103 0 0       0 $rethash->{'deviceid_resultmsg'}
104             = defined $result_messages{$errcode}
105             ? $result_messages{$errcode} : $errcode;
106            
107 0 0       0 $rethash->{'deviceid_resultmsg'} .= ': ' . $val if (defined $val);
108            
109 0 0       0 if ($debug > 0) {
110 0         0 warn "Ham::APRS::DeviceID ERROR $errcode: " . $rethash->{'deviceid_resultmsg'} . "\n";
111             }
112            
113 0         0 return 0;
114             }
115              
116             sub _a_warn($$;$)
117             {
118 0     0   0 my ($rethash, $errcode, $val) = @_;
119            
120 0         0 push @{ $rethash->{'deviceid_warncodes'} }, $errcode;
  0         0  
121            
122 0 0       0 if ($debug > 0) {
123 0 0       0 warn "Ham::APRS::DeviceID WARNING $errcode: "
    0          
124             . (defined $result_messages{$errcode}
125             ? $result_messages{$errcode} : $errcode)
126             . (defined $val ? ": $val" : '')
127             . "\n";
128             }
129            
130 0         0 return 0;
131             }
132              
133              
134             =over
135              
136             =item debug($enable)
137              
138             Enables (debug(1)) or disables (debug(0)) debugging.
139              
140             When debugging is enabled, warnings and errors are emitted using the warn() function,
141             which will normally result in them being printed on STDERR. Succesfully
142             printed packets will be also printed on STDOUT in a human-readable
143             format.
144              
145             When debugging is disabled, nothing will be printed on STDOUT or STDERR -
146             all errors and parsing results need to be collected from the returned
147             hash reference.
148              
149             =back
150              
151             =cut
152              
153             sub debug($)
154             {
155 0     0 1 0 my $dval = shift @_;
156 0 0       0 if ($dval) {
157 0         0 $debug = 1;
158             } else {
159 0         0 $debug = 0;
160             }
161             }
162              
163             my %response = (
164             'd7' => {
165             'vendor' => 'Kenwood',
166             'model' => 'TH-D7',
167             'class' => 'ht',
168             'messaging' => 1,
169             },
170             'd72' => {
171             'vendor' => 'Kenwood',
172             'model' => 'TH-D72',
173             'class' => 'ht',
174             'messaging' => 1,
175             },
176             'd700' => {
177             'vendor' => 'Kenwood',
178             'model' => 'TM-D700',
179             'class' => 'rig',
180             'messaging' => 1,
181             },
182             'd710' => {
183             'vendor' => 'Kenwood',
184             'model' => 'TM-D710',
185             'class' => 'rig',
186             'messaging' => 1,
187             },
188             'vx8' => {
189             'vendor' => 'Yaesu',
190             'model' => 'VX-8',
191             'class' => 'ht',
192             'messaging' => 1,
193             },
194             'vx8g' => {
195             'vendor' => 'Yaesu',
196             'model' => 'VX-8G',
197             'class' => 'ht',
198             'messaging' => 1,
199             },
200             'ftm350' => {
201             'vendor' => 'Yaesu',
202             'model' => 'FTM-350',
203             'class' => 'rig',
204             'messaging' => 1,
205             },
206             'tt3' => {
207             'vendor' => 'Byonics',
208             'model' => 'TinyTrak3',
209             'class' => 'tracker',
210             },
211             'tt4' => {
212             'vendor' => 'Byonics',
213             'model' => 'TinyTrak4',
214             'class' => 'tracker',
215             },
216             'kissoz' => {
217             'vendor' => 'OZ1EKD, OZ7HVO',
218             'model' => 'KissOZ',
219             'class' => 'tracker',
220             },
221             'anyfrog' => {
222             'vendor' => 'HinzTec',
223             'model' => 'anyfrog',
224             'class' => 'tracker',
225             },
226             'unknown' => {
227             'vendor' => 'Unknown',
228             'model' => 'Other Mic-E',
229             },
230             );
231              
232             my %fixed_dstcalls = (
233             'APRS' => {
234             'vendor' => 'unknown',
235             'model' => 'unknown',
236             },
237            
238             'AP1WWX' => {
239             'vendor' => 'TAPR',
240             'model' => 'T-238+',
241             'class' => 'wx',
242             },
243            
244             'APCLEY' => {
245             'vendor' => 'ZS6EY',
246             'model' => 'EYTraker',
247             'class' => 'tracker',
248             },
249             'APCLWX' => {
250             'vendor' => 'ZS6EY',
251             'model' => 'EYWeather',
252             'class' => 'wx',
253             },
254             'APCLEZ' => {
255             'vendor' => 'ZS6EY',
256             'model' => 'Telit EZ10 GSM application',
257             'class' => 'tracker',
258             },
259            
260             'APZMDR' => {
261             'vendor' => 'Open Source',
262             'model' => 'HaMDR',
263             'class' => 'tracker',
264             'os' => 'embedded',
265             },
266             'APZTKP' => {
267             'vendor' => 'Nick Hanks, N0LP',
268             'model' => 'TrackPoint',
269             'class' => 'tracker',
270             'os' => 'embedded',
271             },
272             'APJID2' => {
273             'vendor' => 'Peter Loveall, AE5PL',
274             'model' => 'D-Star APJID2',
275             'class' => 'dstar',
276             },
277            
278             'APDPRS' => {
279             'vendor' => 'unknown',
280             'model' => 'D-Star APDPRS',
281             'class' => 'dstar',
282             },
283            
284             'APERXQ' => {
285             'vendor' => 'PE1RXQ',
286             'model' => 'PE1RXQ APRS Tracker',
287             'class' => 'tracker',
288             },
289            
290             'APNK01' => {
291             'vendor' => 'Kenwood',
292             'model' => 'TM-D700',
293             'class' => 'rig',
294             'messaging' => 1,
295             'version_regexp' => 1,
296             },
297             'APNK80' => {
298             'vendor' => 'Kantronics',
299             'model' => 'KAM',
300             'version' => '8.0',
301             },
302             'APNKMP' => {
303             'vendor' => 'Kantronics',
304             'model' => 'KAM+',
305             },
306             'APAGW' => {
307             'vendor' => 'SV2AGW',
308             'model' => 'AGWtracker',
309             'class' => 'software',
310             'os' => 'Windows',
311             },
312             'PSKAPR' => {
313             'vendor' => 'Open Source',
314             'model' => 'PSKmail',
315             'class' => 'software',
316             },
317             'APSK63' => {
318             'vendor' => 'Chris Moulding, G4HYG',
319             'model' => 'APRS Messenger',
320             'class' => 'software',
321             'os' => 'Windows',
322             },
323             'APSAR' => {
324             'vendor' => 'ZL4FOX',
325             'model' => 'SARTrack',
326             'class' => 'software',
327             'os' => 'Windows',
328             },
329             'APN102' => {
330             'vendor' => 'Gregg Wonderly, W5GGW',
331             'model' => 'APRSNow',
332             'class' => 'mobile',
333             'os' => 'ipad',
334             },
335             'APRNOW' => {
336             'vendor' => 'Gregg Wonderly, W5GGW',
337             'model' => 'APRSNow',
338             'class' => 'mobile',
339             'os' => 'ipad',
340             },
341             'APZ186' => {
342             'vendor' => 'IW3FQG',
343             'model' => 'UIdigi',
344             'class' => 'digi',
345             'version' => '186'
346             },
347             'APZ18' => {
348             'vendor' => 'IW3FQG',
349             'model' => 'UIdigi',
350             'class' => 'digi',
351             'version' => '18'
352             },
353             'APZ19' => {
354             'vendor' => 'IW3FQG',
355             'model' => 'UIdigi',
356             'class' => 'digi',
357             'version' => '19'
358             },
359             'APKRAM' => {
360             'vendor' => 'kramstuff.com',
361             'model' => 'Ham Tracker',
362             'class' => 'mobile',
363             'os' => 'ios',
364             },
365             'APK003' => {
366             'vendor' => 'Kenwood',
367             'model' => 'TH-D72',
368             'class' => 'ht',
369             },
370             'APMI01' => {
371             'vendor' => 'Microsat',
372             'os' => 'embedded',
373             'model' => 'WX3in1',
374             'version_regexp' => 1,
375             },
376             'APMI02' => {
377             'vendor' => 'Microsat',
378             'os' => 'embedded',
379             'model' => 'WXEth',
380             'version_regexp' => 1,
381             },
382             'APMI03' => {
383             'vendor' => 'Microsat',
384             'os' => 'embedded',
385             'model' => 'PLXDigi',
386             'version_regexp' => 1,
387             },
388             'APMI04' => {
389             'vendor' => 'Microsat',
390             'os' => 'embedded',
391             'model' => 'WX3in1 Mini',
392             'version_regexp' => 1,
393             },
394             'APMI05' => {
395             'vendor' => 'Microsat',
396             'os' => 'embedded',
397             'model' => 'PLXTracker',
398             'version_regexp' => 1,
399             },
400             );
401              
402             my @dstcall_regexps = (
403             [ 'APAM(\\d+)', {
404             'vendor' => 'Altus Metrum',
405             'model' => 'AltOS',
406             'class' => 'tracker',
407             'version_regexp' => 1,
408             } ],
409             [ 'APJI(\\d+)', {
410             'vendor' => 'Peter Loveall, AE5PL',
411             'model' => 'jAPRSIgate',
412             'class' => 'software',
413             'version_regexp' => 1,
414             } ],
415             [ 'APBPQ(\\d)', {
416             'vendor' => 'John Wiseman, G8BPQ',
417             'model' => 'BPQ32',
418             'class' => 'software',
419             'os' => 'Windows',
420             'version_regexp' => 1,
421             } ],
422             [ 'APD(\\d+)', {
423             'vendor' => 'Open Source',
424             'model' => 'aprsd',
425             'class' => 'software',
426             'os' => 'Linux/Unix',
427             'version_regexp' => 1,
428             } ],
429             [ 'APDG(\\d+)', {
430             'vendor' => 'Jonathan, G4KLX',
431             'model' => 'ircDDB Gateway',
432             'class' => 'dstar',
433             'version_regexp' => 1,
434             } ],
435             [ 'APDI(\\d+)', {
436             'vendor' => 'Bela, HA5DI',
437             'model' => 'DIXPRS',
438             'class' => 'software',
439             'version_regexp' => 1,
440             } ],
441             [ 'APDS(\\d+)', {
442             'vendor' => 'SP9UOB',
443             'model' => 'dsDIGI',
444             'os' => 'embedded',
445             'version_regexp' => 1,
446             } ],
447             [ 'APDST(\\d)', {
448             'vendor' => 'SP9UOB',
449             'model' => 'dsTracker',
450             'os' => 'embedded',
451             'version_regexp' => 1,
452             } ],
453             [ 'APDU(\\d+)', {
454             'vendor' => 'JA7UDE',
455             'model' => 'U2APRS',
456             'class' => 'mobile',
457             'os' => 'Android',
458             'version_regexp' => 1,
459             } ],
460             [ 'APDW(\\d+)', {
461             'vendor' => 'WB2OSZ',
462             'model' => 'DireWolf',
463             'version_regexp' => 1,
464             } ],
465             [ 'APMG(\\d+)', {
466             'vendor' => 'Alex, AB0TJ',
467             'model' => 'MiniGate',
468             'class' => 'software',
469             'os' => 'Netduino',
470             'version_regexp' => 1,
471             } ],
472             [ 'APJY(\\d+)', {
473             'vendor' => 'KA2DDO',
474             'model' => 'YAAC',
475             'class' => 'software',
476             'version_regexp' => 1,
477             } ],
478             [ 'AP4R(\\d+)', {
479             'vendor' => 'Open Source',
480             'model' => 'APRS4R',
481             'class' => 'software',
482             'version_regexp' => 1,
483             } ],
484             [ 'AP(\\d{3})D', {
485             'vendor' => 'Painter Engineering',
486             'model' => 'uSmartDigi D-Gate',
487             'class' => 'dstar',
488             'version_regexp' => 1,
489             } ],
490             [ 'AP(\\d{3})U', {
491             'vendor' => 'Painter Engineering',
492             'model' => 'uSmartDigi Digipeater',
493             'class' => 'digi',
494             'version_regexp' => 1,
495             } ],
496             [ 'APAF(\\d{2})', {
497             'model' => 'AFilter',
498             'version_regexp' => 1,
499             } ],
500             [ 'APAG(\\d{2})', {
501             'model' => 'AGate',
502             'version_regexp' => 1,
503             } ],
504             [ 'APAGW(\\d)', {
505             'vendor' => 'SV2AGW',
506             'model' => 'AGWtracker',
507             'class' => 'software',
508             'os' => 'Windows',
509             'version_regexp' => 1,
510             } ],
511             [ 'APAX(\\d{2})', {
512             'model' => 'AFilterX',
513             'version_regexp' => 1,
514             } ],
515             [ 'APAH(\\d{2})', {
516             'model' => 'AHub',
517             'version_regexp' => 1,
518             } ],
519             [ 'APAW(\\d{2})', {
520             'vendor' => 'SV2AGW',
521             'model' => 'AGWPE',
522             'class' => 'software',
523             'os' => 'Windows',
524             'version_regexp' => 1,
525             } ],
526             [ 'APBL(\\d{2})', {
527             'vendor' => 'BigRedBee',
528             'model' => 'BeeLine GPS',
529             'class' => 'tracker',
530             'version_regexp' => 1,
531             } ],
532             [ 'APC(\\d{3})', {
533             'vendor' => 'Rob Wittner, KZ5RW',
534             'model' => 'APRS/CE',
535             'class' => 'mobile',
536             'version_regexp' => 1,
537             } ],
538             [ 'APCL(\\d{2})', {
539             'vendor' => 'maprs.org',
540             'model' => 'maprs',
541             'class' => 'mobile',
542             'version_regexp' => 1,
543             } ],
544             [ 'APDT(\\d{2})', {
545             'vendor' => 'unknown',
546             'model' => 'APRStouch Tone (DTMF)',
547             'version_regexp' => 1,
548             } ],
549             [ 'APDF(\\d{2})', {
550             'model' => 'Automatic DF units',
551             'version_regexp' => 1,
552             } ],
553             [ 'APE(\\d{3})', {
554             'model' => 'Telemetry devices',
555             'version_regexp' => 1,
556             } ],
557             [ 'APFG(\\d{2})', {
558             'vendor' => 'KP4DJT',
559             'model' => 'Flood Gage',
560             'class' => 'software',
561             'version_regexp' => 1,
562             } ],
563             [ 'APGO(\\d{2})', {
564             'vendor' => 'AA3NJ',
565             'model' => 'APRS-Go',
566             'class' => 'mobile',
567             'version_regexp' => 1,
568             } ],
569            
570             [ 'APHAX(\\d)', {
571             'vendor' => 'PY2UEP',
572             'model' => 'SM2APRS SondeMonitor',
573             'class' => 'software',
574             'os' => 'Windows',
575             'version_regexp' => 1,
576             } ],
577            
578             [ 'APHH(\\d)', {
579             'vendor' => 'Steven D. Bragg, KA9MVA',
580             'model' => 'HamHud',
581             'class' => 'tracker',
582             'version_regexp' => 1,
583             } ],
584            
585             [ 'APHK(\\d{2})', {
586             'vendor' => 'LA1BR',
587             'model' => 'Digipeater/tracker',
588             'version_regexp' => 1,
589             } ],
590            
591             [ 'API(\\d{3})', {
592             'vendor' => 'Icom',
593             'model' => 'unknown',
594             'class' => 'dstar',
595             'version_regexp' => 1,
596             } ],
597             [ 'APIC(\\d{2})', {
598             'vendor' => 'HA9MCQ',
599             'model' => 'PICiGATE',
600             'version_regexp' => 1,
601             } ],
602            
603             [ 'APJA(\\d{2})', {
604             'vendor' => 'K4HG & AE5PL',
605             'model' => 'JavAPRS',
606             'version_regexp' => 1,
607             } ],
608             [ 'APJE(\\d{2})', {
609             'vendor' => 'Gregg Wonderly, W5GGW',
610             'model' => 'JeAPRS',
611             'version_regexp' => 1,
612             } ],
613             [ 'APJS(\\d{2})', {
614             'vendor' => 'Peter Loveall, AE5PL',
615             'model' => 'javAPRSSrvr',
616             'version_regexp' => 1,
617             } ],
618            
619             [ 'APK0(\\d{2})', {
620             'vendor' => 'Kenwood',
621             'model' => 'TH-D7',
622             'class' => 'ht',
623             'version_regexp' => 1,
624             } ],
625             [ 'APK1(\\d{2})', {
626             'vendor' => 'Kenwood',
627             'model' => 'TM-D700',
628             'class' => 'rig',
629             'version_regexp' => 1,
630             } ],
631            
632             [ 'APLM(..)', {
633             'vendor' => 'WA0TQG',
634             'class' => 'software',
635             'version_regexp' => 1,
636             } ],
637            
638             [ 'APAND(\\d)', {
639             'vendor' => 'Open Source',
640             'model' => 'APRSdroid',
641             'os' => 'Android',
642             'class' => 'mobile',
643             'version_regexp' => 1,
644             } ],
645             [ 'APDR(\\d{2})', {
646             'vendor' => 'Open Source',
647             'model' => 'APRSdroid',
648             'os' => 'Android',
649             'class' => 'mobile',
650             'version_regexp' => 1,
651             } ],
652            
653             [ 'APMI(..)', {
654             'vendor' => 'Microsat',
655             'os' => 'embedded',
656             'version_regexp' => 1,
657             } ],
658            
659             [ 'APN3(\\d{2})', {
660             'vendor' => 'Kantronics',
661             'model' => 'KPC-3',
662             'version_regexp' => 1,
663             } ],
664             [ 'APN9(\\d{2})', {
665             'vendor' => 'Kantronics',
666             'model' => 'KPC-9612',
667             'version_regexp' => 1,
668             } ],
669            
670             [ 'APND([0-9A-Z]{2})', {
671             'vendor' => 'PE1MEW',
672             'model' => 'DIGI_NED',
673             'version_regexp' => 1,
674             } ],
675            
676             [ 'APNM(\\d{2})', {
677             'vendor' => 'MFJ',
678             'model' => 'TNC',
679             'version_regexp' => 1,
680             } ],
681             [ 'APNP(\\d{2})', {
682             'vendor' => 'PacComm',
683             'model' => 'TNC',
684             'version_regexp' => 1,
685             } ],
686            
687             [ 'APNT(\\d{2})', {
688             'vendor' => 'SV2AGW',
689             'model' => 'TNT TNC as a digipeater',
690             'class' => 'digi',
691             'version_regexp' => 1,
692             } ],
693            
694             [ 'APNU([A-Z0-9]{2})', {
695             'vendor' => 'IW3FQG',
696             'model' => 'UIdigi',
697             'class' => 'digi',
698             'version_regexp' => 1,
699             } ],
700             [ 'APNU([0-9]{2}\\-[0-9])', {
701             'vendor' => 'IW3FQG',
702             'model' => 'UIdigi',
703             'class' => 'digi',
704             'version_regexp' => 1,
705             } ],
706            
707             [ 'APNW(\\d{2})', {
708             'vendor' => 'SQ3FYK',
709             'model' => 'WX3in1',
710             'os' => 'embedded',
711             'version_regexp' => 1,
712             } ],
713            
714             [ 'APNX(\\d{2})', {
715             'vendor' => 'K6DBG',
716             'model' => 'TNC-X',
717             'version_regexp' => 1,
718             } ],
719            
720             [ 'APOA(\\d{2})', {
721             'vendor' => 'OpenAPRS',
722             'model' => 'app',
723             'class' => 'mobile',
724             'os' => 'ios',
725             'version_regexp' => 1,
726             } ],
727             [ 'APOLU(.)', {
728             'vendor' => 'AMSAT-LU',
729             'model' => 'Oscar',
730             'class' => 'satellite',
731             'version_regexp' => 1,
732             } ],
733             [ 'APOT([A-Z0-9]{2})', {
734             'vendor' => 'Argent Data Systems',
735             'model' => 'OpenTracker',
736             'class' => 'tracker',
737             'version_regexp' => 1,
738             } ],
739            
740             [ 'APOZ([A-Z0-9]{2})', {
741             'vendor' => 'OZ1EKD, OZ7HVO',
742             'model' => 'KissOZ',
743             'class' => 'tracker',
744             'version_regexp' => 1,
745             } ],
746            
747             [ 'APPT([A-Z0-9]{2})', {
748             'vendor' => 'JF6LZE',
749             'model' => 'KetaiTracker',
750             'class' => 'tracker',
751             'version_regexp' => 1,
752             } ],
753            
754             [ 'APR(8\\d{2})', {
755             'vendor' => 'Bob Bruninga, WB4APR',
756             'model' => 'APRSdos',
757             'class' => 'software',
758             'version_regexp' => 1,
759             } ],
760            
761             [ 'APRRT(\d)', {
762             'vendor' => 'RPC Electronics',
763             'model' => 'RTrak',
764             'class' => 'tracker',
765             'version_regexp' => 1,
766             } ],
767            
768             [ 'APRHH(\d)', {
769             'vendor' => 'Steven D. Bragg, KA9MVA',
770             'model' => 'HamHud',
771             'class' => 'tracker',
772             'version_regexp' => 1,
773             } ],
774            
775             [ 'APRX([0-3].)', {
776             'vendor' => 'OH2MQK',
777             'model' => 'aprx',
778             'class' => 'software',
779             'version_regexp' => 1,
780             } ],
781             [ 'APRX([4-9].)', {
782             'vendor' => 'Bob Bruninga, WB4APR',
783             'model' => 'APRSmax',
784             'class' => 'software',
785             'version_regexp' => 1,
786             } ],
787             [ 'APS(\\d{3})', {
788             'vendor' => 'Brent Hildebrand, KH2Z',
789             'model' => 'APRS+SA',
790             'class' => 'software',
791             'version_regexp' => 1,
792             } ],
793            
794             [ 'APSC(\\d{2})', {
795             'vendor' => 'OH2MQK, OH7LZB',
796             'model' => 'aprsc',
797             'class' => 'software',
798             'version_regexp' => 1,
799             } ],
800            
801             [ 'APTT([0-9])', {
802             'vendor' => 'Byonics',
803             'model' => 'TinyTrak',
804             'class' => 'tracker',
805             'version_regexp' => 1,
806             } ],
807             [ 'APT2([0-9]{2})', {
808             'vendor' => 'Byonics',
809             'model' => 'TinyTrak2',
810             'class' => 'tracker',
811             'version_regexp' => 1,
812             } ],
813             [ 'APT3([0-9A-Z]{2})', {
814             'vendor' => 'Byonics',
815             'model' => 'TinyTrak3',
816             'class' => 'tracker',
817             'version_regexp' => 1,
818             } ],
819             [ 'APT4([0-9A-Z]{2})', {
820             'vendor' => 'Byonics',
821             'model' => 'TinyTrak4',
822             'class' => 'tracker',
823             'version_regexp' => 1,
824             } ],
825             [ 'APTW(\\d{2})', {
826             'vendor' => 'Byonics',
827             'model' => 'WXTrak',
828             'class' => 'wx',
829             'version_regexp' => 1,
830             } ],
831            
832             [ 'APTR(\\d{2})', {
833             'vendor' => 'Motorola',
834             'model' => 'MotoTRBO',
835             'version_regexp' => 1,
836             } ],
837            
838             [ 'APU(2\\d.)(.{0,1})', {
839             'vendor' => 'Roger Barker, G4IDE',
840             'model' => 'UI-View32',
841             'class' => 'software',
842             'os' => 'Windows',
843             'version_regexp' => 1,
844             } ],
845             [ 'APU(1\\d.)', {
846             'vendor' => 'Roger Barker, G4IDE',
847             'model' => 'UI-View16',
848             'class' => 'software',
849             'os' => 'Windows',
850             'version_regexp' => 1,
851             } ],
852            
853             [ 'APUDR(\\d)', {
854             'vendor' => 'NW Digital Radio',
855             'model' => 'UDR',
856             'version_regexp' => 1,
857             } ],
858            
859             [ 'APVR(\\d{2})', {
860             'vendor' => 'unknown',
861             'model' => 'IRLP',
862             'version_regexp' => 1,
863             } ],
864             [ 'APVE(\\d{2})', {
865             'vendor' => 'unknown',
866             'model' => 'EchoLink',
867             'version_regexp' => 1,
868             } ],
869            
870             [ 'APW(\\d{3})', {
871             'vendor' => 'Sproul Brothers',
872             'model' => 'WinAPRS',
873             'class' => 'software',
874             'os' => 'Windows',
875             'version_regexp' => 1,
876             } ],
877            
878             [ 'APWA(..)', {
879             'vendor' => 'KJ4ERJ',
880             'model' => 'APRSISCE',
881             'class' => 'software',
882             'os' => 'Android',
883             'version_regexp' => 1,
884             } ],
885             [ 'APWM(..)', {
886             'vendor' => 'KJ4ERJ',
887             'model' => 'APRSISCE',
888             'class' => 'software',
889             'os' => 'Windows Mobile',
890             'version_regexp' => 1,
891             } ],
892             [ 'APWW(\\d{2})', {
893             'vendor' => 'KJ4ERJ',
894             'model' => 'APRSIS32',
895             'class' => 'software',
896             'os' => 'Windows',
897             'version_regexp' => 1,
898             } ],
899            
900             [ 'APX(\\d{3})', {
901             'vendor' => 'Open Source',
902             'model' => 'Xastir',
903             'class' => 'software',
904             'os' => 'Linux/Unix',
905             'version_regexp' => 1,
906             } ],
907            
908             [ 'APXR(\\d{2})', {
909             'vendor' => 'G8PZT',
910             'model' => 'Xrouter',
911             'version_regexp' => 1,
912             } ],
913            
914             [ 'APZG(\\d{2})', {
915             'vendor' => 'OH2GVE',
916             'model' => 'aprsg',
917             'class' => 'software',
918             'os' => 'Linux/Unix',
919             'version_regexp' => 1,
920             } ],
921            
922             [ 'APRG(\\d{2})', {
923             'vendor' => 'OH2GVE',
924             'model' => 'aprsg',
925             'class' => 'software',
926             'os' => 'Linux/Unix',
927             'version_regexp' => 1,
928             } ],
929            
930             );
931              
932             my %regexp_prefix;
933              
934             #
935             # init code: compile the regular expressions to speed up matching
936             #
937              
938             sub _compile_regexps()
939             {
940 3     3   437 for (my $i = 0; $i <= $#dstcall_regexps; $i++) {
941 252         782 my $dmatch = $dstcall_regexps[$i];
942 252         1690 my($regexp, $response) = @$dmatch;
943            
944 252         6907 my $compiled = qr/^$regexp$/;
945 252         2246 $dstcall_regexps[$i] = [ $regexp, $response, $compiled ];
946             }
947             }
948              
949             #
950             # init: optimize regexps with an initial hash lookup
951             #
952              
953             sub _optimize_regexps()
954             {
955 3     3   8 my @left;
956            
957 3         27 for (my $i = 0; $i <= $#dstcall_regexps; $i++) {
958 252         318 my $dmatch = $dstcall_regexps[$i];
959 252         417 my($regexp, $response, $compiled) = @$dmatch;
960            
961 252 50       5262 if ($regexp =~ /^([^\(]{2,5})(\(.*)$/) {
962 252 100       558 if (!defined $regexp_prefix{$1} ) {
963 240         1333 $regexp_prefix{$1} = [ $dmatch ];
964             } else {
965 12         15 push @{ $regexp_prefix{$1} }, $dmatch;
  12         92  
966             }
967             } else {
968 0         0 push @left, $dmatch;
969 0         0 warn "optimize: leaving $regexp over\n";
970             }
971             }
972            
973 3         20 @dstcall_regexps = @left;
974             }
975              
976             _compile_regexps();
977             _optimize_regexps();
978              
979             =over
980              
981             =item identify($hashref)
982              
983             Tries to identify the device.
984              
985             =back
986              
987             =cut
988              
989             sub identify($)
990             {
991 21     21 1 45689 my($p) = @_;
992            
993 21         58 $p->{'deviceid_resultcode'} = '';
994            
995 21 50       89 return _a_err($p, 'no_format') if (!defined $p->{'format'});
996 21 50       63 return _a_err($p, 'no_dstcall') if (!defined $p->{'dstcallsign'});
997            
998 21 100       69 if ($p->{'format'} eq 'mice') {
999             #warn Dumper($p);
1000 17         21 my $resp;
1001             #warn "comment: " . $p->{'comment'} . "\n";
1002 17 50       60 if (!defined $p->{'comment'}) {
1003 0         0 return _a_err($p, 'mice_no_comment');
1004             }
1005 17 100       248 if ($p->{'comment'} =~ s/^>(.*)=$/$1/) {
    100          
    100          
    100          
    100          
    100          
    100          
    100          
    50          
    0          
    0          
    0          
1006 2         5 $resp = 'd72';
1007             } elsif ($p->{'comment'} =~ s/^>//) {
1008 2         3 $resp = 'd7';
1009             } elsif ($p->{'comment'} =~ s/^\](.*)=$/$1/) {
1010 1         3 $resp = 'd710';
1011             } elsif ($p->{'comment'} =~ s/^\]//) {
1012 2         5 $resp = 'd700';
1013             } elsif ($p->{'comment'} =~ s/^`(.*)_\s*$/$1/) {
1014 3         8 $resp = 'vx8';
1015             } elsif ($p->{'comment'} =~ s/^`(.*)_"$/$1/) {
1016 2         3 $resp = 'ftm350';
1017             } elsif ($p->{'comment'} =~ s/^`(.*)_#$/$1/) {
1018 2         4 $resp = 'vx8g';
1019             } elsif ($p->{'comment'} =~ s/^\'(.*)\|3$/$1/) {
1020 1         3 $resp = 'tt3';
1021             } elsif ($p->{'comment'} =~ s/^\'(.*)\|4$/$1/) {
1022 2         5 $resp = 'tt4';
1023             } elsif ($p->{'comment'} =~ s/^[`\'](.*)\*[0-9]$/$1/) {
1024 0         0 $resp = 'kissoz';
1025             } elsif ($p->{'comment'} =~ s/^[`\'](.*)^.$/$1/) {
1026 0         0 $resp = 'anyfrog';
1027             } elsif ($p->{'comment'} =~ s/^[`\'](.*)~.$/$1/) {
1028 0         0 $resp = 'unknown';
1029             }
1030 17 50       44 if ($resp) {
1031 17         203 $p->{'deviceid'} = $response{$resp};
1032 17         61 return 1;
1033             }
1034 0         0 return _a_err($p, 'mice_no_deviceid');
1035             }
1036            
1037 4 100       15 if (defined $fixed_dstcalls{$p->{'dstcallsign'}}) {
1038 2         19 $p->{'deviceid'} = $fixed_dstcalls{$p->{'dstcallsign'}};
1039 2         7 return 1;
1040             }
1041            
1042 2         6 foreach my $len (4, 3, 5, 2) {
1043 5         9 my $prefix = substr($p->{'dstcallsign'}, 0, $len);
1044 5 100       18 if (defined $regexp_prefix{$prefix}) {
1045 2         3 foreach my $dmatch (@{ $regexp_prefix{$prefix} }) {
  2         6  
1046 3         8 my($regexp, $response, $compiled) = @$dmatch;
1047             #warn "trying '$regexp' against " . $p->{'dstcallsign'} . "\n";
1048 3 100       25 if ($p->{'dstcallsign'} =~ $compiled) {
1049             #warn "match!\n";
1050 2         3 my %copy = %{ $response };
  2         13  
1051 2         6 $p->{'deviceid'} = \%copy;
1052            
1053 2 50       6 if ($response->{'version_regexp'}) {
1054             #warn "version_regexp set: $1 from " . $p->{'dstcallsign'} . " using " . $regexp . "\n";
1055 2         6 $p->{'deviceid'}->{'version'} = $1;
1056 2         7 delete $p->{'deviceid'}->{'version_regexp'};
1057             }
1058            
1059 2         7 return 1;
1060             }
1061             }
1062             }
1063             }
1064            
1065 0           return _a_err($p, 'no_id');
1066             }
1067              
1068              
1069             1;
1070             __END__