diff --git a/examples/Sensor/QMC63xx_GetDataExample/calc_heading_strength_stats.pl b/examples/Sensor/QMC63xx_GetDataExample/calc_heading_strength_stats.pl new file mode 100755 index 0000000..7504f29 --- /dev/null +++ b/examples/Sensor/QMC63xx_GetDataExample/calc_heading_strength_stats.pl @@ -0,0 +1,157 @@ +#!/usr/bin/perl +# +# calc_heading_strength_stats.pl +# +# 20260420 ChatGPT +# $Id$ +# $HeadURL$ +# +# Example: +# chmod 755 calc_heading_strength_stats.pl +# ./calc_heading_strength_stats.pl magnetometer_readings.log +# ./calc_heading_strength_stats.pl ~/sessions/T-Beam_calibrate_20260420_Mon_014614.script +# +# This script scans a console/script log and extracts only lines like: +# +# 3005 00:36.756 Mag: X:289.20 Y:216.86 Z:608.78 μT Metadata: X:10845 Y:8132 Z:22829 Heading (rad): 0.585220 rad Heading (deg): 33.53° Magnetic Strength: 708.01 μT +# +# It ignores non-matching lines and computes statistics for: +# 1) Heading (deg) +# 2) Magnetic Strength +# +# Reported: +# - count +# - min / max / range +# - mean +# - population variance +# - sample variance +# - population std dev +# - sample std dev +# - line number / timestamp / text for min and max +# + +use strict; +use warnings; +use utf8; +use open ':std', ':encoding(UTF-8)'; + +my $input_file = shift @ARGV + or die "Usage: $0 input_log_file\n"; + +open my $fh, '<', $input_file + or die "Cannot open '$input_file': $!\n"; + +my %stats = ( + heading_deg => init_stat('Heading (deg)'), + mag_strength => init_stat('Magnetic Strength'), +); + +my $matched_lines = 0; + +while (my $line = <$fh>) { + chomp $line; + + next unless $line =~ /^\s*(\d+)\s+(\d{2}:\d{2}\.\d{3})\s+Mag:/; + my $log_lineno = $1; + my $time_stamp = $2; + + next unless $line =~ /Heading \(deg\):\s*([-+]?\d+(?:\.\d+)?)°/; + my $heading_deg = $1 + 0; + + next unless $line =~ /Magnetic Strength:\s*([-+]?\d+(?:\.\d+)?)\s*μT/; + my $mag_strength = $1 + 0; + + $matched_lines++; + + update_stat($stats{heading_deg}, $heading_deg, $log_lineno, $time_stamp, $line); + update_stat($stats{mag_strength}, $mag_strength, $log_lineno, $time_stamp, $line); +} + +close $fh; + +if (!$matched_lines) { + die "No matching data lines were found in '$input_file'.\n"; +} + +print_report($stats{heading_deg}); +print "\n"; +print_report($stats{mag_strength}); + +exit 0; + +sub init_stat { + my ($label) = @_; + + return { + label => $label, + count => 0, + mean => 0, + m2 => 0, # Welford running sum of squared deviations + min => undef, + max => undef, + min_line => undef, + min_time => undef, + min_text => undef, + max_line => undef, + max_time => undef, + max_text => undef, + }; +} + +sub update_stat { + my ($s, $x, $log_lineno, $time_stamp, $text) = @_; + + $s->{count}++; + + if (!defined $s->{min} || $x < $s->{min}) { + $s->{min} = $x; + $s->{min_line} = $log_lineno; + $s->{min_time} = $time_stamp; + $s->{min_text} = $text; + } + + if (!defined $s->{max} || $x > $s->{max}) { + $s->{max} = $x; + $s->{max_line} = $log_lineno; + $s->{max_time} = $time_stamp; + $s->{max_text} = $text; + } + + my $delta = $x - $s->{mean}; + $s->{mean} += $delta / $s->{count}; + my $delta2 = $x - $s->{mean}; + $s->{m2} += $delta * $delta2; + + return; +} + +sub print_report { + my ($s) = @_; + + my $count = $s->{count}; + my $range = $s->{max} - $s->{min}; + + my $pop_variance = ($count > 0) ? ($s->{m2} / $count) : 0; + my $samp_variance = ($count > 1) ? ($s->{m2} / ($count-1)) : 0; + + my $pop_stddev = sqrt($pop_variance); + my $samp_stddev = sqrt($samp_variance); + + print "$s->{label}\n"; + print "=" x length($s->{label}), "\n"; + printf "count : %d\n", $count; + printf "min : %.6f\n", $s->{min}; + printf "max : %.6f\n", $s->{max}; + printf "range : %.6f\n", $range; + printf "mean : %.6f\n", $s->{mean}; + printf "population variance : %.6f\n", $pop_variance; + printf "sample variance : %.6f\n", $samp_variance; + printf "population std dev : %.6f\n", $pop_stddev; + printf "sample std dev : %.6f\n", $samp_stddev; + printf "min occurred at : line %s, time %s\n", $s->{min_line}, $s->{min_time}; + printf "max occurred at : line %s, time %s\n", $s->{max_line}, $s->{max_time}; + print "min line text : $s->{min_text}\n"; + print "max line text : $s->{max_text}\n"; + + return; +}