source: ntrip/trunk/BNC/scripts/Bnc.pm@ 10481

Last change on this file since 10481 was 10388, checked in by stuerze, 9 months ago

changes regarding PPP

  • Property svn:keywords set to Header
File size: 29.0 KB
Line 
1package Bnc;
2
3# Perl utility functions for BNC
4#
5# Revision: $Header: trunk/BNC/scripts/Bnc.pm 10388 2024-03-12 14:10:17Z stuerze $
6
7use strict;
8use warnings;
9use File::Basename;
10use File::Spec::Functions qw(catfile);
11use File::Temp qw(tempfile);
12use Exporter;
13use Time::Piece 1.30;
14use PDL::Lite; # to avoid namespace pollution
15use PDL::Primitive;
16use Log::Log4perl qw(:easy);
17
18# use List::MoreUtils qw(uniq); # Prototype mismatch with PDL (uniq)
19
20# =============================================================================
21# callBnc ($bnc_ini, %$opts_ref)
22# =============================================================================
23# Call BNC
24#
25# Param : $bnc_ini [optional] The BNC config file that should be used. If not set, the default config is ised.
26# $opts_ref [required] Hash with BNC config options as key. They will overwrite the settings from the config file.
27# Return : BNC exit status
28# =============================================================================
29sub callBnc {
30 my ( $bnc, $bnc_ini, $opts ) = @_;
31
32 my $config_file = "";
33 if ( $bnc_ini && -s $bnc_ini ) {
34 $config_file = "--conf $bnc_ini";
35 }
36 else {
37 DEBUG("callBnc: Use default bnc-ini file");
38 }
39
40 my $opts_str = "";
41 if ($opts) { $opts_str = options2string($opts) }
42
43 # my @cmd = (
44 # 'xvfb-run',
45 # "--server-args='-screen 0, 1280x1024x8'", # 1024x768x24
46 # "$bnc",
47 # "--nw",
48 # $opts_str,
49 # );
50
51#my $rc = call_system("xvfb-run -a -e /home/user/xvfb.err --server-args='-screen 0 1280x1024x8' $bnc --nw $config_file $opts_str");
52 return Common::runCmd("$bnc --nw $config_file $opts_str");
53}
54
55# Converts options map to string used for calling BNC.
56sub options2string {
57 my ($opts) = shift;
58
59 my $opts_str = "";
60 if ($opts) {
61 foreach my $key ( keys %{$opts} ) {
62 $opts_str .= "--key $key" . ' "' . $opts->{$key} . '" ';
63 }
64 }
65 return $opts_str;
66}
67
68# =============================================================================
69# parseMessageTypesFromFile
70# =============================================================================
71# Parse Message-Types with repetition rate from a BNC/scanRTCM logfile
72#
73# Param : $logfile [required] Path of the BNC/scanRTCM logfile
74# $caAbbr [optional] caster name or abbreviation
75# $tmpPath [optional] write working files to this path
76#
77# Return : Message-types for each mountpoint (as Hash-Ref)
78# Hash with mp as key and the list of messTypes as value
79# Example: $VAR1 = { 'RIO10' => ['1004(1)','1006(15)','1008(15)',...],
80# 'CLIB0' => ['1004(1)','1006(10)','1008(10)',...],
81# };
82# =============================================================================
83sub parseMessageTypesFromFile {
84 my ( $logfile, $caAbbr, $mytmpPath ) = @_;
85
86 unless ( -s $logfile ) {
87 ERROR "File [$logfile] is empty or does not exist";
88 return;
89 }
90
91 my $tmp = File::Temp->new( UNLINK => 1, SUFFIX => '.messtyps' );
92 my $casterMessTypeFile = $tmp->filename;
93
94 #my $caster = $logfilename =~ s/\.log\.messtyps//r;
95 #INFO "Process caster '$caster'";
96
97 INFO "Process $logfile";
98
99 # scan beginns with that line: 16-02-18 23:58:02 WTZ37: Get data in RTCM 3.x format
100
101 # First grep for message type lines in bnc-logfile and write them to local temp. directory
102 # NOTE: 'sort -u' because Ephemeries message-types 1019 (GPS), 1020 (GLONASS), 1045 (Galileo) come
103 # for every sat. at the same time/second. This is done for getting the repetition rate
104 system ("grep \"Received message type\" $logfile | sort -u > $casterMessTypeFile") == 0
105 or ERROR "Fehler: $!";
106
107 # --------------------------------------------
108 # Get message types foreach station/mountpoint
109 # --------------------------------------------
110 my $cmd = "cat $casterMessTypeFile | awk '{print \$3, \$7}' | sort -u";
111 my @messTypes = `$cmd`; # ['DARX1: 1004\n','DARX1: 1006\n',... ]
112 if ( scalar @messTypes < 1 ) {
113 ERROR "Could not retrieve message types from file $casterMessTypeFile";
114 return;
115 }
116
117 # Note: Skip first 1500 lines because BNC is weird here, all lines with same timestamp, buffer problem?
118
119 # ------------------------------------------------------
120 # Guess repetition rate for each mountpoint/message-type
121 # ------------------------------------------------------
122 # For that get the first $minSamples appearances of mountp with same message type
123 # and build the differences between them
124 my $minSamples = 4;
125 foreach my $mpType (@messTypes) {
126 chomp $mpType; # 'AUBG3: 1004'
127 my ( $mp, $mt ) = split ( /:\s*/, $mpType );
128 my $cmd = "grep \"$mp: Received message type $mt\" $logfile";
129 my @rows = `$cmd`;
130 if ( scalar @rows < 1 ) {
131 ERROR "Could not retrieve message types for $mpType from file $casterMessTypeFile";
132 next;
133 }
134
135 if ( scalar @rows <= $minSamples ) {
136 WARN "Could not guess repetition rate for message type $mpType: only " . scalar @rows . " matches found";
137 next;
138 }
139
140 my $repRate = _computeMessTypRepetitonRate( \@rows, $caAbbr );
141 if ($repRate) {
142 $mpType .= '(' . $repRate . ')';
143 }
144 } # ----- end foreach messageType -----
145
146 #if ( unlink $casterMessTypeFile ) { TRACE "Removed file [$casterMessTypeFile]" }
147
148 # Return an HASH OF ARRAYS with mp as key and the list of messTypes as value
149 my %messTypesHash;
150 foreach (@messTypes) {
151 my @ele = split ( ': ', $_ );
152 my $mp = shift @ele;
153 push ( @{ $messTypesHash{$mp} }, shift @ele );
154 }
155
156 return \%messTypesHash;
157}
158
159# =============================================================================
160# _computeMessTypRepetitonRate
161# =============================================================================
162# Guess repetition rate of message types
163#
164# Param : $firstMatches [required] Array-Ref with first appearances of station
165# and mess type. Complete logfile lines, e.g.
166# '13-07-04 17:38:26 BRUX0: Received message type 1004 '
167# $caAbbr [optional] caster abbreviation
168# Return : repetition rate in secs, (median value)
169# =============================================================================
170sub _computeMessTypRepetitonRate {
171 my ( $rows, $caAbbr ) = @_;
172
173 my $maxGap = 600; # if gap > 10min then we guess it is a new scan
174
175 $rows->[0] =~ /.{18}([a-x0-9]+): Received message type (\d+)/i;
176 my $stat = $1;
177 my $mestp = $2;
178
179 # Create list of unix timestamps
180 my @scans;
181 my $scan = 0;
182 my ( $prevTime, $deltaT ) = ( 0, 0 );
183 foreach (@$rows) {
184 my $uxtime = date2unix( substr ( $_, 0, 17 ) );
185 if ( !$uxtime ) {
186 ERROR "Could not parse date from line $_";
187 next;
188 }
189 $deltaT = $uxtime - $prevTime;
190 next if ( $deltaT == 0 ); # e.g. eph 1019,1020 one message for each sat.
191 next if ( $deltaT <= 1 && $mestp =~ /10(19|20|42|43|44|45|46)|63/ );
192 if ( $prevTime && $deltaT > $maxGap ) {
193 $scan++;
194 }
195 push ( @{ $scans[$scan] }, $uxtime );
196 $prevTime = $uxtime;
197 }
198
199 my @repRates;
200 my $highest_nof_diffs = 0;
201 foreach (@scans) {
202 my @timestamps = @{$_};
203
204 # Compute the differences
205 my @diffs;
206 for ( my $i = 1; $i <= $#timestamps; $i++ ) {
207 push ( @diffs, $timestamps[$i] - $timestamps[ $i - 1 ] );
208 }
209 my $nof_diffs = scalar @diffs;
210
211 if ( $nof_diffs < 2 ) {
212 WARN("$stat: $mestp: only $nof_diffs diffs");
213 next;
214 }
215
216 my ( $mean, $prms, $median, $min, $max, $adev, $rms_n ) = stats( pdl \@diffs );
217 $mean = sprintf ( "%.0f", $mean );
218 $rms_n = sprintf ( "%.02f", $rms_n );
219 print $stat, ": ", $mestp, ": ", join ( ' ', @diffs ), "[Sig: $mean, $rms_n]\n";
220
221 if ( $rms_n > 10 ) {
222 WARN("$stat: $mestp: RMS too high: $rms_n");
223 next;
224 }
225
226 # get the most frequent value
227 my %counti = ();
228 $counti{$_}++ foreach (@diffs);
229 my ( $ni, $mfv ) = ( 0, 0 );
230 while ( my ( $k, $v ) = each %counti ) {
231 if ( $v > $ni ) {
232 $ni = $v;
233 $mfv = $k;
234 }
235 }
236
237 my $rounded_val = $mfv; # init
238 foreach ( ( 1, 5, 10, 15, 30, 60, 120, 150, 300 ) ) { # most likely values
239 my $mdiff = abs ( $mfv - $_ );
240 if ( $mdiff <= 2 ) {
241 $rounded_val = $_;
242 }
243 }
244 push ( @repRates, [ $rounded_val, $nof_diffs ] );
245
246 if ( $nof_diffs > $highest_nof_diffs ) {
247 $highest_nof_diffs = $nof_diffs;
248 }
249 } # ----- end foreach scan -----
250
251 my @mostLikelyRates = grep { $_->[1] == $highest_nof_diffs } @repRates;
252 my $mostLikelyRate = $mostLikelyRates[0]->[0];
253 foreach (@repRates) {
254 if ( abs ( $_->[0] - $mostLikelyRate ) > 2 ) {
255 ERROR "$stat: $caAbbr: $mestp: repetition rates from different scans differ: $mostLikelyRate $_->[0]";
256 if ( scalar @repRates == 2 ) {
257 return;
258 }
259 }
260 }
261
262 return $mostLikelyRate;
263}
264
265# =============================================================================
266# parseConfig ($confFile)
267# =============================================================================
268# Parse the BNC config file.
269#
270# Param : $confFile [required] BNC config file
271# Return : Hash with configuration on success, otherwise undef
272# Usage : $bncConf = parseConf($bncConfFile);
273# $corrMount = $bncConf->{'PPP'}->{'corrMount'};
274# =============================================================================
275sub parseConfig {
276 my ($confFile) = @_;
277
278 -s $confFile || LOGDIE "BNC config file \"$confFile\" does not exist\n";
279 TRACE "Parse BNC config file $confFile";
280 open ( my $INP, '<', $confFile ) || die "Could not open file '$confFile': $!";
281 my @confLines = <$INP>;
282 close ($INP);
283
284 my %conf;
285 my $section; # [General], [PPP]
286 foreach (@confLines) {
287 chomp;
288 s/#.*//; # entfernt Kommentare
289 s/^\s*//; # whitespace am Anfang entfernen
290 s/\s+$//; # entfernt alle whitespaces am Ende
291 next unless length;
292 if ( $_ =~ /\[(\S+)\]/ ) { $section = $1 }
293 next if ( !$section );
294 my ( $key, $val ) = split ( /\s*=\s*/, $_, 2 );
295 if ( !defined $val ) { $val = "" }
296
297 if ( $key eq "mountPoints" ) {
298
299 # Simple parsing
300 $val =~ s/^\/\///;
301 my @mpts = split ( /,\s?\/{2}/, $val );
302 $conf{$section}->{$key} = \@mpts;
303
304 # Extended parsing
305 my @mpts_def = ();
306 foreach (@mpts) {
307
308 # user:passwd@igs-ip.net:2101/ASPA0 RTCM_3.0 ASM -14.33 189.28 no 1
309 if ( $_ =~
310 /^([\w-]+):(.+[^@])@([\w\.-]+):(\d{3,5})\/([-\w]+) ([\w\.]+) ?(\w{3})? ([\+\-\d\.]+) ([\+\-\d\.]+) no (\w+)/i
311 )
312 {
313 push ( @mpts_def, { caster => $3, port => $4, mp => $5, ntripVers => $10 } );
314 }
315 else { ERROR "$confFile: Could not parse mountPoints string $_" }
316 }
317 $conf{$section}->{'mountPoints_parsed'} = \@mpts_def;
318 }
319 elsif ( $key eq "cmbStreams" ) {
320 my @cmbStrs = split ( /\s*,\s*/, $val );
321 foreach (@cmbStrs) {
322 s/"//g;
323 s/\s+$//; # entfernt alle whitespaces am Ende
324 }
325 $conf{$section}->{$key} = \@cmbStrs;
326 }
327 else { $conf{$section}->{$key} = $val }
328 }
329
330 my @nofPar = keys %conf;
331 if ( scalar @nofPar < 1 ) {
332 ERROR "No parameter found in BNC conf \"$confFile\"";
333 return;
334 }
335 return \%conf;
336}
337
338# =============================================================================
339# parseLogfile ($file, $sampling, $goBackSecs, $logMode )
340# =============================================================================
341# Parse BNCs' logfile
342#
343# Param : $file [required] BNC logfile
344# $sampling [optional] sampling rate for logfile
345# $logMode [optional] Flag. If set, remember the position of the file-read
346# for the next read. Default: off
347# Return : \%data
348# =============================================================================
349sub parseLogfile {
350 my $file = shift;
351 my $sampling = shift // 1;
352 my $logMode = shift // 0;
353
354 open ( my $fh, "<", $file ) || LOGDIE "Could not open file $file: $!\n";
355
356 # Goto last position from last read
357 #my $fPos = filePosition($file);
358 #TRACE "Current file pos: $fPos";
359 $logMode && seek ( $fh, filePosition($file), 0 );
360
361 #$logMode && seek ( $fh, $fPos, 0 );
362 my $ln = "";
363 my ( @hlp, @epochs, @latencies, @restarts );
364 my $rec = {};
365 while (<$fh>) {
366 chomp ( $ln = $_ );
367 $rec = {};
368
369 if ( $ln =~ /\bNEU/ ) { # NEU displacements
370 @hlp = split ( /\s+/, $ln );
371 my $tp = Time::Piece->strptime( substr ( $hlp[2], 0, 19 ), '%Y-%m-%d_%H:%M:%S' );
372
373 if ( $hlp[14] eq '-nan' || $hlp[15] eq '-nan' || $hlp[16] eq '-nan' ) {
374 WARN("$hlp[2] $hlp[3]: NEU displacements are NAN");
375 next;
376 }
377
378 #DEBUG ($tp->epoch, $hlp[3]);
379 push (
380 @epochs,
381 {
382 time => $tp->epoch,
383 site => $hlp[3],
384 dN => $hlp[14],
385 dE => $hlp[15],
386 dU => $hlp[16],
387 TRP => $hlp[18] + $hlp[19],
388 }
389 );
390 }
391 elsif ( index ( $ln, "latency", 25 ) > 1 ) {
392
393# DEBUG ($ln);
394# 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
395# 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
396 @hlp = split ( /\s+/, $ln );
397
398 # old latency log format
399 if ( $hlp[2] =~ /:$/ ) {
400 splice @hlp, 3, 0, 'Placeholder:';
401 $hlp[2] =~ s/:$//;
402 }
403 $hlp[3] =~ s/:$//;
404
405 my $tp = Time::Piece->strptime( "$hlp[0] $hlp[1]", '%y-%m-%d %H:%M:%S' );
406 $rec = {
407 time => $tp->epoch,
408 mp => $hlp[2],
409 meanLat => $hlp[6] + 0.0,
410 epochs => int ( $hlp[14] ),
411 type => $hlp[3]
412 };
413
414 # Unter bestimmten Bedingungen werden die gaps nicht rausgeschrieben!
415 if ( $ln =~ /gaps/ ) {
416 $rec->{'gaps'} = int ( $hlp[16] );
417 }
418
419 push ( @latencies, $rec );
420 }
421 elsif ( index ( $ln, "Start BNC" ) > 1 ) {
422
423 # 17-06-13 07:06:58 ========== Start BNC v2.12.3 (LINUX) ==========
424 @hlp = split ( /\s+/, $ln );
425 my $tp = Time::Piece->strptime( "$hlp[0] $hlp[1]", '%y-%m-%d %H:%M:%S' );
426 push (
427 @restarts,
428 {
429 time => $tp->epoch,
430 bncvers => $hlp[5]
431 }
432 );
433 }
434
435 } # ----- next line -----
436
437 $logMode && filePosition( $file, tell ($fh) ); # Remember pos for next read
438 close $fh;
439
440 # Sampling must be done afterwords, for each station separated!
441 my @epochs_sampled;
442 my @sites = map { $_->{'site'} } @epochs;
443
444 #@sites = uniq @sites;
445 my %hlp1 = ();
446 @sites = grep { !$hlp1{$_}++ } @sites;
447 foreach my $s (@sites) {
448 my $epoch_selected = 0;
449 foreach my $rec (@epochs) {
450 next if ( $rec->{'site'} ne $s );
451 if ( $rec->{'time'} - $epoch_selected >= $sampling ) {
452 push ( @epochs_sampled, $rec );
453 $epoch_selected = $rec->{'time'};
454 }
455 }
456 }
457
458 my %data = (
459 EPOCHS => \@epochs_sampled,
460 LATENCIES => \@latencies,
461 RESTARTS => \@restarts
462 );
463
464 return \%data;
465}
466
467# =============================================================================
468# parsePPPLogfile ($file, $sampling, $goBackSecs, $logMode )
469# =============================================================================
470# Parse BNCs' PPP station logfile
471#
472# Param : $file [required] BNC PPP station
473# $sampling [optional] sampling rate for logfile
474# $goBackSecs [optional] go back that seconds from now in logfile
475# $logMode [optional] Flag. If set, remember the position of the file-read
476# for the next read. Default: off
477# Return : $station, \%data
478# =============================================================================
479sub parsePPPLogfile {
480 my $file = shift;
481 my $sampling = shift // 1;
482 my $goBackSecs = shift // 0;
483 my $logMode = shift // 0;
484
485 if ($logMode) { $goBackSecs = 0 }
486
487 my $startSec;
488 if ($goBackSecs) {
489 $startSec = time () - $goBackSecs;
490 }
491 my $epo;
492 my $old_epochSec = 0;
493 my $epochSec = 0;
494 my $epochDiff = 0;
495 my ( @hlp, @N, @E, @U, %SATNUM, @TRP, @CLK, @OFF_GPS, @OFF_GLO, @OFF_GAL, @OFF_BDS);
496 my ( @EPOCH, @EPOCH_CLK, @EPOCH_OFF_GPS, @EPOCH_OFF_GLO, @EPOCH_OFF_GAL, @EPOCH_OFF_BDS );
497 my ( %AMB, %RES, %ELE, %ION, %BIA );
498 my ( $station, $lki, $sys, $sat, $amb );
499 open ( my $fh, "<", $file ) || LOGDIE "Could not open file $file: $!\n";
500
501 # Goto last position from last read
502 #my $fPos = filePosition($file);
503 #TRACE "Current file pos: $fPos";
504 $logMode && seek ( $fh, filePosition($file), 0 );
505
506 #$logMode && seek ( $fh, $fPos, 0 );
507 my $ln = "";
508 while (<$fh>) {
509 chomp ( $ln = $_ );
510
511 if ( $ln =~ /\bof Epoch\b/ ) {
512
513 # PPP of Epoch 2015-08-27_14:00:15.000
514 if ( $ln =~ /PPP of Epoch (\d{4}-\d{2}-\d{2}_\d{2}:\d{2}:\d{2})\.\d+/ ) {
515 $epo = $1; #print "$epo\n";
516 }
517 else { ERROR "strange line: \"$ln\""; next }
518
519 my $tp = Time::Piece->strptime( $epo, '%Y-%m-%d_%H:%M:%S' );
520 $epochSec = $tp->epoch();
521 $epochDiff = $epochSec - $old_epochSec;
522 next;
523 }
524
525 next if ( !$epo );
526 next if ( defined $startSec && $epochSec < $startSec );
527 next if ( $epochDiff && $epochDiff < $sampling );
528
529 @hlp = split ( /\s+/, $ln );
530
531 if ( $ln =~ /\bdN\b/ ) {
532 push ( @EPOCH, $epochSec ); # besser $epo ?
533 $old_epochSec = $epochSec;
534
535 #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
536 $station = $hlp[1];
537
538 if ( $hlp[19] eq '-nan' || $hlp[24] eq '-nan' || $hlp[29] eq '-nan' ) {
539 WARN("$hlp[0] $station: NEU displacements are NAN");
540 }
541
542 push @N, $hlp[19];
543 push @E, $hlp[24];
544 push @U, $hlp[29];
545 }
546 elsif ( ( $ln =~ /\bAMB\b/ ) && ( $ln !~ /RESET/ ) ) {
547
548 # 2015-08... AMB lIF G04 253.0000 -8.9924 +- 1.7825 el = 22.03 epo = 86
549 $lki = $hlp[2];
550 $sat = $hlp[3];
551 $sys = substr ( $sat, 0, 1 );
552 $amb = $hlp[4] + $hlp[5];
553 push @{ $AMB{$lki}{$sys}{$sat}{EPOCH} }, $epochSec;
554 push @{ $AMB{$lki}{$sys}{$sat}{DATA} }, $amb;
555 push @{ $AMB{$lki}{$sys}{$sat}{NUMEPO} }, $hlp[13];
556 push @{ $ELE{$sys}{$sat}{EPOCH} }, $epochSec;
557 push @{ $ELE{$sys}{$sat}{DATA} }, $hlp[10];
558 }
559 elsif ( $ln =~ /\bRES\b/ && $ln !~ /Neglected/ ) {
560
561 # 2015-08... RES lIF G30 -0.0076
562 $sat = $hlp[3];
563 $lki = $hlp[2];
564 $sys = substr ( $sat, 0, 1 );
565
566 #print "$epo $lki $sys $sat $res\n";
567 push @{ $RES{$lki}{$sys}{$sat}{EPOCH} }, $epochSec;
568 push @{ $RES{$lki}{$sys}{$sat}{DATA} }, $hlp[4];
569 }
570 elsif ( ( $ln =~ /\bION\b/ ) && ( $ln !~ /RESET/ ) ) {
571
572 # 2018-12-01_20:37:58.000 ION G02 0.0000 -0.3277 +- 2.4663
573 $sat = $hlp[2];
574 $sys = substr ( $sat, 0, 1 );
575 push @{ $ION{$sys}{$sat}{EPOCH} }, $epochSec;
576 push @{ $ION{$sys}{$sat}{DATA} }, $hlp[4];
577 }
578 elsif ( ( $ln =~ /\bBIA\b/ ) && ( $ln !~ /RESET/ ) ) {
579
580 # 2020-12-09_00:55:19.000 BIA c1 G 0.0000 +2.5149 +- 9.6543
581 $lki = $hlp[2];
582 $sys = $hlp[3];
583 push @{ $BIA{$lki}{$sys}{EPOCH} }, $epochSec;
584 push @{ $BIA{$lki}{$sys}{DATA} }, $hlp[4] + $hlp[5];
585 }
586 elsif ( $ln =~ /\bREC_CLK\b/ ) {
587 push ( @EPOCH_CLK, $epochSec );
588 push ( @CLK, $hlp[2] + $hlp[3] );
589 }
590 elsif ( $ln =~ /\bOFF_GPS\b/ ) { # 2015-08... OFF_GPS 52.6806 -3.8042 +- 9.0077
591 push ( @EPOCH_OFF_GPS, $epochSec );
592 push ( @OFF_GPS, $hlp[2] + $hlp[3] );
593 }
594 elsif ( $ln =~ /\bOFF_GLO\b/ ) { # 2015-08... OFF_GLO 52.6806 -3.8042 +- 9.0077
595 push ( @EPOCH_OFF_GLO, $epochSec );
596 push ( @OFF_GLO, $hlp[2] + $hlp[3] );
597 }
598 elsif ( $ln =~ /\bOFF_GAL\b/ ) { # 2015-08... OFF_GAL 52.6806 -3.8042 +- 9.0077
599 push ( @EPOCH_OFF_GAL, $epochSec );
600 push ( @OFF_GAL, $hlp[2] + $hlp[3] );
601 }
602 elsif ( $ln =~ /\bOFF_BDS\b/ ) { # 2015-08... OFF_BDS 52.6806 -3.8042 +- 9.0077
603 push ( @EPOCH_OFF_BDS, $epochSec );
604 push ( @OFF_BDS, $hlp[2] + $hlp[3] );
605 }
606 elsif ( $ln =~ /\bSATNUM\b/ ) { # 2015-09... SATNUM G 8
607 push ( @{ $SATNUM{ $hlp[2] } }, $hlp[3] );
608 }
609 elsif ( $ln =~ /\bTRP\b/ ) { # 2015-08... TRP 2.3803 +0.1009 +- 0.0324
610 push ( @TRP, $hlp[2] + $hlp[3] );
611 }
612 } # ----- next line -----
613
614 $logMode && filePosition( $file, tell ($fh) ); # Remember pos for next read
615 close $fh;
616
617 my $nof_epochs = scalar @EPOCH;
618 DEBUG( "$station: epochs:$nof_epochs, North displac.: "
619 . scalar @N
620 . ", East displac.: "
621 . scalar @E
622 . ", Up displac.: "
623 . scalar @U
624 . ", TRP:"
625 . scalar @TRP
626 );
627 if ( $nof_epochs != scalar @N ) { LOGDIE "number of epochs and residuals not equal\n" }
628 if ( $nof_epochs != scalar @TRP ) { LOGDIE "number of epochs and TRP not equal\n" }
629 if ( @CLK && scalar @EPOCH_CLK != scalar @CLK ) { LOGDIE "number of epochs and CLK not equal\n" }
630 if ( @OFF_GPS && scalar @EPOCH_OFF_GPS != scalar @OFF_GPS ) { LOGDIE "number of epochs and OFF_GPS not equal\n" }
631 if ( @OFF_GLO && scalar @EPOCH_OFF_GLO != scalar @OFF_GLO ) { LOGDIE "number of epochs and OFF_GLO not equal\n" }
632 if ( @OFF_GAL && scalar @EPOCH_OFF_GAL != scalar @OFF_GAL ) { LOGDIE "number of epochs and OFF_GAL not equal\n" }
633 if ( @OFF_BDS && scalar @EPOCH_OFF_BDS != scalar @OFF_BDS ) { LOGDIE "number of epochs and OFF_BDS not equal\n" }
634
635 if ( !$station ) { WARN "could not grep stationname from file: $file\n" }
636
637 my %data = (
638 EPOCH => \@EPOCH,
639 N => \@N,
640 E => \@E,
641 U => \@U,
642 SATNUM => \%SATNUM,
643 TRP => \@TRP,
644 CLK => \@CLK,
645 OFF_GPS => \@OFF_GPS,
646 OFF_GLO => \@OFF_GLO,
647 OFF_GAL => \@OFF_GAL,
648 OFF_BDS => \@OFF_BDS,
649 RES => \%RES,
650 AMB => \%AMB,
651 ELE => \%ELE,
652 ION => \%ION,
653 BIA => \%BIA,
654 );
655
656 return ( $station, \%data, 0 );
657}
658
659# =============================================================================
660# BncStillWorks ($bncConfFile)
661# =============================================================================
662# Checks if BNC is still working.
663#
664# BNC Jobs can still be alive (in processlist) but are not producing any more.
665# This function checks if a BNC process is proper working.
666#
667# Param : $bncConfFile [required] path of BNC config file
668# Return : true if BNC is still working otherwise false.
669# =============================================================================
670sub BncStillWorks {
671 my ($bncConfFile) = @_;
672
673 my $timep = Time::Piece->new;
674
675 # for safety if it is exatly at 00:00, add 30 sec
676 my $min_tmp = $timep->strftime("%M");
677 if ( $min_tmp =~ /00|15|30|45/ && $timep->strftime("%S") < 15 ) {
678 $timep += 30;
679 sleep 30;
680 }
681 my $yyyy = $timep->year;
682 my $yy = $timep->yy;
683 my $doy = sprintf "%03d", $timep->yday + 1;
684 my $hh = $timep->strftime("%H");
685 my $h = uc ( chr ( 65 + $hh ) );
686 my $min = $timep->min;
687 my $startmin;
688 if ( $min < 15 ) { $startmin = "00" }
689 elsif ( $min < 30 ) { $startmin = "15" }
690 elsif ( $min < 45 ) { $startmin = "30" }
691 elsif ( $min <= 59 ) { $startmin = "45" }
692 my $bncConf = parseConf($bncConfFile);
693 my $bncLogFileStub = $bncConf->{'General'}->{'logFile'};
694
695 # BNC log file
696 # ------------
697 my $bncLogFile = "${bncLogFileStub}_" . $timep->strftime("%y%m%d"); # -> bnc.log_160425
698 unless ( -s $bncLogFile ) {
699 WARN("BNC logfile \"$bncLogFile\" is empty or does not exist");
700 return 0;
701 }
702
703 # RINEX Obs Generation
704 # --------------------
705 if ( $bncConf->{'General'}->{'rnxPath'} ) {
706 my $rnxPath = $bncConf->{'General'}->{'rnxPath'};
707 $rnxPath =~ s/\/$//;
708
709 # Write Rnx3 files (i.e. long Rnx3 filenames) 2: on ('rnxV3filenames' is deprecated since 2.12.8!!!)
710 my $writeRnxV3 = $bncConf->{'General'}->{'rnxV3'};
711 my $rnxIntr = $bncConf->{'General'}->{'rnxIntr'};
712 my $fileMask;
713
714 if ($writeRnxV3) {
715 if ( $rnxIntr eq "1 hour" ) {
716 $fileMask = "*_S_${yyyy}${doy}${hh}??_01H_30S_?O.rnx";
717 }
718 elsif ( $rnxIntr eq "15 min" ) {
719 $fileMask = "*_S_${yyyy}${doy}${hh}${startmin}_15M_01S_?O.rnx";
720 }
721 else { # daily?
722 $fileMask = "*_S_${yyyy}${doy}????_01D_30S_?O.rnx"; # HRAG00ZAF_S_20191220000_01D_30S_MO.rnx
723 }
724 }
725 else { # Rnx2
726 if ( $rnxIntr eq "1 hour" ) {
727 $fileMask = "????${doy}${h}.${yy}O";
728 }
729 elsif ( $rnxIntr eq "15 min" ) {
730 $fileMask = "????${doy}${h}${startmin}.${yy}O";
731 }
732 else { # daily?
733 $fileMask = "????${doy}*.${yy}O";
734 }
735 }
736
737 my @rnxFiles = glob "$rnxPath/$fileMask";
738 if ( scalar @rnxFiles < 1 ) {
739 ERROR("BNC does not create RINEX Obs files. (Filemask: \"$fileMask\" Path: $rnxPath)");
740
741 #return 0;
742 }
743 }
744
745 # RINEX Ephemerides Generation
746 # ----------------------------
747 if ( $bncConf->{'General'}->{'ephPath'} ) {
748 my $rnxPath = $bncConf->{'General'}->{'ephPath'};
749 $rnxPath =~ s/\/$//;
750 my $writeRnxV3 = $bncConf->{'General'}->{'ephV3'};
751 my $rnxIntr = $bncConf->{'General'}->{'ephIntr'};
752 my $fileMask;
753
754 if ($writeRnxV3) {
755 if ( $rnxIntr eq "1 hour" ) {
756 $fileMask = "BRD?00WRD_S_${yyyy}${doy}${hh}00_01H_?N.rnx";
757 }
758 elsif ( $rnxIntr eq "15 min" ) {
759 $fileMask = "BRD?00WRD_S_${yyyy}${doy}${hh}${startmin}_15M_?N.rnx"; # BRDC00WRD_S_20191220900_15M_MN.rnx
760 }
761 else { # daily?
762 $fileMask = $fileMask = "BRD?00WRD_S_${yyyy}${doy}0000_01D_?N.rnx";
763 }
764 }
765 else { # Rnx2
766 $fileMask = "BRD?${doy}*.${yy}N";
767 }
768
769 my @rnxFiles = glob "$rnxPath/$fileMask";
770 if ( scalar @rnxFiles < 1 ) {
771 ERROR("BNC does not create RINEX Nav files. (Filemask: \"$fileMask\" Path: $rnxPath)");
772
773 #return 0;
774 }
775 }
776
777 # Check jobs making PPP
778 # ---------------------
779 if ( $bncConf->{'PPP'}->{'corrMount'} && $bncConf->{'PPP'}->{'staTable'} ) {
780 my $timeOfLastCoo = `grep "NEU:" $bncLogFile | tail -1 | cut -d ' ' -f1,2`;
781 chomp $timeOfLastCoo;
782 if ( !$timeOfLastCoo ) {
783 ERROR "BNC does not compute coordinates";
784 return 0;
785 }
786
787 my $tp = Time::Piece->strptime( $timeOfLastCoo, '%y-%m-%d %H:%M:%S' );
788 my $now = Time::Piece->new;
789 my $tdiff = $now - $tp;
790 if ( $tdiff > 1200 ) {
791 ERROR( "Last computed coordinates are " . $tdiff / 60 . " min old" );
792 return 0;
793 }
794 }
795
796 # BNC works
797 return 1;
798}
799
8001; # End of Bnc
Note: See TracBrowser for help on using the repository browser.