package Bnc; # Perl utility functions for BNC # # Revision: $Header: trunk/BNC/scripts/Bnc.pm 9597 2022-01-10 16:35:57Z wiese $ use strict; use warnings; use File::Basename; use File::Spec::Functions qw(catfile); use File::Temp qw(tempfile); use Exporter; use Time::Piece 1.30; use PDL::Lite; # to avoid namespace pollution use PDL::Primitive; use Log::Log4perl qw(:easy); # use List::MoreUtils qw(uniq); # Prototype mismatch with PDL (uniq) # ============================================================================= # callBnc ($bnc_ini, %$opts_ref) # ============================================================================= # Call BNC # # Param : $bnc_ini [optional] The BNC config file that should be used. If not set, the default config is ised. # $opts_ref [required] Hash with BNC config options as key. They will overwrite the settings from the config file. # Return : BNC exit status # ============================================================================= sub callBnc { my ( $bnc, $bnc_ini, $opts ) = @_; my $config_file = ""; if ( $bnc_ini && -s $bnc_ini ) { $config_file = "--conf $bnc_ini"; } else { DEBUG("callBnc: Use default bnc-ini file"); } my $opts_str = ""; if ($opts) { $opts_str = options2string($opts) } # my @cmd = ( # 'xvfb-run', # "--server-args='-screen 0, 1280x1024x8'", # 1024x768x24 # "$bnc", # "--nw", # $opts_str, # ); #my $rc = call_system("xvfb-run -a -e /home/user/xvfb.err --server-args='-screen 0 1280x1024x8' $bnc --nw $config_file $opts_str"); return Common::runCmd("$bnc --nw $config_file $opts_str"); } # Converts options map to string used for calling BNC. sub options2string { my ($opts) = shift; my $opts_str = ""; if ($opts) { foreach my $key ( keys %{$opts} ) { $opts_str .= "--key $key" . ' "' . $opts->{$key} . '" '; } } return $opts_str; } # ============================================================================= # parseMessageTypesFromFile # ============================================================================= # Parse Message-Types with repetition rate from a BNC/scanRTCM logfile # # Param : $logfile [required] Path to the BNC-logfile. # $caAbbr [optional] caster name or abbreviation # # Return : Message-types for each mountpoint (as Hash-Ref) # Hash with mp as key and the list of messTypes as value # Example: $VAR1 = { 'RIO10' => ['1004(1)','1006(15)','1008(15)',...], # 'CLIB0' => ['1004(1)','1006(10)','1008(10)',...], # }; # ============================================================================= sub parseMessageTypesFromFile { my ( $logfile, $caAbbr, $mytmpPath ) = @_; unless ( -s $logfile ) { ERROR "File [$logfile] is empty or does not exist"; return; } my $tmp = File::Temp->new( UNLINK => 1, SUFFIX => '.messtyps' ); my $casterMessTypeFile = $tmp->filename; #my $caster = $logfilename =~ s/\.log\.messtyps//r; #INFO "Process caster '$caster'"; INFO "Process $logfile"; # scan beginns with that line: 16-02-18 23:58:02 WTZ37: Get data in RTCM 3.x format # First grep for message type lines in bnc-logfile and write them to local temp. directory # NOTE: 'sort -u' because Ephemeries message-types 1019 (GPS), 1020 (GLONASS), 1045 (Galileo) come # for every sat. at the same time/second. This is done for getting the repetition rate system ("grep \"Received message type\" $logfile | sort -u > $casterMessTypeFile") == 0 or ERROR "Fehler: $!"; # -------------------------------------------- # Get message types foreach station/mountpoint # -------------------------------------------- my $cmd = "cat $casterMessTypeFile | awk '{print \$3, \$7}' | sort -u"; my @messTypes = `$cmd`; # ['DARX1: 1004\n','DARX1: 1006\n',... ] if ( scalar @messTypes < 1 ) { ERROR "Could not retrieve message types from file $casterMessTypeFile"; return; } # Note: Skip first 1500 lines because BNC is weird here, all lines with same timestamp, buffer problem? # ------------------------------------------------------ # Guess repetition rate for each mountpoint/message-type # ------------------------------------------------------ # For that get the first $minSamples appearances of mountp with same message type # and build the differences between them my $minSamples = 4; foreach my $mpType (@messTypes) { chomp $mpType; # 'AUBG3: 1004' my ( $mp, $mt ) = split ( /:\s*/, $mpType ); my $cmd = "grep \"$mp: Received message type $mt\" $logfile"; my @rows = `$cmd`; if ( scalar @rows < 1 ) { ERROR "Could not retrieve message types for $mpType from file $casterMessTypeFile"; next; } if ( scalar @rows <= $minSamples ) { WARN "Could not guess repetition rate for message type $mpType: only " . scalar @rows . " matches found"; next; } my $repRate = _computeMessTypRepetitonRate( \@rows, $caAbbr ); if ($repRate) { $mpType .= '(' . $repRate . ')'; } } # ----- end foreach messageType ----- #if ( unlink $casterMessTypeFile ) { TRACE "Removed file [$casterMessTypeFile]" } # Return an HASH OF ARRAYS with mp as key and the list of messTypes as value my %messTypesHash; foreach (@messTypes) { my @ele = split ( ': ', $_ ); my $mp = shift @ele; push ( @{ $messTypesHash{$mp} }, shift @ele ); } return \%messTypesHash; } # ============================================================================= # _computeMessTypRepetitonRate # ============================================================================= # Guess repetition rate of message types # # Param : $firstMatches [required] Array-Ref with first appearances of station # and mess type. Complete logfile lines, e.g. # '13-07-04 17:38:26 BRUX0: Received message type 1004 ' # $caAbbr [optional] caster abbreviation # Return : repetition rate in secs, (median value) # ============================================================================= sub _computeMessTypRepetitonRate { my ( $rows, $caAbbr ) = @_; my $maxGap = 600; # if gap > 10min then we guess it is a new scan $rows->[0] =~ /.{18}([a-x0-9]+): Received message type (\d+)/i; my $stat = $1; my $mestp = $2; # Create list of unix timestamps my @scans; my $scan = 0; my ( $prevTime, $deltaT ) = ( 0, 0 ); foreach (@$rows) { my $uxtime = date2unix( substr ( $_, 0, 17 ) ); if ( !$uxtime ) { ERROR "Could not parse date from line $_"; next; } $deltaT = $uxtime - $prevTime; next if ( $deltaT == 0 ); # e.g. eph 1019,1020 one message for each sat. next if ( $deltaT <= 1 && $mestp =~ /10(19|20|42|43|44|45|46)|63/ ); if ( $prevTime && $deltaT > $maxGap ) { $scan++; } push ( @{ $scans[$scan] }, $uxtime ); $prevTime = $uxtime; } my @repRates; my $highest_nof_diffs = 0; foreach (@scans) { my @timestamps = @{$_}; # Compute the differences my @diffs; for ( my $i = 1; $i <= $#timestamps; $i++ ) { push ( @diffs, $timestamps[$i] - $timestamps[ $i - 1 ] ); } my $nof_diffs = scalar @diffs; if ( $nof_diffs < 2 ) { WARN("$stat: $mestp: only $nof_diffs diffs"); next; } my ( $mean, $prms, $median, $min, $max, $adev, $rms_n ) = stats( pdl \@diffs ); $mean = sprintf ( "%.0f", $mean ); $rms_n = sprintf ( "%.02f", $rms_n ); print $stat, ": ", $mestp, ": ", join ( ' ', @diffs ), "[Sig: $mean, $rms_n]\n"; if ( $rms_n > 10 ) { WARN("$stat: $mestp: RMS too high: $rms_n"); next; } # get the most frequent value my %counti = (); $counti{$_}++ foreach (@diffs); my ( $ni, $mfv ) = ( 0, 0 ); while ( my ( $k, $v ) = each %counti ) { if ( $v > $ni ) { $ni = $v; $mfv = $k; } } my $rounded_val = $mfv; # init foreach ( ( 1, 5, 10, 15, 30, 60, 120, 150, 300 ) ) { # most likely values my $mdiff = abs ( $mfv - $_ ); if ( $mdiff <= 2 ) { $rounded_val = $_; } } push ( @repRates, [ $rounded_val, $nof_diffs ] ); if ( $nof_diffs > $highest_nof_diffs ) { $highest_nof_diffs = $nof_diffs; } } # ----- end foreach scan ----- my @mostLikelyRates = grep { $_->[1] == $highest_nof_diffs } @repRates; my $mostLikelyRate = $mostLikelyRates[0]->[0]; foreach (@repRates) { if ( abs ( $_->[0] - $mostLikelyRate ) > 2 ) { ERROR "$stat: $caAbbr: $mestp: repetition rates from different scans differ: $mostLikelyRate $_->[0]"; if ( scalar @repRates == 2 ) { return; } } } return $mostLikelyRate; } # ============================================================================= # parseConfig ($confFile) # ============================================================================= # Parse the BNC config file. # # Param : $confFile [required] BNC config file # Return : Hash with configuration on success, otherwise undef # Usage : $bncConf = parseConf($bncConfFile); # $corrMount = $bncConf->{'PPP'}->{'corrMount'}; # ============================================================================= sub parseConfig { my ($confFile) = @_; -s $confFile || LOGDIE "BNC config file \"$confFile\" does not exist\n"; TRACE "Parse BNC config file $confFile"; open ( my $INP, '<', $confFile ) || die "Could not open file '$confFile': $!"; my @confLines = <$INP>; close ($INP); my %conf; my $section; # [General], [PPP] foreach (@confLines) { chomp; s/#.*//; # entfernt Kommentare s/^\s*//; # whitespace am Anfang entfernen s/\s+$//; # entfernt alle whitespaces am Ende next unless length; if ( $_ =~ /\[(\S+)\]/ ) { $section = $1 } next if ( !$section ); my ( $key, $val ) = split ( /\s*=\s*/, $_, 2 ); if ( !defined $val ) { $val = "" } if ( $key eq "mountPoints" ) { # Simple parsing $val =~ s/^\/\///; my @mpts = split ( /,\s?\/{2}/, $val ); $conf{$section}->{$key} = \@mpts; # Extended parsing my @mpts_def = (); foreach (@mpts) { # user:passwd@igs-ip.net:2101/ASPA0 RTCM_3.0 ASM -14.33 189.28 no 1 if ( $_ =~ /^([\w-]+):(.+[^@])@([\w\.-]+):(\d{3,5})\/([-\w]+) ([\w\.]+) ?(\w{3})? ([\+\-\d\.]+) ([\+\-\d\.]+) no (\w+)/i ) { push ( @mpts_def, { caster => $3, port => $4, mp => $5, ntripVers => $10 } ); } else { ERROR "$confFile: Could not parse mountPoints string $_" } } $conf{$section}->{'mountPoints_parsed'} = \@mpts_def; } elsif ( $key eq "cmbStreams" ) { my @cmbStrs = split ( /\s*,\s*/, $val ); foreach (@cmbStrs) { s/"//g; s/\s+$//; # entfernt alle whitespaces am Ende } $conf{$section}->{$key} = \@cmbStrs; } else { $conf{$section}->{$key} = $val } } my @nofPar = keys %conf; if ( scalar @nofPar < 1 ) { ERROR "No parameter found in BNC conf \"$confFile\""; return; } return \%conf; } # ============================================================================= # parseLogfile ($file, $sampling, $goBackSecs, $logMode ) # ============================================================================= # Parse BNCs' logfile # # Param : $file [required] BNC logfile # $sampling [optional] sampling rate for logfile # $logMode [optional] Flag. If set, remember the position of the file-read # for the next read. Default: off # Return : \%data # ============================================================================= sub parseLogfile { my $file = shift; my $sampling = shift // 1; my $logMode = shift // 0; open ( my $fh, "<", $file ) || LOGDIE "Could not open file $file: $!\n"; # Goto last position from last read #my $fPos = filePosition($file); #TRACE "Current file pos: $fPos"; $logMode && seek ( $fh, filePosition($file), 0 ); #$logMode && seek ( $fh, $fPos, 0 ); my $ln = ""; my ( @hlp, @epochs, @latencies, @restarts ); my $rec = {}; while (<$fh>) { chomp ( $ln = $_ ); $rec = {}; if ( $ln =~ /\bNEU/ ) { # NEU displacements @hlp = split ( /\s+/, $ln ); my $tp = Time::Piece->strptime( substr ( $hlp[2], 0, 19 ), '%Y-%m-%d_%H:%M:%S' ); if ( $hlp[14] eq '-nan' || $hlp[15] eq '-nan' || $hlp[16] eq '-nan' ) { WARN("$hlp[2] $hlp[3]: NEU displacements are NAN"); next; } #DEBUG ($tp->epoch, $hlp[3]); push ( @epochs, { time => $tp->epoch, site => $hlp[3], dN => $hlp[14], dE => $hlp[15], dU => $hlp[16], TRP => $hlp[18] + $hlp[19], } ); } elsif ( index ( $ln, "latency", 25 ) > 1 ) { # DEBUG ($ln); # altes format: 15-10-06 15:29:02 POTS0: Mean latency 2.34 sec, min 1.58, max 3, rms 0.48, 43 epochs, 17 gaps # neu in BNC 2.12.4: 17-06-06 15:35:02 OHI37 Observations: Mean latency 1.51 sec, min 0.57, max 2.7, rms 0.5, 203 epochs, 73 gaps @hlp = split ( /\s+/, $ln ); # old latency log format if ( $hlp[2] =~ /:$/ ) { splice @hlp, 3, 0, 'Placeholder:'; $hlp[2] =~ s/:$//; } $hlp[3] =~ s/:$//; my $tp = Time::Piece->strptime( "$hlp[0] $hlp[1]", '%y-%m-%d %H:%M:%S' ); $rec = { time => $tp->epoch, mp => $hlp[2], meanLat => $hlp[6] + 0.0, epochs => int ( $hlp[14] ), type => $hlp[3] }; # Unter bestimmten Bedingungen werden die gaps nicht rausgeschrieben! if ( $ln =~ /gaps/ ) { $rec->{'gaps'} = int ( $hlp[16] ); } push ( @latencies, $rec ); } elsif ( index ( $ln, "Start BNC" ) > 1 ) { # 17-06-13 07:06:58 ========== Start BNC v2.12.3 (LINUX) ========== @hlp = split ( /\s+/, $ln ); my $tp = Time::Piece->strptime( "$hlp[0] $hlp[1]", '%y-%m-%d %H:%M:%S' ); push ( @restarts, { time => $tp->epoch, bncvers => $hlp[5] } ); } } # ----- next line ----- $logMode && filePosition( $file, tell ($fh) ); # Remember pos for next read close $fh; # Sampling must be done afterwords, for each station separated! my @epochs_sampled; my @sites = map { $_->{'site'} } @epochs; #@sites = uniq @sites; my %hlp1 = (); @sites = grep { !$hlp1{$_}++ } @sites; foreach my $s (@sites) { my $epoch_selected = 0; foreach my $rec (@epochs) { next if ( $rec->{'site'} ne $s ); if ( $rec->{'time'} - $epoch_selected >= $sampling ) { push ( @epochs_sampled, $rec ); $epoch_selected = $rec->{'time'}; } } } my %data = ( EPOCHS => \@epochs_sampled, LATENCIES => \@latencies, RESTARTS => \@restarts ); return \%data; } # ============================================================================= # parsePPPLogfile ($file, $sampling, $goBackSecs, $logMode ) # ============================================================================= # Parse BNCs' PPP station logfile # # Param : $file [required] BNC PPP station # $sampling [optional] sampling rate for logfile # $goBackSecs [optional] go back that seconds from now in logfile # $logMode [optional] Flag. If set, remember the position of the file-read # for the next read. Default: off # Return : $station, \%data # ============================================================================= sub parsePPPLogfile { my $file = shift; my $sampling = shift // 1; my $goBackSecs = shift // 0; my $logMode = shift // 0; if ($logMode) { $goBackSecs = 0 } my $startSec; if ($goBackSecs) { $startSec = time () - $goBackSecs; } my $isFreePPP = 1; # Free or closed PPP version my $epo; my $old_epochSec = 0; my $epochSec = 0; my $epochDiff = 0; my ( @hlp, @EPOCHs, @N, @E, @U, %SATNUM, @TRPs, @CLKs, @G_CLKs, @R_CLKs, @E_CLKs, @C_CLKs, @OGRs, @OGEs, @OGCs, @OFFGLOs ); my ( @EPOCHs_OGE, @EPOCHs_OGR, @EPOCHs_OGC ); my ( @EPOCHs_G_CLK, @EPOCHs_R_CLK, @EPOCHs_E_CLK, @EPOCHs_C_CLK ); my ( %AMB, %RES, %ELE, %ION, %BIA ); my ( $station, $lki, $sys, $sat, $amb ); open ( my $fh, "<", $file ) || LOGDIE "Could not open file $file: $!\n"; # Goto last position from last read #my $fPos = filePosition($file); #TRACE "Current file pos: $fPos"; $logMode && seek ( $fh, filePosition($file), 0 ); #$logMode && seek ( $fh, $fPos, 0 ); my $ln = ""; while (<$fh>) { chomp ( $ln = $_ ); if ( $ln =~ /\bof Epoch\b/ ) { # PPP of Epoch 2015-08-27_14:00:15.000 if ( $ln =~ /PPP of Epoch (\d{4}-\d{2}-\d{2}_\d{2}:\d{2}:\d{2})\.\d+/ ) { # closed PPP $isFreePPP = 0; $epo = $1; #print "$epo\n"; } elsif ( $ln =~ /Point Positioning of Epoch (\d{4}-\d{2}-\d{2}_\d{2}:\d{2}:\d{2})\.\d+/ ) { # free PPP $isFreePPP = 1; $epo = $1; } else { ERROR "strange line: \"$ln\""; next } #my $date = sprintf ( "%s %s", split ( /_/, $epo ) ); my $tp = Time::Piece->strptime( $epo, '%Y-%m-%d_%H:%M:%S' ); $epochSec = $tp->epoch(); $epochDiff = $epochSec - $old_epochSec; next; } next if ( !$epo ); next if ( defined $startSec && $epochSec < $startSec ); next if ( $epochDiff && $epochDiff < $sampling ); @hlp = split ( /\s+/, $ln ); if ( $ln =~ /\bOFFGLO\b/ ) { # ... OFFGLO 8.417 +- 28.479 push ( @OFFGLOs, $hlp[2] ); } elsif ( $ln =~ /\bdN\b/ ) { push ( @EPOCHs, $epochSec ); # besser $epo ? $old_epochSec = $epochSec; #2015-08-27_13:59:50.000 DIEP1 X = 3842152.9054 +- 0.0242 Y = 563402.0331 +- 0.0176 Z = 5042888.5182 +- 0.0319 dN = 0.0130 +- 0.0193 dE = -0.0032 +- 0.0178 dU = -0.0248 +- 0.0349 $station = $hlp[1]; if ( $hlp[19] eq '-nan' || $hlp[24] eq '-nan' || $hlp[29] eq '-nan' ) { WARN("$hlp[0] $station: NEU displacements are NAN"); } push @N, $hlp[19]; push @E, $hlp[24]; push @U, $hlp[29]; } elsif ( ( $ln =~ /\bAMB\b/ ) && ( $ln !~ /RESET/ ) ) { if ($isFreePPP) { # 2015-10... AMB G05 -6.754 +- 0.086 nEpo = 633 $sat = $hlp[2]; $sys = substr ( $sat, 0, 1 ); $amb = $hlp[4]; push @{ $AMB{$sys}{$sat}{EPOCH} }, $epochSec; push @{ $AMB{$sys}{$sat}{DATA} }, $amb; push @{ $AMB{$sys}{$sat}{NUMEPO} }, $hlp[9]; } else { # 2015-08... AMB lIF G04 253.0000 -8.9924 +- 1.7825 el = 22.03 epo = 86 $lki = $hlp[2]; $sat = $hlp[3]; $sys = substr ( $sat, 0, 1 ); $amb = $hlp[4] + $hlp[5]; push @{ $AMB{$lki}{$sys}{$sat}{EPOCH} }, $epochSec; push @{ $AMB{$lki}{$sys}{$sat}{DATA} }, $amb; push @{ $AMB{$lki}{$sys}{$sat}{NUMEPO} }, $hlp[13]; push @{ $ELE{$sys}{$sat}{EPOCH} }, $epochSec; push @{ $ELE{$sys}{$sat}{DATA} }, $hlp[10]; } } elsif ( $ln =~ /\bRES\b/ && $ln !~ /Neglected/ ) { if ($isFreePPP) { # 2015-10... RES R08 L3 -0.0069 $sat = $hlp[2]; $lki = $hlp[3]; } else { # 2015-08... RES lIF G30 -0.0076 $sat = $hlp[3]; $lki = $hlp[2]; } $sys = substr ( $sat, 0, 1 ); #print "$epo $lki $sys $sat $res\n"; push @{ $RES{$lki}{$sys}{$sat}{EPOCH} }, $epochSec; push @{ $RES{$lki}{$sys}{$sat}{DATA} }, $hlp[4]; } elsif ( ( $ln =~ /\bION\b/ ) && ( $ln !~ /RESET/ ) ) { # 2018-12-01_20:37:58.000 ION G02 0.0000 -0.3277 +- 2.4663 $sat = $hlp[2]; $sys = substr ( $sat, 0, 1 ); push @{ $ION{$sys}{$sat}{EPOCH} }, $epochSec; push @{ $ION{$sys}{$sat}{DATA} }, $hlp[4]; } elsif ( ( $ln =~ /\bBIA\b/ ) && ( $ln !~ /RESET/ ) ) { # 2020-12-09_00:55:19.000 BIA c1 G 0.0000 +2.5149 +- 9.6543 $lki = $hlp[2]; $sys = $hlp[3]; push @{ $BIA{$lki}{$sys}{EPOCH} }, $epochSec; push @{ $BIA{$lki}{$sys}{DATA} }, $hlp[4] + $hlp[5]; } elsif ( $ln =~ /\bCLK\b/ ) { if ($isFreePPP) { push ( @CLKs, $hlp[2] ) } else { push ( @CLKs, $hlp[2] + $hlp[3] ) } } # REC_CLK in BNC 2.13 #elsif ( $ln =~ /\bREC_CLK\b/ ) { elsif ( $ln =~ /\bREC_CLK\s{8}/ ) { push ( @CLKs, $hlp[2] + $hlp[3] ); } elsif ( $ln =~ /\bREC_CLK G\b/ ) { push ( @EPOCHs_G_CLK, $epochSec ); push ( @G_CLKs, $hlp[3] + $hlp[4] ); } elsif ( $ln =~ /\bREC_CLK R\b/ ) { push ( @EPOCHs_R_CLK, $epochSec ); push ( @R_CLKs, $hlp[3] + $hlp[4] ); } elsif ( $ln =~ /\bREC_CLK E\b/ ) { push ( @EPOCHs_E_CLK, $epochSec ); push ( @E_CLKs, $hlp[3] + $hlp[4] ); } elsif ( $ln =~ /\bREC_CLK C\b/ ) { push ( @EPOCHs_C_CLK, $epochSec ); push ( @C_CLKs, $hlp[3] + $hlp[4] ); } elsif ( $ln =~ /\bOGR\b/ ) { # 2015-08... OGR 52.6806 -3.8042 +- 9.0077 push ( @EPOCHs_OGR, $epochSec ); push ( @OGRs, $hlp[2] + $hlp[3] ); # only https so far } elsif ( $ln =~ /\bOGE\b/ ) { # 2015-08... OGE 52.6806 -3.8042 +- 9.0077 push ( @EPOCHs_OGE, $epochSec ); push ( @OGEs, $hlp[2] + $hlp[3] ); # only https so far } elsif ( $ln =~ /\bOGC\b/ ) { # 2015-08... OGC 52.6806 -3.8042 +- 9.0077 push ( @EPOCHs_OGC, $epochSec ); push ( @OGCs, $hlp[2] + $hlp[3] ); # only https so far } elsif ( $ln =~ /\bSATNUM\b/ ) { # 2015-09... SATNUM G 8 push ( @{ $SATNUM{ $hlp[2] } }, $hlp[3] ); } elsif ( $ln =~ /\bTRP\b/ ) { # 2015-08... TRP 2.3803 +0.1009 +- 0.0324 push ( @TRPs, $hlp[2] + $hlp[3] ); } } # ----- next line ----- $logMode && filePosition( $file, tell ($fh) ); # Remember pos for next read close $fh; my $nof_epochs = scalar @EPOCHs; DEBUG( "epochs:$nof_epochs, North displac.: " . scalar @N . ", East displac.: " . scalar @E . ", Up displac.: " . scalar @U . ", TRPs:" . scalar @TRPs . ", CLKs:" . scalar @CLKs . ", OFFGLOs:" . scalar @OFFGLOs ); if ( $nof_epochs != scalar @N ) { LOGDIE "number of epochs and residuals not equal\n" } if ( $nof_epochs != scalar @TRPs ) { LOGDIE "number of epochs and TRPs not equal\n" } if ( @CLKs && $nof_epochs != scalar @CLKs ) { LOGDIE "number of epochs and CLKs not equal\n" } if ( @G_CLKs && scalar @EPOCHs_G_CLK != scalar @G_CLKs ) { LOGDIE "number of epochs and G_CLKs not equal\n" } if ( @R_CLKs && scalar @EPOCHs_R_CLK != scalar @R_CLKs ) { LOGDIE "number of epochs and R_CLKs not equal\n" } if ( @E_CLKs && scalar @EPOCHs_E_CLK != scalar @E_CLKs ) { LOGDIE "number of epochs and E_CLKs not equal\n" } if ( @C_CLKs && scalar @EPOCHs_C_CLK != scalar @C_CLKs ) { LOGDIE "number of epochs and C_CLKs not equal\n" } if ( @OGRs && scalar @EPOCHs_OGR != scalar @OGRs ) { LOGDIE "number of epochs and OGRs not equal\n" } if ( @OGEs && scalar @EPOCHs_OGE != scalar @OGEs ) { LOGDIE "number of epochs and OGEs not equal\n" } if ( @OGCs && scalar @EPOCHs_OGC != scalar @OGCs ) { LOGDIE "number of epochs and OGCs not equal\n" } my %data = ( EPOCHS => \@EPOCHs, N => \@N, E => \@E, U => \@U, SATNUM => \%SATNUM, TRPs => \@TRPs, CLKs => \@CLKs, G_CLKs => \@G_CLKs, R_CLKs => \@R_CLKs, E_CLKs => \@E_CLKs, C_CLKs => \@C_CLKs, OGRs => \@OGRs, OGEs => \@OGEs, OGCs => \@OGCs, OFFGLOs => \@OFFGLOs, RES => \%RES, AMB => \%AMB, ELE => \%ELE, ION => \%ION, BIA => \%BIA, ); return ( $station, \%data, $isFreePPP ); } # ============================================================================= # BncStillWorks ($bncConfFile) # ============================================================================= # Checks if BNC is still working. # # BNC Jobs can still be alive (in processlist) but are not producing any more. # This function checks if a BNC process is proper working. # # Param : $bncConfFile [required] path of BNC config file # Return : true if BNC is still working otherwise false. # ============================================================================= sub BncStillWorks { my ($bncConfFile) = @_; my $timep = Time::Piece->new; # for safety if it is exatly at 00:00, add 30 sec my $min_tmp = $timep->strftime("%M"); if ( $min_tmp =~ /00|15|30|45/ && $timep->strftime("%S") < 15 ) { $timep += 30; sleep 30; } my $yyyy = $timep->year; my $yy = $timep->yy; my $doy = sprintf "%03d", $timep->yday + 1; my $hh = $timep->strftime("%H"); my $h = uc ( chr ( 65 + $hh ) ); my $min = $timep->min; my $startmin; if ( $min < 15 ) { $startmin = "00" } elsif ( $min < 30 ) { $startmin = "15" } elsif ( $min < 45 ) { $startmin = "30" } elsif ( $min <= 59 ) { $startmin = "45" } my $bncConf = parseConf($bncConfFile); my $bncLogFileStub = $bncConf->{'General'}->{'logFile'}; # BNC log file # ------------ my $bncLogFile = "${bncLogFileStub}_" . $timep->strftime("%y%m%d"); # -> bnc.log_160425 unless ( -s $bncLogFile ) { WARN("BNC logfile \"$bncLogFile\" is empty or does not exist"); return 0; } # RINEX Obs Generation # -------------------- if ( $bncConf->{'General'}->{'rnxPath'} ) { my $rnxPath = $bncConf->{'General'}->{'rnxPath'}; $rnxPath =~ s/\/$//; # Write Rnx3 files (i.e. long Rnx3 filenames) 2: on ('rnxV3filenames' is deprecated since 2.12.8!!!) my $writeRnxV3 = $bncConf->{'General'}->{'rnxV3'}; my $rnxIntr = $bncConf->{'General'}->{'rnxIntr'}; my $fileMask; if ($writeRnxV3) { if ( $rnxIntr eq "1 hour" ) { $fileMask = "*_S_${yyyy}${doy}${hh}??_01H_30S_?O.rnx"; } elsif ( $rnxIntr eq "15 min" ) { $fileMask = "*_S_${yyyy}${doy}${hh}${startmin}_15M_01S_?O.rnx"; } else { # daily? $fileMask = "*_S_${yyyy}${doy}????_01D_30S_?O.rnx"; # HRAG00ZAF_S_20191220000_01D_30S_MO.rnx } } else { # Rnx2 if ( $rnxIntr eq "1 hour" ) { $fileMask = "????${doy}${h}.${yy}O"; } elsif ( $rnxIntr eq "15 min" ) { $fileMask = "????${doy}${h}${startmin}.${yy}O"; } else { # daily? $fileMask = "????${doy}*.${yy}O"; } } my @rnxFiles = glob "$rnxPath/$fileMask"; if ( scalar @rnxFiles < 1 ) { ERROR("BNC does not create RINEX Obs files. (Filemask: \"$fileMask\" Path: $rnxPath)"); #return 0; } } # RINEX Ephemerides Generation # ---------------------------- if ( $bncConf->{'General'}->{'ephPath'} ) { my $rnxPath = $bncConf->{'General'}->{'ephPath'}; $rnxPath =~ s/\/$//; my $writeRnxV3 = $bncConf->{'General'}->{'ephV3'}; my $rnxIntr = $bncConf->{'General'}->{'ephIntr'}; my $fileMask; if ($writeRnxV3) { if ( $rnxIntr eq "1 hour" ) { $fileMask = "BRD?00WRD_S_${yyyy}${doy}${hh}00_01H_?N.rnx"; } elsif ( $rnxIntr eq "15 min" ) { $fileMask = "BRD?00WRD_S_${yyyy}${doy}${hh}${startmin}_15M_?N.rnx"; # BRDC00WRD_S_20191220900_15M_MN.rnx } else { # daily? $fileMask = $fileMask = "BRD?00WRD_S_${yyyy}${doy}0000_01D_?N.rnx"; } } else { # Rnx2 $fileMask = "BRD?${doy}*.${yy}N"; } my @rnxFiles = glob "$rnxPath/$fileMask"; if ( scalar @rnxFiles < 1 ) { ERROR("BNC does not create RINEX Nav files. (Filemask: \"$fileMask\" Path: $rnxPath)"); #return 0; } } # Check jobs making PPP # --------------------- if ( $bncConf->{'PPP'}->{'corrMount'} && $bncConf->{'PPP'}->{'staTable'} ) { my $timeOfLastCoo = `grep "NEU:" $bncLogFile | tail -1 | cut -d ' ' -f1,2`; chomp $timeOfLastCoo; if ( !$timeOfLastCoo ) { ERROR "BNC does not compute coordinates"; return 0; } my $tp = Time::Piece->strptime( $timeOfLastCoo, '%y-%m-%d %H:%M:%S' ); my $now = Time::Piece->new; my $tdiff = $now - $tp; if ( $tdiff > 1200 ) { ERROR( "Last computed coordinates are " . $tdiff / 60 . " min old" ); return 0; } } # BNC works return 1; } 1; # End of Bnc