我是Perl的新手,我将数据合并到一个新文件时遇到了问题。
我有一个日志文件(下面的示例),包括带有(RSSI值和时间戳)的行和带有(GPS位置(纬度,经度)和时间戳的行)。 GPS位置的触发频率高于RSSI值,因此日志文件包含的GPS线位数多于RSSI值的线位数。
我必须找到具有与每个RSSI时间戳匹配的正确时间戳的GPS线。
时间戳有99:99:99.999
格式。要比较时间戳,我只需要99:99:99
格式。
最后,我想生成一个新的CSV文件,其时间戳为99:99:99格式,RSSI值和相应的GPS位置,丢弃无用的GPS位置。 CSV文件应包含(时间戳,RSSI,纬度,经度)。日志文件如下所示。 (在这个例子中,GPS位置不会改变,但实际上它们会改变。)
# .log file
# first rssi timestamp is 10:23:05.746 (hour:minute:second.microsecond) and RSSI value -91
Line 167: 0.11036 0 25.11.2014 10:23:05.746 01.01.1970 00:53:55.545 CON.NAD.CINTERION nadProc.NAD_Run_AT_Cmds info 55 NADCinterion::handleSMONI(3441): SMONI LTE_RSSI -91 -91
Line 1747: 0.12577 0 25.11.2014 10:23:07.967 01.01.1970 00:53:57.766 CON.NAD.CINTERION nadProc.NAD_Run_AT_Cmds info 55 NADCinterion::handleSMONI(3441): SMONI LTE_RSSI -92 -92
Line 2096: 0.12925 0 25.11.2014 10:23:11.744 01.01.1970 00:54:01.544 CON.NAD.CINTERION nadProc.NAD_Run_AT_Cmds info 55 NADCinterion::handleSMONI(3441): SMONI LTE_RSSI -93 -93
Line 3472: 0.14272 0 25.11.2014 10:23:15.745 01.01.1970 00:54:05.545 CON.NAD.CINTERION nadProc.NAD_Run_AT_Cmds info 55 NADCinterion::handleSMONI(3441): SMONI LTE_RSSI -92 -92
Line 4915: 0.15681 0 25.11.2014 10:23:17.965 01.01.1970 00:54:07.766 CON.NAD.CINTERION nadProc.NAD_Run_AT_Cmds info 55 NADCinterion::handleSMONI(3441): SMONI LTE_RSSI -94 -94
# first GPS timestamp is 10:23:05.716 (hour:minute:second.microsecond) and GPS position 11.38172906823456,48.78751751035452
Line 194: 0.11062 0 25.11.2014 10:23:05.716 01.01.1970 00:53:55.515 BL.POSITIONING navi.DEF_THR debug 98 PositionNLAdapter < pos:11.38172906823456,48.78751751035452 heading: 156.0113220214844 speed:0km/h
Line 819: 0.11672 0 25.11.2014 10:23:06.715 01.01.1970 00:53:56.514 BL.POSITIONING navi.DEF_THR debug 98 PositionNLAdapter < pos:11.38172906823456,48.78751751035452 heading: 156.0113220214844 speed:0km/h
Line 1443: 0.12281 0 25.11.2014 10:23:07.716 01.01.1970 00:53:57.515 BL.POSITIONING navi.DEF_THR debug 98 PositionNLAdapter < pos:11.38172906823456,48.78751751035452 heading: 156.0113220214844 speed:0km/h
Line 2538: 0.13558 0 25.11.2014 10:23:08.714 01.01.1970 00:54:04.514 BL.POSITIONING navi.DEF_THR debug 98 PositionNLAdapter < pos:11.38172906823456,48.78751751035452 heading: 156.0113220214844 speed:0km/h
Line 2738: 0.13558 0 25.11.2014 10:23:11.714 01.01.1970 00:54:04.514 BL.POSITIONING navi.DEF_THR debug 98 PositionNLAdapter < pos:11.38172906823456,48.78751751035452 heading: 156.0113220214844 speed:0km/h
Line 3324: 0.14130 0 25.11.2014 10:23:15.714 01.01.1970 00:54:05.514 BL.POSITIONING navi.DEF_THR debug 98 PositionNLAdapter < pos:11.38172906823456,48.78751751035452 heading: 156.0113220214844 speed:0km/h
Line 4261: 0.15048 0 25.11.2014 10:23:17.114 01.01.1970 00:54:06.914 BL.POSITIONING navi.DEF_THR debug 98 PositionNLAdapter < pos:11.38172906823456,48.78751751035452 heading: 156.0113220214844 speed:0km/h
Line 4694: 0.15466 0 25.11.2014 10:23:17.813 01.01.1970 00:54:07.614 BL.POSITIONING navi.DEF_THR debug 98 PositionNLAdapter < pos:11.38172906823456,48.78751751035452 heading: 156.0113220214844 speed:0km/h
我必须使用LTE_RSSI $line =~ /RSSI/
,然后从该行$lines_rssi
获得正确的时间戳$rssi_timestamp
和带有正则表达式的$lte_rssi
m/(\-\d+$)/
$line =~ m/BL.POSITIONING/
1}}。
获取GPS位置和时间戳BL.POSITIONING $lines_gps
的正确行,然后从该行$gps_timestamp
获取时间戳$latitude
和相应的GPS位置$longitude
和m/pos:(\d+\.\d+),(\d+\.\d+)/
正则表达式99:99:99.999
。
将时间戳格式从99:99:99
格式化为#!/usr/bin/perl
use strict;
use warnings;
print "Geben Sie den Namen der log Datei ein: " ;
my $log =<STDIN>;
open(LOG, $log) || die "Log nicht gefunden";
my $rssi_timestamp; #rssi timestamp in format dd:dd:dd.ddd
my $lte_rssi; #rssi value
my $gps_timestamp; #gps timestamp ind format dd:dd:dd.ddd
my $latitude; #gps latitude
my $longitude; #gps longitude
my $lines_rssi; #one complete line with rssi
my $lines_gps; #one complete line with gps
my $gps_timestamp_mod; #modified gps timestamp in format dd:dd:dd
my $rssi_timestamp_mod; #modified rssi timestamp in format dd:dd:dd
while (my $line = <LOG>)
{
if ($line =~ /RSSI/) #find right line containing rssi value (regex RSSI)
{
$lines_rssi = $line;
$lines_rssi =~ m/(\d{2}\:\d{2}\:\d{2}\.\d{3})/; #find matching timestamp pattern with regex
$rssi_timestamp = $1;
print "$rssi_timestamp,";
$rssi_timestamp =~ m/(\d{2}\:\d{2}\:\d{2})/; #modify rssi timestamp format from dd:dd:dd.ddd to dd:dd:dd
$rssi_timestamp_mod = $1;
print "$rssi_timestamp_mod,";
$lines_rssi =~ m/(\-\d+$)/; #find rssi value with regex
$lte_rssi = $1;
print "$lte_rssi\n";
}
if ($line =~ m/BL.POSITIONING/) #find line with GPS position with regex
{
$lines_gps = $line;
$lines_gps =~ m/(\d{2}\:\d{2}\:\d{2}\.\d{3})/; # find matching timestamp in gps line
$gps_timestamp = $1;
$gps_timestamp =~ m/(\d{2}\:\d{2}\:\d{2})/;
$gps_timestamp_mod = $1;
if ($gps_timestamp_mod == $rssi_timestamp_mod) # here i want to compare the 2 modified timestamps with each other to find the right gps line but it doesn't work
{
$lines_gps =~ m/pos:(\d+\.\d+),(\d+\.\d+)/;
$latitude = $2;
$longitude = $1;
print "$gps_timestamp,$gps_timestamp_mod,$latitude,$longitude\n";
}
}
}
进行比较。
从RSSI线获取第一个修改后的时间戳,并将其与GPS线的修改时间戳进行比较,并在匹配时间戳时,从该线获取此GPS位置。
打印出具有修改的RSSI时间戳,RSSI值,匹配GPS线的GPS位置的CSV文件(参见第4点)。
我的代码看起来像这样。它打印出所有RSSI时间戳和值,以及所有GPS时间戳和位置,但不会将时间戳相互比较。然后只打印一个时间戳,RSSI值,纬度,经度
10:23:05,-91,48.78751751035452,11.38172906823456
10:23:07,-92,48.78751751035452,11.38172906823456
10:23:11,-93,48.78751751035452,11.38172906823456
10:23:15,-92,48.78751751035452,11.38172906823456
10:23:17,-94,48.78751751035452,11.38172906823456
CSV文件应如下(timestamp,rssi,latitude,longitude)。
只有来自RSSI值的时间戳应该在这里(10:23:05,10:23:07,10:23:11,10:23:15,10:23:17)丢弃带有时间戳的GPS线路(10:23:06,10:23:08,以及10:23:17之一):
{{1}}
有人可以帮我解决这个问题吗?
@Borodin:非常感谢代码。它完美无缺! 此外,正如你所提到的,我也在考虑GPS坐标的插值,就像在第2页的{1}和(2)Polynomial interpolation of GPS satellite coordinates
这个文件中所解释的一样。代码中的插值有多复杂?
答案 0 :(得分:1)
我认为这可以满足您的需求。我没有删除小数秒,而是编写了一个子程序epoch_seconds
,它使用Time::Piece
模块将时间(包括毫秒)和日期转换为浮点数 - 从1970年开始以来的秒数。这将避免在午夜时分出现任何问题。
读取整个文件,相关数据存储在两个哈希值中:%rssi
和%gps
,由浮点时间戳索引。然后,使用List::UtilsBy
中的%rssi
函数,通过查找时间戳中绝对差异最小的元素,将%gps
的每个元素与min_by
元素配对。然后,只需要在找到的两个元素中打印所有数据。
您可能必须安装List::UtilsBy
,因为它不是核心模块。
请注意,输出中实际报告的时间戳是$report_time
设置的值。我已经删除了日期和小数秒,使其与您的示例相同,但您可以根据需要修改它。
我觉得在RSSI数据两侧的GPS坐标之间进行线性插值以获得更好的准确度是很简单的,如果有用的话。
我希望这会有所帮助。
use strict;
use warnings;
use Time::Piece;
use List::UtilsBy qw/ min_by /;
print "Geben Sie den Namen der log Datei ein: ";
chomp(my $log_file = <STDIN>);
open my $log_fh, '<', $log_file or die "Log nicht gefunden: $!";
my $out_file = 'logfile.csv';
my (%rssi, %gps);
while (<$log_fh>) {
next if /^#/;
if (/LTE_RSSI/) { # find right line containing rssi value (regex RSSI)
next unless /( \d+\.\d+\.\d+ \s+ \d+:\d+:\d+\.\d+ )/x;
my $timestamp = $1;
my $timestamp_seconds = epoch_seconds($timestamp);
my ($report_time) = $timestamp =~ /(\d+:\d+:\d+)/;
next unless /(\-\d+$)/; # find RSSI value with regex
$rssi{$timestamp_seconds} = [$report_time, $1];
}
elsif (/BL\.POSITIONING/) { # find line with GPS position with regex
next unless /( \d+\.\d+\.\d+ \s+ \d+:\d+:\d+\.\d+ )/x;
my $timestamp = $1;
my $timestamp_seconds = epoch_seconds($timestamp);
next unless /pos:(\d+\.\d+),(\d+\.\d+)/;
$gps{$timestamp_seconds} = [$2,$1];
}
}
open my $out_fh, '>', $out_file or die qq{Unable to open "$out_file" for output: $!};
for my $rssi_seconds (sort { $a <=> $b } keys %rssi) {
my $gps_seconds = min_by { abs($_ - $rssi_seconds) } keys %gps;
print $out_fh join(',', @{ $rssi{$rssi_seconds} }, @{ $gps{$gps_seconds} }), "\n";
}
sub epoch_seconds {
my ($date_time) = @_;
die unless shift =~ /(.+)\.(.+)/;
Time::Piece->strptime($1, '%d.%m.%Y %H:%M:%S')->epoch . ".$2";
}
<强>输出强>
10:23:05,-91,48.78751751035452,11.38172906823456
10:23:07,-92,48.78751751035452,11.38172906823456
10:23:11,-93,48.78751751035452,11.38172906823456
10:23:15,-92,48.78751751035452,11.38172906823456
10:23:17,-94,48.78751751035452,11.38172906823456