# ## Copyright (c) 1995-2020 University Corporation for Atmospheric Research ## All rights reserved # my $pkgdoc = <<'EOD'; #/**---------------------------------------------------------------------- # @file BJParser.pm # Routines for processing BlackJack type data from multiple missions. # # @author Doug Hunt # @since 10/27/2008 # @version $URL: svn://svn1.cosmic.ucar.edu/trunk/src/BJutils/BJParser.pm $ $Id: BJParser.pm 26016 2020-05-21 17:44:41Z dhunt $ # cdaacTask no # -----------------------------------------------------------------------*/ EOD package BJParser; require 5.010_000; # Needs features from perl version 5.10 use feature "switch"; use strict; use warnings; use lib qw(.); use BJTools; use TimeClass; use PDL; use Carp; use BJConstants; # ## Globals # # hash defining lengths of 'pack' format characters %BJParser::pack_length = ('N' => 4, 'C' => 1, 'n' => 2, 'd' => 8, 'f' => 4, 'S' => 2, 'L' => 4, 'c' => 1, 's' => 2, ' ' => 0, 'l' => 4, 'Q' => 8); #/**---------------------------------------------------------------------- # @sub new # # Create a new BJParser object # # @parameter $type - Class name ('BJParser' or 'GraceParser', or ...) # @ $opt - {BYDATE => 1} -> create output files labelled by the date of the contents # @ {TMPDIR => $tmpdir (tempdir object) # @ START_TIME => do not process packets earlier than this time # @ END_TIME => do not process packets later than this time # @ SKIP_SEEN => hashref of already processed packets: # @ $$seen{OBSDqfit}{$time}{$prn}{$antin} # @ $$seen{NAVGants}{$time} # @ TIMECHECK => 1, implies just parsing the file to find start and end times # @ HRTYPE => 2 = closed loop, 1 = open loop or 3 = L2C open loop # @ The choice of HRTYPE tells which format opnGps file to output # @ P2_FAZ_PREC=> Default = 2 (double precision). If set to 1 (single precision) # @ Then read in P2 PHASE values in single precision # @exception Will toss an exception in case of real trouble. # ----------------------------------------------------------------------*/ sub new { my $type = shift; my $opt = shift || {}; my $tmpdir = $opt->{TMPDIR} // die "no TMPDIR specified!"; my %self = (%$opt); $self{TMPDIR} = $tmpdir; return bless \%self, $type; } #/**---------------------------------------------------------------------- # @sub parseLv0 # # Routine which takes as input one binary data file and outputs: # # 1) A hash of NAV solution records by time # 2) A hash of low rate data records by time and PRN # 3) A list of high rate data files in the input tmpdir # # Should handle little/big endian issues correctly. Only tested # on a little endian system. # # @parameter $self - BJParser object or subclass # @ $infile - Full path of input level 0 file # @exception Will toss an exception in case of real trouble. # ----------------------------------------------------------------------*/ sub parseLv0 { my $self = shift; my $infile = shift; my $leoid = shift; #Jun add leoid #print "Parsing $infile...\n"; #print $leoid,"\n"; #die 'stop here'; my $filetext = (ref($infile) eq 'SCALAR') ? $$infile : do { local( @ARGV, $/ ) = $infile; <> } ; # slurp! # Input hash of packet times already processed my $seen = defined ($self->{SKIP_SEEN}) ? $self->{SKIP_SEEN} : ''; # Do not process packets earlier than this time, if specified my $start_time = defined ($self->{START_TIME}) ? $self->{START_TIME} : 0; # Do not process packets later than this time, if specified my $end_time = defined ($self->{END_TIME}) ? $self->{END_TIME} : 9e99; my $pkt_delim = pack ("C", 0x02); my @packets = split /$pkt_delim/, $filetext; my $total = scalar(@packets); # Escape sequences for BlackJack data: 0x10,0x4F and 0x10,0x45 my $twoEsc = pack ("CC", 0x10,0x4F); my $two = pack ("C", 0x02); my $tenEsc = pack ("CC", 0x10,0x45); my $ten = pack ("C", 0x10); my $cnt = 0; my $offset = 0; my $timecheck = $self->{TIMECHECK}; # check times only, do not parse. $self->{FIRSTTIME} = 9.e99; $self->{LASTTIME} = 0; PACKET: while (1) { my $pkt = shift @packets; last PACKET if (!defined($pkt)); $cnt++; $offset += length($pkt) + 1; # add one for 0x02 delimeter # Get rid of blackjack escape sequences designed to protect 0x02 (the delimiter byte) $pkt =~ s/$twoEsc/$two/gsm; # un-escape $pkt =~ s/$tenEsc/$ten/gsm; my $len = length($pkt); next PACKET if (!$len); my ($flag, $hdrlen, $datalen1, $id, $idst, $datalen2) = unpack "(CCSCCS)>", $pkt; next PACKET if (!defined($idst)); # Added to help SAC-C parsing. D. Hunt 4/12/2010 if (!BJTools::checkCRC ($pkt, $len)) { # Sometimes the 0x02 delimiter gets messed up, so two packets are processed together. # If the CRC fails with the whole packet, try again using the header data length # plus 6 (for the header and CRC). If this passes, then trim the garbage off the end # and go on. # # Added ($datalen1+6 <= $len) check before calling checkCRC to prevent segfaults # D. Hunt, 4/15/2010 if ( ($datalen1+6 <= $len) && BJTools::checkCRC ($pkt, $datalen1+6)) { $self->{EXTRA_DATA_SKIPPED}{$infile}++; $pkt = substr ($pkt, 0, $datalen1+6); # chop off remaining garbage } else { $self->{BADCRC}{$infile}++; next PACKET; } } substr ($pkt, 0, 8, ''); # get rid of 8 byte header unpacked above substr ($pkt, -2, 2, ''); # get rid of 2 byte CRC from end of packet my $pkttype = ($id == 0xBB && $idst == 0xBD) ? substr ($pkt, 0, 8, '') : 'Turbobin'; $self->{PACKETS}{$infile}{$pkttype}++; # for reporting after parse my $pktlen = length($pkt); $self->{PACKETSIZE}{$infile}{$pkttype}{$pktlen}++; # for reporting after parse # skip duplicates, if requested to do so. my ($time, $prn, $antin); if ($pkttype =~ /^(OBSDqfit|NAVGants|OBSDq12a|OBSDq12b|OBSDq12c|OBSDq12d|OBSDq13a|OCCDq12a|OCCDq12b|OCCDq13a|SCNThrat|OBSDq13a)$/) { ($time) = unpack ("N", $pkt); # Guard against bogus times. Do not use this packet if LASTTIME has been set # and it is more than a week away from the current greatest time in the file. # D. Hunt 5/7/2010 # Commented out 2/1/2017 by D. Hunt--this breaks the v4.1 data test (test 22) #next PACKET if ($self->{LASTTIME} && abs($time - $self->{LASTTIME}) > 86400*7); # Further check for bogus times. Do not use this packet if the user passes # in an EXPECTED_TIME and the current time is more than a week away from this. # D. Hunt 12/1/2011 next PACKET if ($self->{EXPECTED_TIME} && abs($time - $self->{EXPECTED_TIME}) > 86400*7); # Bail out if times out of requested range. Do this before setting FIRSTTIME and LASTTIME # D. Hunt 11/01/2010 next PACKET if ($time < $start_time); # packet too early next PACKET if ($time > $end_time); # packet too late my $min_rec_time = 10*86400*365; # The Trig receiver spits out times near zero when first warming up. # Ignore times earlier than around 1990. D. Hunt 9/8/2017 $self->{FIRSTTIME} = $time if ($time < $self->{FIRSTTIME} && $time > $min_rec_time); $self->{LASTTIME} = $time if ($time > $self->{LASTTIME}); $self->{PACKETTIMES}{$pkttype}{$time}++; } next PACKET if ($timecheck); # print "Processing packet type $pkttype\n"; if ($pkttype eq 'OBSDqfit') { ($time, $prn, $antin) = unpack ("NCC", $pkt); next PACKET if ($seen && $$seen{OBSDqfit}{$time}{$prn}{$antin}++); $self->parse_OBSDqfit($pkt); } elsif ($pkttype eq 'OBSDq12a') { ($time, $prn, $antin) = unpack ("NCC", $pkt); next PACKET if ($seen && $$seen{OBSDq12a}{$time}{$prn}{$antin}++); $self->parse_OBSDq12a($pkt); } elsif ($pkttype eq 'OBSDq12b') { ($time, $prn, $antin) = unpack ("NCC", $pkt); next PACKET if ($seen && $$seen{OBSDq12b}{$time}{$prn}{$antin}++); $self->parse_OBSDq12a($pkt); # Use the same routine to parse q12a and q12b packets } elsif ($pkttype eq 'OBSDq12c') { ($time, $prn, $antin) = unpack ("NCC", $pkt); next PACKET if ($seen && $$seen{OBSDq12c}{$time}{$prn}{$antin}++); $self->parse_OBSDq12c($pkt); } elsif ($pkttype eq 'OBSDq12d') { ($time, $prn, $antin) = unpack ("NCC", $pkt); next PACKET if ($seen && $$seen{OBSDq12d}{$time}{$prn}{$antin}++); $self->parse_OBSDq12c($pkt); # Use the same routine to parse q12c and q12d packets } elsif ($pkttype eq 'OCCDq12a') { ($time, $prn, $antin) = unpack ("NCC", $pkt); next PACKET if ($seen && $$seen{OCCDq12a}{$time}{$prn}{$antin}++); $self->parse_OCCDq12a($pkt); } elsif ($pkttype eq 'OCCDq12b') { ($time, $prn, $antin) = unpack ("NCC", $pkt); next PACKET if ($seen && $$seen{OCCDq12b}{$time}{$prn}{$antin}++); $self->parse_OCCDq12b($pkt,$leoid); #Jun add leoid } elsif ($pkttype eq 'OBSDq13a') { ($time, $prn, $antin) = unpack ("NCC", $pkt); next PACKET if ($seen && $$seen{OBSDq13a}{$time}{$prn}{$antin}++); $self->parse_OBSDq12a($pkt); } elsif ($pkttype eq 'NAVGants') { ($time) = unpack ("N", $pkt); next PACKET if ($seen && $$seen{NAVGants}{$time}++); $self->parse_NAVGants($pkt); } elsif ($pkttype eq 'SCNThrat') { ($time) = unpack ("N", $pkt); next PACKET if ($seen && $$seen{SCNThrat}{$time}++); $self->parse_SCNThrat($pkt); } elsif ($pkttype eq 'RCVMadct') { ($time) = unpack ("N", $pkt); next PACKET if ($seen && $$seen{RCVMadct}{$time}++); $self->parse_RCVMadct($pkt); } elsif ($pkttype eq 'OCCTopar') { $self->parse_OCCTopar($pkt); } elsif ($pkttype eq 'CONFantc') { $self->parse_CONFantc($pkt); } elsif ($pkttype eq 'RCVMfdir') { $self->parse_RCVMfdir($pkt); } elsif ($pkttype eq 'RCVMlogm') { $self->parse_RCVMlogm($pkt); } elsif ($pkttype eq 'RCVMcmdr') { $self->parse_RCVMcmdr($pkt); } elsif ($pkttype eq 'TIMEppst') { $self->parse_TIMEppst($pkt); } else { print "Unknown packet type: $pkttype\n" if ($self->{VERBOSE}); 1; # stopping point in debugger for packets not handled } } my $badcrc = $self->{BADCRC}{$infile} // 0; #print "$infile: Bad CRCs found: $badcrc, total packets: $total\n"; } #/**---------------------------------------------------------------------- # @sub timerange # # Return the min and max times for the file(s) processed # # @parameter $self -- BJParser object # @return $mintime, $maxtime -- min and max times in GPS seconds # @exception Will toss an exception in case of real trouble. # ----------------------------------------------------------------------*/ sub timerange { my $infile = shift; my $parser = BJParser->new({TMPDIR => 'memory', TIMECHECK => 1}); $parser->parseLv0($infile); if (defined($parser->{LASTTIME} && defined($parser->{FIRSTTIME}))) { return ($parser->{FIRSTTIME}, $parser->{LASTTIME}); } else { return (0, 0); } } #/**---------------------------------------------------------------------- # @sub finish # # Clean up the parser object. For the moment, just removes files from $$tmpdir # associated with GOX parsing # # @parameter $self -- BJParser object # @return nothing # @exception Will toss an exception in case of real trouble. # ----------------------------------------------------------------------*/ sub finish { my $self = shift; my $tmpdir = $self->{TMPDIR}; if (defined($tmpdir) && $tmpdir ne 'memory') { my @names = qw(opar.txt antc.txt fdir.txt logm.txt scin.txt hirate.bin* lorate.txt); unlink glob map {"$$tmpdir/$_"} @names; } } #/**---------------------------------------------------------------------- # @sub parseStrip # # Routine to carve a set of fields off of the front of a binary record # and return the fields decoded. # # Input is the format string to pull off the front of the input packet, # and then a reference to the packet itself. The format string uses # the convention of the 'pack' perl builtin. # # @parameter $fmt -- Format string of letters defined for 'pack' # @ $pkt -- A reference to the perl string containing the data. # @ The binary fields returned are chopped off of the front of this # @ string. # @return A list of values from $pkt according to $fmt # @exception Will toss an exception in case of real trouble. # ----------------------------------------------------------------------*/ sub parseStrip { my $fmt = shift; my $pkt = shift; my $len; if (exists ($::lenCache{$fmt})) { $len = $::lenCache{$fmt}; } else { $len = $::lenCache{$fmt} = findLen($fmt); } # This depends upon perl 5.10! return unpack "($fmt)>", substr($$pkt, 0, $len, ''); # all BJ data are big endian (>) } #/**---------------------------------------------------------------------- # @sub findLen # # Find the length in bytes of the values represented by the input 'pack' # format string. Only those letters defined in the global %BJParser::pack_length # are considered. For example, 'CCSdd' would be 20 bytes long, since # C = char = 1 byte, S = short = 2 bytes and d = double = 8 bytes. # # @parameter $str -- Format string to count the length of # @ $pkt -- A reference to the perl string containing the data. # @ The binary fields returned are chopped off of the front of this # @ string. # @return A list of values from $pkt according to $fmt # @exception Will toss an exception in case of real trouble. # ----------------------------------------------------------------------*/ sub findLen { my $str = shift; my $n = 0; foreach my $b (split '', $str) { $n += ($BJParser::pack_length{$b} || 0); } return $n; } #/**---------------------------------------------------------------------- # @sub parse_NAVGants # # Subroutine to parse navigation records # # Input is the packet, with 'NAVGants' removed and the CRC removed # (this routine is only called if the CRC is good). # # Output is a set of ASCII files in $$tmpdir (global) with low rate # data records per antenna. Also output is high rate data, per antenna, # in binary files ready for conversion into opnGps format. # # @parameter $pkt -- Contents of NAVGants packet (without 'NAVGants') # @ as a perl string # @return $$tmpdir/navg.txt # @exception Will toss an exception in case of real trouble. # ----------------------------------------------------------------------*/ sub parse_NAVGants { my $self = shift; my $pkt = shift; my $time = parseStrip ("L", \$pkt); # 4 my $date = defined($self->{BYDATE}) ? '.'.TimeClass->new->set_gps($time)->get_yrdoy_gps : ''; my $tmpdir = $self->{TMPDIR}; my ($CHISquared, $CoVarPrecMult, $RawSteeringVoltage, @x, @dx, @v, @dv); ($CHISquared, $CoVarPrecMult, $RawSteeringVoltage, @x[0..3], @dx[0..3], @v[0..3], @dv[0..3]) = parseStrip ("ffs dddd ffff dddd ffff", \$pkt); # 106 my ($prnstr) = unpack ("Z*", $pkt); # Use Z unpack code to get a null-terminated string. D. Hunt 10/05/2009 my @SolutionPRNs = unpack "C" x length($prnstr), $prnstr; my $nprns = scalar(@SolutionPRNs); substr ($pkt, 0, $nprns+1, ''); # get rid of SolutionPRNs string plus NULL byte my (@SolutionAzs, @SolutionEls); (@SolutionAzs[0..$nprns-1], @SolutionEls[0..$nprns-1]) = parseStrip ("f" x ($nprns*2), \$pkt); # 4*$nprns*2 my $outrec = sprintf ("%ld NAVG %.6f %.6f %.6f %.3e %.8f %.8f %.8f %.3e %.3e %.3e %d", $time, $x[0]/1000.0, $x[1]/1000.0, $x[2]/1000.0, $x[3], $v[0]/1000.0, $v[1]/1000.0, $v[2]/1000.0, $v[3], $CHISquared, $CoVarPrecMult, $RawSteeringVoltage); for (my $i=0;$i<$nprns;$i++) { $outrec .= sprintf " PRN%02d %.3f %.3f", $SolutionPRNs[$i], $SolutionAzs[$i], $SolutionEls[$i]; } $outrec .= "\n"; my $file = "navg.txt$date"; # Support writes to memory instead of an output file if ($tmpdir eq 'memory') { $self->{OUTFILES}{$file} .= $outrec; } else { open my $fh, '>>', "$$tmpdir/$file" or croak "Cannot open $file for appending"; printf {$fh} $outrec; close $fh; } } #/**---------------------------------------------------------------------- # @sub parse_OCCTopar # # Subroutine to parse occultation table records # # Input is the packet, with 'OCCTopar' removed and the CRC removed # (this routine is only called if the CRC is good). # # @parameter $pkt -- Contents of OCCTopar packet (without 'OCCTopar') # @ as a perl string # @return $$tmpdir/opar.txt # @exception Will toss an exception in case of real trouble. # ----------------------------------------------------------------------*/ sub parse_OCCTopar { my $self = shift; my $pkt = shift; my $tmpdir = $self->{TMPDIR}; # parse packet, return file $$tmpdir/opar.txt or $self->{OUTFILES}{"opar.txt"} my $Antenna = unpack ("Z*", $pkt); substr ($pkt, 0, length($Antenna)+1, ''); # get rid of $Antenna plus null byte my ($RegionBottom, $RegionTop, $DDBottomLimit, $RegionAz, $Enable, $CARate, $P1Rate, $P2Rate) = parseStrip ("ddd f cccc", \$pkt); my $Name = unpack ("Z*", $pkt); my $outrec = sprintf (" opar> --------------------------------------------------------------------\n") . sprintf (" opar> Name = %s, Antenna = %s\n", $Name, $Antenna) . sprintf (" opar> RegionBottom = %f, RegionTop = %f, DDBottomLimit = %f, RegionAz = %f\n", $RegionBottom, $RegionTop, $DDBottomLimit, $RegionAz) . sprintf (" opar> Enable = %u, CARate = %d, P1Rate = %d, P2Rate = %d\n", $Enable, $CARate, $P1Rate, $P2Rate); my $file = 'opar.txt'; # Support writes to memory instead of an output file if ($tmpdir eq 'memory') { $self->{OUTFILES}{$file} .= $outrec; } else { open my $fh, '>>', "$$tmpdir/$file" or croak "Cannot open $file for appending"; printf {$fh} $outrec; close $fh; } } #/**---------------------------------------------------------------------- # @sub parse_CONFantc # # Subroutine to parse occultation table records # # Input is the packet, with 'CONFantc' removed and the CRC removed # (this routine is only called if the CRC is good). # # @parameter $pkt -- Contents of CONFantc packet (without 'CONFantc') # @ as a perl string # @return $$tmpdir/antc.txt # @exception Will toss an exception in case of real trouble. # ----------------------------------------------------------------------*/ sub parse_CONFantc { my $self = shift; my $pkt = shift; my $tmpdir = $self->{TMPDIR}; # parse packet, return file $$tmpdir/antc.txt or $self->{OUTFILES}{"antc.txt"} my ($XBoreCoeff, $YBoreCoeff, $ZBoreCoeff, $Enable, $PODDefault, $OCCDefault, $AntennaInput) = parseStrip ("fff cccc", \$pkt); my $Name = unpack ("Z*", $pkt); my $outrec = sprintf (" antc> --------------------------------------------------------------------\n") . sprintf (" antc> Name = %s\n", $Name) . sprintf (" antc> XBoreCoeff = %f, YBoreCoeff = %f, ZBoreCoeff = %f\n", $XBoreCoeff, $YBoreCoeff, $ZBoreCoeff) . sprintf (" antc> Enable = %u, PODDefault = %u, OCCDefault = %u, AntennaInput = %d\n", $Enable, $PODDefault, $OCCDefault, $AntennaInput); my $file = 'antc.txt'; # Support writes to memory instead of an output file if ($tmpdir eq 'memory') { $self->{OUTFILES}{$file} .= $outrec; } else { open my $fh, '>>', "$$tmpdir/$file" or croak "Cannot open $file for appending"; printf {$fh} $outrec; close $fh; } } #/**---------------------------------------------------------------------- # @sub parse_RCVMfdir # # Subroutine to parse occultation table records # # Input is the packet, with 'RCVMfdir' removed and the CRC removed # (this routine is only called if the CRC is good). # # @parameter $pkt -- Contents of RCVMfdir packet (without 'RCVMfdir') # @ as a perl string # @return $$tmpdir/fdir.txt # @exception Will toss an exception in case of real trouble. # ----------------------------------------------------------------------*/ sub parse_RCVMfdir { my $self = shift; my $pkt = shift; my $tmpdir = $self->{TMPDIR}; # parse packet, return file $$tmpdir/fdir.txt or $self->{OUTFILES}{"fdir.txt"} my $Name = unpack ("Z*", $pkt); substr ($pkt, 0, length($Name)+1, ''); my ($Sector, $Size) = parseStrip ("cL", \$pkt); my $Type = unpack ("a4", $pkt); substr ($pkt, 0, 4, ''); my $outrec = sprintf (" fdir> --------------------------------------------------------------------\n"). sprintf (" fdir> File name = %s, Sector = %d, Size = %d, Type = %s\n", $Name, $Sector, $Size, $Type); if ($Type eq 'Joy!') { my ($PEFCV, $PEFOD, $PEFOI, $LoadType, $PEFTime) = parseStrip ("LLLCL", \$pkt); $outrec .= sprintf (" fdir> PEFCV = %u, PEFOD = %u, PEFOI = %u, LoadType = %d, PEFTime = %u\n", $PEFCV, $PEFOD, $PEFOI, $LoadType, $PEFTime); } elsif ($Type eq 'BJbt') { my $BootCodeVersion = unpack ("Z*", $pkt); $outrec .= sprintf (" fdir> BootCodeVersion = %s\n", $BootCodeVersion); } my $file = 'fdir.txt'; # Support writes to memory instead of an output file if ($tmpdir eq 'memory') { $self->{OUTFILES}{$file} .= $outrec; } else { open my $fh, '>>', "$$tmpdir/$file" or croak "Cannot open $file for appending"; printf {$fh} $outrec; close $fh; } } #/**---------------------------------------------------------------------- # @sub parse_RCVMlogm # # Subroutine to parse log records # # Input is the packet, with 'RCVMlogm' removed and the CRC removed # (this routine is only called if the CRC is good). # # @parameter $pkt -- Contents of RCVMlogm packet (without 'RCVMlogm') # @ as a perl string # @return $$tmpdir/navg.txt # @exception Will toss an exception in case of real trouble. # ----------------------------------------------------------------------*/ sub parse_RCVMlogm { my $self = shift; my $pkt = shift; my $time = $self->{LASTTIME}; my $date = defined($self->{BYDATE}) ? '.'.TimeClass->new->set_gps($time)->get_yrdoy_gps : ''; my $tmpdir = $self->{TMPDIR}; $pkt =~ s/\x00$//; # get rid of trailing null # Append " log> " to all lines in the log message my $outrec = join "", map { " log> $_\n" } split (/\n/, $pkt); my $file = "logm.txt$date"; if ($tmpdir eq 'memory') { $self->{OUTFILES}{$file} .= $outrec; } else { open my $fh, '>>', "$$tmpdir/$file" or croak "Cannot open $file for appending"; print {$fh} $outrec; close $fh; } } #/**---------------------------------------------------------------------- # @sub parse_RCVMcmdr # # Subroutine to parse command response records # # Input is the packet, with 'RCVMcmdr' removed and the CRC removed # (this routine is only called if the CRC is good). # # @parameter $pkt -- Contents of RCVMlogm packet (without 'RCVMlogm') # @ as a perl string # @return $$tmpdir/cmdr.txt (or in memory in $self->{OUTFILES}{cmdr.txt}) # @exception Will toss an exception in case of real trouble. # ----------------------------------------------------------------------*/ sub parse_RCVMcmdr { my $self = shift; my $pkt = shift; my $time = $self->{LASTTIME}; my $date = defined($self->{BYDATE}) ? '.'.TimeClass->new->set_gps($time)->get_yrdoy_gps : ''; my $tmpdir = $self->{TMPDIR}; # parse packet, return file $$tmpdir/cmdr.txt or $self->{OUTFILES}{"cmdr.txt"} my ($ack, $libID, $cmdCode, $statusCode) = parseStrip ("CLLL", \$pkt); my $Message = unpack ("Z*", $pkt); my $outrec = sprintf (" cmdr> --------------------------------------------------------------------\n") . sprintf (" cmdr> ACK = %d, Lib ID = %s, Cmd code = %s, Status code = %u\n", $ack, $libID, $cmdCode, $statusCode) . sprintf (" cmdr> %s\n", $Message); my $file = 'cmdr.txt'; if ($tmpdir eq 'memory') { $self->{OUTFILES}{$file} .= $outrec; } else { open my $fh, '>>', "$$tmpdir/$file" or croak "Cannot open $file for appending"; print {$fh} $outrec; close $fh; } } #/**---------------------------------------------------------------------- # @sub parse_TIMEppst # # Subroutine to parse TIMEppst packets # # Input is the packet, with 'TIMEppst' removed and the CRC removed # (this routine is only called if the CRC is good). # # @parameter $pkt -- Contents of TIMEppst packet (without 'TIMEppst') # @ as a perl string # @return $$tmpdir/ppst.txt (or in memory in $self->{OUTFILES}{ppst.txt}) # @exception Will toss an exception in case of real trouble. # ----------------------------------------------------------------------*/ sub parse_TIMEppst { my $self = shift; my $pkt = shift; my $tmpdir = $self->{TMPDIR}; # parse packet, return file $$tmpdir/ppst.txt or $self->{OUTFILES}{"ppst.txt"} my ($ppstime) = parseStrip ("L", \$pkt); my $outrec = " ppst> $ppstime\n"; my $file = "ppst.txt"; if ($tmpdir eq 'memory') { $self->{OUTFILES}{$file} .= $outrec; } else { open my $fh, '>>', "$$tmpdir/$file" or croak "Cannot open $file for appending"; print {$fh} $outrec; close $fh; } } #/**---------------------------------------------------------------------- # @sub report # # Given a report of problems found during parsing. Currently # only shows bad CRCs and total packets per type. # # @parameter $self - parser object # @output Report to STDOUT # @exception Will toss an exception in case of real trouble. # ----------------------------------------------------------------------*/ sub report { my $self = shift; my $version; my $first_stamp = TimeClass->new->set_gps($self->{FIRSTTIME})->get_stamp_gps; my $last_stamp = TimeClass->new->set_gps($self->{LASTTIME})->get_stamp_gps; my $duration = ($self->{LASTTIME} - $self->{FIRSTTIME})/60; printf "Data from $first_stamp to $last_stamp (%5.2f minutes)\n", $duration; # $self->{PACKETS}{$infile}{$pkttype} foreach my $infile (sort keys %{$self->{PACKETS}}) { my $badcrc = defined($self->{BADCRC}{$infile}) ? $self->{BADCRC}{$infile} : 0; my $mangled_delimiter = defined($self->{EXTRA_DATA_SKIPPED}{$infile}) ? $self->{EXTRA_DATA_SKIPPED}{$infile} : 0; print "$infile:\n" unless ($infile =~ /^SCALAR/); # do not print if a file name not stored here foreach my $pkttype (sort keys %{$self->{PACKETS}{$infile}}) { $version = 'v434+' if ($pkttype =~ /OBSDq12c/); printf "%15s: total %6d\n", $pkttype, $self->{PACKETS}{$infile}{$pkttype}; my $tot_bytes = 0; foreach my $len (sort keys %{$self->{PACKETSIZE}{$infile}{$pkttype}}) { my $count = $self->{PACKETSIZE}{$infile}{$pkttype}{$len}; my $bytes = $count * $len; printf "%10s len = %3d, count = %5d, bytes = %8d\n", '', $len, $count, $bytes; $tot_bytes += $bytes; } printf "%10s total = %8d bytes\n", '', $tot_bytes; } printf "%15s: %6d\n", "Bad CRC", $badcrc; printf "%15s: %6d\n", "Bad Delimiter", $mangled_delimiter; } $self->{FIRMWARE_VERSION} = $version; return $self } #/**---------------------------------------------------------------------- # @sub write_memory_files # # At the end of parsing, write out all the in-memory data # # @parameter $self - parser object # @output Report to STDOUT # @exception Will toss an exception in case of real trouble. # ----------------------------------------------------------------------*/ sub write_memory_files { my $self = shift; foreach my $file (keys %{$self->{OUTFILES}}) { open my $fh, '>', $file or croak "Cannot open $file for writing"; print {$fh} $self->{OUTFILES}{$file}; close $fh; } return $self; } #/**---------------------------------------------------------------------- # @sub file_data # # At the end of parsing, write out all the in-memory data # # @parameter $self - parser object # @ $file_type - file type to return # @output Report to STDOUT # @exception Will toss an exception in case of real trouble. # ----------------------------------------------------------------------*/ sub file_data { my $self = shift; my $file_type = shift; return $self->{OUTFILES}{$file_type}; } 1;