#!/usr/bin/env perl # -*- perl -*- #----------------------------------------------------------------------- # Module Name: $Source: /Users/goeke/programming/perl/crater/RCS/browse,v $ # Purpose: Merge two LRO-standard data files into one # Language: Perl 5 # Assumptions: Consistent with Data ICD, 32-02001 # Part Number: # Author: Robert F. Goeke (goeke@space.mit.edu # References: # Copyright: Massachusetts Institute of Technology 2006 #----------------------------------------------------------------------- ######################################################################## ######################################################################## # To Do # TTY Interface; all these are input with CR termination # "bias on" -- skip forward to this command # "#Bksp" -- skip backward # seconds # "" -- stop ######################################################################## ######################################################################## # Initial Constants ######################################################################## $Version = '$Id: browse,v 1.20 2009/03/27 13:31:56 goeke Exp goeke $'; require 5.6.0; use Fcntl; use Socket; use Time::Local; use Time::Local; # do /not/ use Time::gmtime, it breaks gmtime ??? use POSIX; # needed for strftime() $Epoch = timegm(0,0,0,1,0,2001); # Epoch for LRO MET $LeapSec = 1; # occured 1/1/06 # NB Perl time() function does not include leap seconds $FILEID = 202; # see comments for &Header $ECHO = 2; # cmd RT address for "echo" command $MAXTIME = 0xFFFFFFFF; # 32-bits of one $UDPADDR = 11403; # default output UDP port address $UDPHOST = "localhost"; # default output UDP machine $COMPRESS = 1; # default time compression $FILELIMIT = 250; # maximum number of input files (filehandle # limit on both Mac and Solaris @CmdTypes = ( '', # Command types used in Jump '', 'Echo', 'Discrete', 'VideoProc', 'Mask', 'ThinLLD', 'ThickLLD', 'CalAmp', ); $CmdArgs = " Valid jump-to-next-command arguments for \"j #\": 2 = Echo; 3 = Discrete; 4 = VideoProc; 5 = Mask; 6 = ThinLLD; 7 = ThickLLD; 8 = CalAmp; (none) = any "; $CmdHelp = " C = continue; K = slow down; L = speed up; S = Stop; X = Exit +/-### = skip ### seconds =xxx = skip ### seconds quietly J [#] = jump to next command ? = list of commands # "; ######################################################################## ######################################################################## # Main Program executes here # $Active is the number of files currently open (1..n) # $In[] - filenumber # $Time[] - current packet time # $Time[] = 0 for files which have reached EOF ######################################################################## ######################################################################## &Setup; # Process the command line &Initialize; # Open each input file & get first packet printf "STAT Starting time: %s\n",&Localtime($Min) if ($Verbose); &Distribute; # Work through the files, merge second by second # This is where keyboard control resides printf "STAT End time: %s\n",&Localtime(--$Min) if ($Verbose); unlink(@Tmpfiles); &Ausgang(0); # sometimes a delay on exit is called for ##########################That's all, folks! ########################### ######################################################################## ######################################################################## # Quit with a delay if in Verbose mode # (need delay so separate xterm doesn't disappear too soon) # Takes one argument: error code on exit ######################################################################## sub Ausgang { close(ARCHIVE) if (!$Packet); if ($Verbose>1) { print STDERR "*** Exit: window will close in 30 seconds ***\n"; sleep (30); } exit $_[0]; } ######################################################################## # Read the packets and pump them out # $Min contains the time of the earliest packet in current set # $Active contains number of currently open files; # we exit when this reaches zero # Alternatively, exit on hitting a Stop:Stop_add invoked with -T flag # # NB If we are writing to a file, only the first few lines of the # loop are executed; all the rest is for operator control of RT display ######################################################################## sub Distribute { # In the following, we /always/ have one packet from each file in cache $Halt = $Quiet = 0; my $cmd; my $value; while ($Active) { &D_Fetch; # read and write packets if ($Fn) { $Min++; # if writing to a file, ignore the keyboard next; } my ($rv,$buffer); $rv = sysread(STDIN, $buffer, 64); # we set non-blocking in Setup if (defined($rv)) { # do this only if someone's home $CEcho = 0; # set to cancel command echo chop $buffer; # successfully read $rv bytes from HANDLE if ($buffer =~ /^[cC]$/) { # continue $Halt = 0; printf "Speed = $Speed sec/sec\n"; } elsif ($buffer =~ /^[jJ]/) { # jump to next command ($cmd,$value) = split(/ /,$buffer); if (defined($value)) { if ($value=~/[^0-9]/) { print $CmdArgs; } else { $Start_add = $value; } } printf "Jumping to next %s\n", ($Start_add) ? "$CmdTypes[$Start_add] cmd" : "cmd"; $Halt = 0; # we go $Pass = 0; # but quietly $Found = 0; # clear flag while (!$Found && $Active) { &D_Fetch; $Min++; } $Start_add = 0; # clear start value $Pass++; # enable output &D_Fetch; # write # $Halt++; # and wait } elsif ($buffer =~ /^[kK]$/) { # slow down $Halt = 0; $Speed *= 2; printf "Speed = $Speed sec/sec\n"; } elsif ($buffer =~ /^[lL]$/) { # speed up $Halt = 0; $Speed /= 2; printf "Speed = $Speed sec/sec\n"; } elsif ($buffer =~ /^[s|S]$/) { # stop that $Halt++; print "Stop\n"; } elsif ($buffer =~ /^[x|X]$/) { # politely exit print "Exit program\n"; return; } elsif ($buffer !~ /[^0-9-+=]/) { # skip n seconds if ($buffer =~ /=/) { # special quiet backspace case $buffer =~ s/=/-/; $CEcho++; } $buffer = 1 if ($buffer == ""); # for quick keyboarding my $save = $Min; # &Initialize will reset $Min if ($buffer < 0) { # start from beginning for ($ii=0; $ii<$Active; $ii++) { # we rewind and skip headers # /not/ seek, since that works with buffered reads sysseek($In[$ii],64,SEEK_SET) || die "Error on rewind of file $ii"; } &Initialize; # get first packets } $Halt = 0; # we go $save = $save + $buffer - 1; # this far if ($save<$Min) { print "Jumping back to beginning of file\n"; } elsif ($buffer == 1) { print "."; } else { print "Jumping $buffer seconds; Stop\n"; } $Min = ($save<$Min) ? $Min : $save; # but not too far! $Pass = 0; # and quietly &D_Fetch; $Min++; # next second $Pass++; # write this one &D_Fetch; $Halt++; # and stop } elsif ($buffer =~ /^\?/) { printf $CmdArgs; } else { printf $CmdHelp; } } # a sleep implementation that works for numbers <1 select(undef, undef, undef, $Speed) if ($Packet); $Min++ if (!$Halt); } # &Ausgang; } ######################################################################## # Get 1 full second from each available input stream # Stops when &ReadIt acquires a packet with time >$Min # which leaves that packet's info in the buffers # Returns 0 normally, 1 on getting non-zero from ReadIt ######################################################################## sub D_Fetch { my $jj,$mark,$next; for ($jj=0; !$Halt && $Active && $jj<=$#In; $jj++) { $mark = 0; while ($Time[$jj]<=$Min) { # if latest packet has this time &WriteIt($jj) if ($Pass); # Pass it out &ReadIt($jj); # and get a new one $mark++; # Mark that we got at least one } } if (!$mark) { # no packets were processed $next = $MAXTIME; for ($jj=0; $jj<=$#In; $jj++) { # scan for a minimum $next = $Time[$jj] if ($Time[$jj]<$next); } if ($Min < $next - 60) { printf "STAT -> Skipping from %s to %s\n", &Localtime($Min), &Localtime($next) if $Verbose; $Min = $next; $Min--; # because caller will increment } } } ######################################################################## # Get a complete CCSDS packet -> $Data # Does a data sync if required when reading from a file # Returns 0 on success, 1 on end-of-file, 2 on EOF during sync, # 3 on bad length field causing overrun # NB the sync test will fail for command packets !!! ######################################################################## sub GetPacket { my ($sync,$count,$length,$d2,$check); my $index = $_[0]; defined($check=sysread($In[$index],$Data[$index],6)) || die $!; return 1 if ($check==0); # Normal End-of-file if ($check<6) { print STDERR "WARN $check orphan bytes at end-of-file\n" if ($Verbose); return 2; } ($sync,$count,$length) = unpack("n n n",$Data[$index]); if (!($sync == 0x0864 || $sync == 0x0878 || $sync == 0x0879 || $sync == 0x087a || $sync == 0x087b || $sync == 0x087c || $sync == 0x087d)) { print STDERR "\nWARN data sync in progress ... " if ($Verbose); $mark = 4; # 2 bytes that were wrong and 2 to come do { $mark++; defined(sysread($In[$index],$d2,1)) || die $!; if (!defined($d2)) { # End-of-file print STDERR "$mark bytes skipped\n" if ($Verbose); return 2; } $u2 = unpack("C",$d2); if (!defined($u2)) { # evidently EOF doesn't unpack print STDERR "$mark bytes skipped\n" if ($Verbose); return 2; } $sync = ($sync << 8)&0xFF00; # lose MSByte $sync = $sync | $u2; # add new byte at end } until ($sync == 0x0864 || $sync == 0x0878 || $sync == 0x0879 || $sync == 0x087a || $sync == 0x087b || $sync == 0x087c || $sync == 0x087d); print STDERR "$mark bytes skipped\n" if ($Verbose); defined(sysread($In[$index],$d2,4)) || die $!; # get next 4 bytes ($count,$length) = unpack("n n",$d2); # extract info $Data[$index] = pack("n n n",$sync,$count,$length); # repack } $length++; # CCSDS length is packet-primary_header-1 defined($check=sysread($In[$index],$d2,$length)) || die $!; if($check<$length) { # presumably, file ended early printf STDERR " WARN packet read terminated early; WARN %d bytes expected in last packet, %d bytes received\n", $length+6, $check+6 if ($Verbose); return 3; } $Data[$index] .= $d2; return 0; } ######################################################################## # Put an LRO-standard 64 byte header in a file # Except, of course, they don't have a fileId for "all packets" # and rtlm doesn't know what will be in the stream, anyway # fileId = 202 for our private usage here # fileId = 201 for analog housekeeping files # fileId = 200 for primary + secondary science files # # Single argument passed is file name of data file ######################################################################## sub Header { my ($fileId,$blank,$telemetry); $fileId = $FILEID & 0x000000FF; $blank = 0x00000000; # $filesec = time() - $Epoch + $LeapSec; # current MET $telemetry = pack("N N N N N N a40", $fileId, $blank, $File_sec, $blank, $blank, $blank, $_[0]); syswrite(ARCHIVE,$telemetry,length($telemetry)); print "STAT File header written\n" if ($Verbose>1); } ######################################################################## # Read back the Header in an LRO-standard archive file ######################################################################## sub Header_read { my ($id,$sec,$name) = unpack("N x4 N x12 a40",$_[0]); if ($Active<1) { # Save first input file into $File_sec = $sec; # $File_name = $name; # $File_name =~ s/[A-z.]{4}\000+$/.OUT/; } $name =~ s/\000+$//; my $dt = &Localtime($sec); printf "### Input %d ->\t FileID: %d\tTime: %s\tFilename: %s\n", $Active,$id,$dt,$name if $Verbose; } ######################################################################## # Help facility ######################################################################## sub Help { my $ii; print " usage: $0 [-a ###] [-c #] [-f basename] [-F file_id] [-h] \\ [-m machine_name] [-q UDP_port] \\ [-s #:###] [-t|T #:###] [-u] [-v] filename1 [filename2 ...] "; return if (!$Verbose); print " Merge and filter one or more LRO data files into a UDP packet stream (the default) or a single LRO data file. NB: start/stop commands are of form Command_ID:Command_Value If only a value is given, the \"echo\" command is assumed. flag -a ### Only output packets with given AppId -c # Compress time by # [$COMPRESS] -f filename write output to filename -F file_id file_id inserted in LRO-standard header [$FILEID] -l list embedded echoes/commands; equivalent to \"-f /dev/null -v\" -h provide this help message -m hostname select host to aim UDP packets at [$UDPHOST] -q port select UDP port for packet output [$UDPADDR] -s #:### start output on this command in input -t #:### stop output on this command in input -T #:### stop output and exit on this command -u when reporting time, use UTC [local timezone] -v verbose operation When emitting a packet stream, the flow is controlled from the keyboard; the packets are initially emitted at real-time speed. C continue L speed up by 2x K slow down by 2x J jump to next command echo S stop ### skip forward n seconds and stop -### skip backward n seconds and stop =### skip backward n seconds quietly and stop $Version "; } ######################################################################## # Get first packet from each stream find earliest time stamp # BUT first packet will have time=0 on power up # $Active will start will all open and notch down on each EOF ######################################################################## sub Initialize { do { &ReadIt(0); # get first packet and time } while (!$Time[0]); $Min = $Time[0]; # initialize $min for ($jj=1; $jj<=$#In; $jj++) { # get first packet from each file do { &ReadIt($jj); } while (!$Time[$jj]); $Min = $Time[$jj] if ($Min>$Time[$jj]); # find earliest time } } ######################################################################## # Initialize Input File pipe # We can't handle compressed files directly because we are using # "sysread" and that doesn't work well at EOF # Save tmp file names in $Openstuff ######################################################################## sub InitInput { my $readfile = $_[0]; # safety save of argument my $data; my $tmpfile = "/tmp/" . $Basename . "-data.$Active"; if ($readfile =~ /\.gz$/ || $readfile =~ /\.zip$/) { system("gzcat $readfile >$tmpfile"); open($In[$Active],"$tmpfile") || die "Could not open temporary file $tmpfile"; push(@Tmpfiles,$tmpfile); # save for later removal } else { open($In[$Active],"$readfile") || die "Could not open input file $readfile"; } defined(sysread($In[$Active],$data,64)) || die $!; # get header &Header_read($data); } ######################################################################## # Set up Output File including initial 64 byte header ######################################################################## sub InitOutput { my $fn = $_[0]; # Save filename passed in # if (-e $fn) { # print STDERR "Output file \"$fn\" already exists\n"; # exit 2; # } open(ARCHIVE,">$fn") || die "Could not open $fn to write"; print "STAT Opening file $fn for output\n" if ($Verbose); &Header($fn); } ######################################################################## # Set up Output Packt Stream (but no initial 64 byte header!) ######################################################################## sub InitPacket { my $udp_host = inet_aton($_[0]); my $udp_port = $_[1]; $Portaddr = sockaddr_in($udp_port,$udp_host); socket(ARCHIVE, PF_INET, SOCK_DGRAM, getprotobyname('udp')) || die $!; printf STDERR "STAT Writing UDP packet to $_[0]:$_[1]\n"; printf "STAT Accepting input from keyboard -- try \"h\"\n"; } ######################################################################## # Convert LRO time to local version ######################################################################## sub Localtime { my ($sec,$min,$hour,$mday,$mon,$year) = eval "$TIME($Epoch + $_[0])"; return sprintf("%02d:%02d:%02d %d/%d/%02d $TZ", $hour, $min, $sec, $mon+1, $mday, $year-100, %TZ); } ######################################################################## # Convert octal, hex, or decimal numeric input to standard number ######################################################################## sub Number { return $_[0] if ($_[0] =~ /^[1-9]/); # do this before octal test! return $_[0] if ($_[0] =~ /^0$/); return oct($_[0]) if ($_[0] =~ /^0[1-7]+$|^0x[0-9a-fA-F]+$|^0X[0-9a-fA-F]+$/); print "Not recognized as a number> $_[0]"; &Help; &Ausgang(1); } ######################################################################## # Read one packet from an input file # Takes file index as argument # Extracts last command info from Seconday Science # Updates $Time[] for this packet # Puts packet into $Data[] # Must deal with errors and end-of-file generated by &GetPacket ######################################################################## sub ReadIt { my ($appid,$seqcount,$length,$sec_time,$subsec_time,$cmd_add,$cmd_value); my $index = $_[0]; my $result = &GetPacket($index); if ($result) { if ($result == 2) { print "WARN File $index ended with a packet sync error\n"; &Ausgang(2); } elsif ($result == 3) { print "WARN File $index ended with a bad packet length error\n"; &Ausgang(3); } else { $Time[$index] = $MAXTIME; # Mark as complete $Active--; # Decrement count of active files print "STAT -> EOF reached for input file $index\n" if ($Verbose>1); } } else { ($appid) = unpack("n",$Data[$index]); $appid &= 0x07FF; if ($appid == 121) { # Secondary Science ($appid,$seqcount,$length,$sec_time,$subsec_time, $cmd_add,$cmd_value) = unpack("n n n N n n n",$Data[$index]); $cmd_add &= 0x001F; # $seqcount &= 0x3FFF; # $subsec_time &= 0xF000; # printf "STAT cmd echo -> %d:0x%04x\n", $cmd_add, $cmd_value printf "STAT_CMD_ECHO -> %s -> %s\n", &Localtime($sec_time),&Translate($cmd_add,$cmd_value) if ($cmd_add && $Verbose); if ( ((($Start_add!=0)&&($cmd_add==$Start_add)) || ($Start_add==0)&&($cmd_add)) && ((!$CEcho && !$Fn)||$Verbose)) { $Found++; } if (($cmd_add==$Start_add) && ($cmd_value==$Start)) { $Pass++; $Once = ($Once) ? 2 : 0; } elsif (($cmd_add==$Stop_add) && ($cmd_value==$Stop)) { $Pass = 0; if ($Once>1) { print "\n"; # Clean up after packet counter print "STAT Stop command reached\n"; &Ausgang(0); } } } else { ($appid,$seqcount,$length,$sec_time,$subsec_time) = unpack("n n n N n n n",$Data[$index]); } $Time[$index] = $sec_time; } } ######################################################################## # Process all the command line arguments # Open input and output files # NB that output must be closed and write protected before progam exit! ######################################################################## sub Setup { $Fn = ""; # filename for archiving data $Active = 0; # To keep track of the input files $APPS = ""; # To keep track of which AppIds to write $TIME = 'localtime'; # default is to use local wall clock $Basename = $0; # We might need this later for tmp files $Basename =~ s/.*\///; my $udp_host = $UDPHOST; # default UDP host my $udp_addr = $UDPADDR; # default UDP port $Packet = 0; # 0 = write to file; 1 = UDP packet output $Pass = 1; # Cmd Echo filter starts in all-pass $Start = $Stop = -1; # This has the effect of all-pass $Start_add = $Stop_add = 0; $Once = 0; # 0 = ignore; 1 = cocked; 2 = exit $Speed = 1/$COMPRESS; $Verbose = 0; my ($foo,$bar); while ( $foo = shift(@ARGV) ) { if ( $foo =~ /^-[vV]/ ) { $Verbose++; next; } if ( $foo =~ /^-[aA]/ ) { if ( !defined($bar=shift(@ARGV)) || $bar !~ /^1[0-9][0-9]$/) { print STDERR "Flag $foo requires a valid AppID argument, not $bar\n"; &Help; } $APPS = $APPS . " " . $bar; next; } if ( $foo =~ /^-[cC]/ ) { if ( !defined($bar=shift(@ARGV)) || $bar =~ /[^0-9]/) { print STDERR "Flag $foo requires a numeric argument, not $bar\n"; &Help; } $Speed = 1/$bar; next; } if ( $foo =~ /^-[f]/ ) { if ( !defined($ARGV[0]) ) { print STDERR "flag $foo requires an argument\n"; &Help; &Ausgang(1); } $Fn = shift(@ARGV); # filename length limit is 40 bytes, but we will add ".bin" if (length($Fn)>36) { print STDERR "Filename provided with $foo flag is limited to 36 characters\n"; &Help; &Ausgang(1); } next; } if ( $foo =~ /^-[F]/ ) { if ( !defined($ARGV[0]) ) { print STDERR "flag $foo requires an argument\n"; &Help; &Ausgang(1); } $FILEID = shift(@ARGV); if ($FILEID !~ /[0-9]{1,3}/) { print STDERR "File_ID provided with $foo flag must be integer less than 256\n"; &Help; &Ausgang(1); } next; } if ( $foo =~ /^-[lL]/ ) { $Fn = "/dev/null"; $Verbose++; next; } if ( $foo =~ /^-[hH]/ ) { $Verbose++; &Help; &Ausgang(0); } if ( $foo =~ /^-[mM]/ ) { if ( !defined($ARGV[0]) ) { print STDERR "flag $foo requires an argument\n"; &Help; &Ausgang(1); } $udp_host = shift(@ARGV); next; } if ( $foo =~ /^-[sStT]/ ) { # Start/Stop notice if ( !defined($ARGV[0]) ) { print STDERR "flag $foo requires an argument\n"; &Help; &Ausgang(1); } my ($cmd,$value); $bar = shift(@ARGV); if ($bar =~ /:/) { ($cmd,$value) = split(/:/,$bar); } else { $cmd = $ECHO; $value = $bar; } if ($foo=~/^-[sS]/) { $Start = &Number($value); $Start_add = &Number($cmd); $Pass = 0; # turn off default all-pass } else { $Stop = &Number($value); $Stop_add = &Number($cmd); } $Once++ if ($foo=~/-[T]/); # quit after first "stop" next; } if ( $foo =~ /^-[qQ]/ ) { if ( !defined($udp_addr=shift(@ARGV)) || $udp_addr =~ /[^0-9]/) { print STDERR "Flag $foo requires a numeric argument\n"; &Help; } next; } if ( $foo =~ /^-[uU]/ ) { $TIME = 'gmtime'; next; } if ( $foo =~ /^-/ ) { print STDERR "Unknown argument: $foo\n"; &Help; &Ausgang(0); } if ( -r $foo ) { # A filename to read &InitInput($foo); $Active++; # NB resetting ulimit on a Mac will not work if ($Active>$FILELIMIT) { print STDERR "Cannot read more than $FILELIMIT files in one pass\n"; &Ausgang(1); } } else { print STDERR "Could not read filename: $foo\n"; &Ausgang(1); } } if ($Active<1) { print STDERR "Must specify at least one input file\n"; &Help; &Ausgang(1); } $| = 1; # don't buffer stdout $TZ = ($TIME =~ /gmtime/) ? "UTC" : strftime("%Z",localtime()); if ($Fn) { # Write to file if name given &InitOutput($Fn); } else { &InitPacket($udp_host,$udp_addr); $Packet++; } # Without a "start", the first "stop" would be final, but we'd # still go on processing all the inputs if not for this fudge $Once++ if ($Start<0); printf "STAT Start on cmd %d:0x%04x\n",$Start_add,$Start if ($Verbose&&$Start>=0); printf "STAT Stop on cmd %d:0x%04x\n",$Stop_add,$Stop if ($Verbose&&$Stop>=0 ); # Need to set up STDIN for non-blocking if we're in packet mode if (!$Fn) { $flags = ''; fcntl(STDIN, F_GETFL, $flags) || die "Couldn't get flags for HANDLE : $!\n"; $flags |= O_NONBLOCK; fcntl(STDIN, F_SETFL, $flags) || die "Couldn't set flags for HANDLE: $!\n"; system("stty -echo") && die $!; # don't echo keyboard } printf "STAT Filtering on AppIds $APPS\n" if ($Verbose && $APPS); } ######################################################################## # Translate echoed commands numbers into mnemonics ######################################################################## sub Translate { my $cmd = $_[0]; my $val = $_[1]; my $retval = "*** error ***"; if ($cmd == 2) { $retval = sprintf("Echo 0x%04x (hex) %d (decimal)",$val,$val) } ; if ($cmd == 3) { $retval = "Detector Bias Off" if ($val == 1<<13); $retval = "Detector Bias On" if ($val == 1<<12); $retval = "Electrical Cal Low Off" if ($val == 1<<11); $retval = "Electrical Cal Low On" if ($val == 1<<10); $retval = "Electrical Cal High Off" if ($val == 1<<9); $retval = "Electrical Cal High On" if ($val == 1<<8); $retval = "Electrical Cal Rate 8Hz" if ($val == 1<<7); $retval = "Electrical Cal Rate 2KHz" if ($val == 1<<6); $retval = "Data Test Mode" if ($val == 1<<4); $retval = "Clear all Commands" if ($val == 1<<1); $retval = "System Reset" if ($val == 1); } if ($cmd == 4) { $retval = "Detector D1 Processing Off" if ($val == 1<<15); $retval = "Detector D1 Processing On" if ($val == 1<<14); $retval = "Detector D2 Processing Off" if ($val == 1<<13); $retval = "Detector D2 Processing On" if ($val == 1<<12); $retval = "Detector D3 Processing Off" if ($val == 1<<11); $retval = "Detector D3 Processing On" if ($val == 1<<10); $retval = "Detector D4 Processing Off" if ($val == 1<<9); $retval = "Detector D4 Processing On" if ($val == 1<<8); $retval = "Detector D5 Processing Off" if ($val == 1<<7); $retval = "Detector D5 Processing On" if ($val == 1<<6); $retval = "Detector D6 Processing Off" if ($val == 1<<5); $retval = "Detector D6 Processing On" if ($val == 1<<4); } if ($cmd == 5) { $retval = sprintf("Detector Mask MSW: 0x%04x",$val); } if ($cmd ==6) { $retval = sprintf("Thin Detector Amplitude Disc: %d",$val); } if ($cmd ==7) { $retval = sprintf("Thick Detector Amplitude Disc: %d",$val); } if ($cmd ==8) { $retval = sprintf("Electrical Cal Amplitude: %d",$val); } return $retval; } ######################################################################## # Write one packet out # Takes file index as argument # NB Packet /output/ filtering takes place here at end of chain ######################################################################## sub WriteIt { if ($APPS) { # if something is in the filter definition ($appid) = unpack("n",$Data[$_[0]]); $appid &= 0x07FF; # printf "$appid \n" if $Verbose; return if ($APPS !~ /$appid/); } if ($Packet) { defined(send(ARCHIVE,$Data[$_[0]],0,$Portaddr)) || die $!; } else { syswrite(ARCHIVE,$Data[$_[0]],length($Data[$_[0]])) || die $!; } $Pcount++; # printf "\r%10d packets %s", $Pcount if ( !($Pcount%10) ); } ######################################################################## # Pod follows ######################################################################## =for html CRaTER -- browse =head2 NAME browse -- takes several LRO-standard data files and time-merges them into a single output stream, either UDP packets (by default) or a file, slicing and dicing on the way. =head2 USAGE browse [-a ###] [-c #] [-f basename] [-F file_ID] [-h] [-m machine_name] [-q UDP_port] [-s #:###] [-t|T #:###] [-u] [-v] filename1 [filename2 ...] =head2 FLAGS -a ### only output packets with the given AppId multiple -a flags are allowed -c # set time compression ratio; default is 1 -F sets file_ID in LRO standard LO archive header; default is 202 -f writes LRO-standard LO archive to given name -l list embedded echoes/commands; equivalent to "-f /dev/null -v" -h prints this help message -m machine_nmae machine to which UDP packets are to be sent [localhost] -q UDP_port port to which UDP packets are to be sent [11403] -s #:### start output on this command echo in form of command_id:command_value. If no command_id given, the echo command is assumed -t #:### stop output on this command echo; see -s flag -T #:### stop output and exit on this command echo; see -s flag -u when reporting time, use UTC [local timezone] -v verbose display =head2 DESCRIPTION =head3 Input Given a set of LRO-standard data files, B reads all in parallel and writes them into a time-ordered, single output file. Within a given one second interval, the input streams are read in the order given in the command line. If the input files are compressed, the /tmp directory is used to accumulate working uncompressed input data. =head3 Packet Output In this (default) mode the B<-c, -s, and -t flags> are typically not used. Instead the output starts by emitting packets in real time and is controlled by keyboard entry. The following letters may be entered followed by a RET. C continue at last speed setting L speed up by 2x K slow down by 2x J # jump to next command # [ECHO] S stop X exit program ### skip forward n seconds and stop -### skip backward n seconds and stop =### skip backward n seconds quietly and stop A very large negative number will simply rewind the input to the beginning. An empty RET will advance the stream by 1 second also. Each echoed command is written to STDOUT, even if the packets are not being written out (as in a skip). Since a negative skip is accomplished by rewinding the input files to the beginning, a single -1 command will result in all commands issued up to this point being (re)echoed. Using the syntax B<=###> rather than B<-###> will cancel this behavior. =head3 File Output Filtering of the input is handled by scanning for commands echoed in secondary science packets, starting/stopping output based on matches to the given flag arguments. The nominal format is Command_ID:Command_Value See the Data ICD (32-02001) for the command list, and note that the numbers may be given in octal, decimal, or hex notation. If no Command_ID: is given, the "echo" command is assumed. The flow control is applied immediately upon reading a packet from whichever input stream contains secondary science data. The effect is that the output stream will contain the packet with the "start" echo, but will not contain the packet with the "stop" echo. The B<-c flag> works as a time-compression ratio; a value of 0.2 will cause the output to run 5 times slower than real time. Conversely, a value of 5 will cause the output to run 5 times faster than real time. =head2 BUGS There is an operating system limit on the number of open files any single process can access -- typically 256. Given more data files than this, the generation of intermediate products is called for. The first packet of each input file is used to determine where the first-in-time data resides. If one of the input files has a sudden time step, strange things may appear in the output stream. =head2 SEE ALSO rtlm csh -->> "limit descriptors n" to increase limit on open files =head2 AUTHOR Bob Goeke =head2 RCS Information $Id: browse,v 1.20 2009/03/27 13:31:56 goeke Exp goeke $ =cut ######################################################################## # history follows ######################################################################## # $Log: browse,v $ # Revision 1.20 2009/03/27 13:31:56 goeke # Fix bug in assigning UDP address with -q flag # # Revision 1.19 2009/01/05 14:20:27 goeke # Add -l flag to save typing # # Revision 1.18 2008/11/24 18:37:02 goeke # Fix minor but in reporting end time of last packet # # Revision 1.17 2008/10/24 13:24:35 goeke # Mods to help messages and echos (now displays actual command names) # # Revision 1.16 2008/09/25 14:31:46 goeke # Add feature to (real time) jump to a designated command id # # Revision 1.15 2008/09/23 15:30:16 goeke # Fix bug in previous (double increment on $Active) # # Revision 1.14 2008/09/23 15:23:50 goeke # Handle file limit gracefully # # Revision 1.13 2008/09/23 14:46:26 goeke # Fix another bug in $Active. # # Revision 1.12 2008/09/12 19:57:43 goeke # Add -u flag for UTC time # # Revision 1.11 2008/08/22 14:58:53 goeke # Made the time jump logic more reasonable. # # Revision 1.10 2008/08/21 16:51:29 goeke # Add ability to skip ahead rapidly if s/c clock changes in mid-data # # Revision 1.9 2008/08/19 18:29:38 goeke # Add AppId 100 to sync logic # Add time to command echo in verbose mode # # Revision 1.8 2008/06/17 14:13:21 goeke # Added a 30 second termination delay in Verbose mode so that an # invocation in an xterm doesn't exit too, too promptly # # Revision 1.7 2008/05/02 15:14:51 goeke # Fix error in display of Discimator Mask command echo # Delete TranslateDetector ... only valid for data, not command echo # # Revision 1.6 2008/04/10 18:08:01 goeke # Improved reporting of command echoes # # Revision 1.5 2008/04/10 14:33:20 goeke # Fixed bug in "jump" command # Added "exit" command so that unlinking tmp files would happen # # Revision 1.4 2008/04/10 14:16:16 goeke # Allow output files to overwrite previous versions # Fix bug in trying to run sysread on compressed files # (added intermediate step of generating temp files in /tmp) # # Revision 1.3 2008/04/09 18:54:01 goeke # Fix bug in file counter $Active # Minow doc fixes # # Revision 1.2 2008/03/06 17:23:02 goeke # Fix help message and pod # # Revision 1.1 2008/02/27 17:15:29 goeke # Initial revision # # Revision 3.9 2008/02/05 20:55:32 goeke # Name change, that's all # # Revision 3.8 2008/02/05 20:48:07 goeke # Fixed bugs in &Translate # Arbitrary large negative number returns to beginning of file(s) # Using = sign instead of - squelches command echoes # # Revision 3.7 2008/02/04 20:49:08 goeke # Translate echoed commands into Mnemonics # # Revision 3.6 2008/02/04 20:05:37 goeke # Add -J flag to step to next echoed command # # Revision 3.5 2008/02/04 16:18:24 goeke # Fixed seek bug (use sysseek) and updated help files # # Revision 3.4 2008/02/04 12:51:22 goeke # Got +/- n seconds steps working; documentation needs updating # # Revision 3.3 2008/01/28 20:49:24 goeke # Accepts console input to slow down/speed up in UDP mode. # # Revision 3.2 2008/01/28 15:54:49 goeke # Fixed documentation; added -c flag to select time compression # # Revision 3.1 2008/01/28 15:26:25 goeke # Write out UDP packets as default; output to files still works # # Revision 2.2 2008/01/24 19:32:51 goeke # Added -T flag to exit after first stop cmd is seen # # Revision 2.1 2008/01/24 15:52:35 goeke # Added -s, -t flags to start and stop stream on "echo" command # # Revision 1.4 2008/01/23 20:07:17 goeke # Implemented -F flag # Default output file name is now "composite.bin" # # Revision 1.3 2008/01/23 17:03:41 goeke # Processes n files correctly; archive date is current TOD # # Revision 1.2 2008/01/23 15:18:51 goeke # Restructured for n input files ... BUG: quits on end of first file! # # Revision 1.1 2008/01/22 19:24:21 goeke # Initial revision #