Old style barograph

In the past pressure was recorded on beautiful drum barograph instruments (the meteorological equivalent of the drum seismograph).

Nowadays the instruments are digital, and people plot boring time series of pressure (time on the X axis, pressure on the Y axis). A few years ago I wrote a shell and GMT script to turn digital pressure records into old style barograph charts:


Barograph showing the pressure wave from the 15 January 2022 Hunga Tonga - Hunga Haʻapai volcanic eruption, as recorded at Honolulu International Airport. The pressure wave circumnavigated the world at least three times (in both directions), and was observed at many locations, including Honolulu.

The ksh script is below:

#!/usr/bin/env ksh

function barocoords {
	#
	# Convert date in format t,p,... to x,y,... on the barograph chart.
	#
	# Input data are provided on stdin and transformed data written to stdout.
	# Ignore lines beginning with ">".
	#
	# Arguments
	#
	# $1: length of chart (cm)									Default: 30
	# $2: height of chart (cm)									Default: 8
	# $3: mimimum pressure (hPa)								Default: 950
	# $4: maximum pressure (hPa)								Default: 1050
	# $5: minimum time (s since 1970-01-01T00:00:00)	Default: 7 days ago
	# $6: maximum time (s since 1970-01-01T00:00:00)	Default: Now
	# $7: length of barograph arm (cm)						Default: 15

	awk -v plen="${1:-63}" -v phgt="${2:-15}" -v pmin="${3:-950}" \
		-v pmax="${4:-1050}" -v tmin="${5:-$(($(gdate -u +%s)-604800))}" \
		-v tmax="${6:-$(gdate -u +%s)}" -v arm="${7:-25}" '
	/^[^>]/{
		t = $1;
		p = $2;
		pmid = (pmin + pmax)/2;
		y = phgt*(p - pmid)/(pmax - pmin);
		d = arm - sqrt(arm^2 - y^2);
		x = plen*(t - tmin)/(tmax - tmin) + d;
		printf("%f\t%f\t", x, y);
		for (ii=3; ii<=NF; ii++) printf("%s\t", $ii);
		printf("\n");
	}
	/^>/{
		printf("%s\n", $0);
	}'
}

function decode_us {
	#
	# Decode 1-minute data from the US NWS.
	#
	gawk -F, '
	/^[[:upper:]]{4}/{
		year	= substr($3,1,4);
		mon	= substr($3,6,2);
		day	= substr($3,9,2);
		hour	= substr($3,12,2);
		min	= substr($3,15,2);
		utime	= mktime(year" "mon" "day" "hour" "min" 00",1);
		prs	= $4*33.86388666;	# Convert inches of Hg to hPa.
		if (prs > 0){
			printf("%d\t%f\n", utime, $4*33.86388666);
		}
	}'
}

function find_missing {
	#
	# Find missing time steps in (t, P) series, and insert GMT "gaps".
	#
	awk -v tmin=${1:-0} -v tmax=${2:-999999999999} '
	BEGIN{
		tprev 	= 0;
		nsteps	= 0;
		ssteps	= 0;
		avgstep	= 86400;
	}
	($1 >= tmin) && ($1 <= tmax){
		if (($1 - avgstep) > tprev){
			printf(">\n");
		} else {
			ssteps += ($1 - tprev);
			nsteps ++;
			avgstep = ssteps/nsteps;
		}
		printf("%s\n", $0);
		tprev = $1;
	}'
}

function helpmsg {
	cat <<- END 1>&2
	usage: plot_baro -d infile [ options ]
		-d infile  set data file name   (Required)
		-w width   set plot width       (integer, default = 30 cm)
		-h height  set plot height      (integer, default = 8 cm)
		-p pmin    set minimum prs      (integer, default = 950 hPa, 0=auto)
		-P pmax    set maximum prs      (integer, default = 1050 hPa, 0=auto)
		-t tmin    set start time       (gdate parseable, default = 1 week ago)
		-n ndays   set num of days      (integer, default = 7)
		-a arm     set barograph length (integer, default = 15 cm)
		-c ccol    set chart colour     (GMT parseable, default = 84/189/140)
		-i icol    set ink colour       (GMT parseable, default = 60/60/60)
		-s stn     set station name     (default = Brisbane)
		-o outfile set output file name (default = barograph.*)
		-h         print help message
	END
	exit 2
}

#
# Read the command line.
#

#
# Default values.
#

pmin=0						# Automatic
pmax=0						# Automatic
arm=15
plen=30
phgt=8
tmin=$(gdate -u -d "2022-01-12 00:00:00" +%s)
ndays=7
chartcol="84/189/140" 	# A darkish green.
pencol="60/60/60"			# Something like India ink.
stn=""
ifile=""
ofile="barograph"

while getopts ":w#h#p#P#n#a#t:c:i:s:o:d:h" option
do
	case "${option}" in
		w)	plen="${OPTARG}";;
		h)	phgt="${OPTARG}";;
		p)	pmin="${OPTARG}";;
		P)	pmax="${OPTARG}";;
		n)	ndays="${OPTARG}";;
		a)	arm="${OPTARG}";;
		t)	tmin=$(gdate -u -d "${OPTARG}" +%s) || helpmsg;;
		c)	chartcol="${OPTARG}";;
		i)	pencol="${OPTARG}";;
		s) stn="${OPTARG}";;
		d) ifile="${OPTARG}";;
		o) ofile="${OPTARG}";;
		h) helpmsg;;
		?) helpmsg;;
	esac
done

#
# The data file is mandatory.
#
if [[ ! -f "${ifile}" ]]
then
	helpmsg
fi

#
# Make a temporary directory for intermediate files.
#
tmpdir=$(mktemp -d)

#
# If station name not given, try and extract it from the data file.
#
if [[ "${stn}" = "" ]]
then
	stn=$(awk -F, '
	BEGIN{
		stn="";
	}
	/^[[:upper:]]{4}/{
		stn=$2;
	}
	END{
		gsub("_", " ", stn);
		print stn;
	}' < "${ifile}")
fi

#
# Set some parameters based on length of chart.
#
# tinc: increment between time grid lines (s)
# linc: increment between pressure labels (s)
#

if [[ ${ndays} -eq 1 ]]
then
	tinc=3600
	linc=86400
else
	tinc=10800
	linc=172800
fi

#
# Adjust the start and end times of the chart so
# that it starts on multiples of tinc.
#

tmin=$(((${tmin}/${tinc})*${tinc}))
tmax=$((${tmin}+${ndays}*86400))

#
# Extract the data.
#
# This is set up to extract data from an example climate zone output.
#

#gawk -F, -v tmin="${tmin}" -v tmax="${tmax}" '
#	/^hd/ && ($13+0 > 500){
#		utime = mktime($8" "$9" "$10" "$11" "$12" 00", 1);
#		if ((utime >= tmin)&&(utime <= tmax)){
#			printf("%d\t%f\n", utime, $13);
#		}
#	}' < "${ifile}" > "${tmpdir}/data.txt"

decode_us < "${ifile}" | find_missing "${tmin}" "${tmax}" > "${tmpdir}/data.txt"
cp "${tmpdir}/data.txt" data.txt

#
# Settings for the pressure axis.
#

if [[ ${pmin} -eq 0 ]]
then
	pmin=$(gmt info -C -I5 "${tmpdir}/data.txt" | cut -f 3)
fi

if [[ ${pmax} -eq 0 ]]
then
	pmax=$(gmt info -C -I5 "${tmpdir}/data.txt" | cut -f 4)
fi

if [[ $((${pmax}-${pmin})) -le 40 ]]
then
	if [[ $((${pmax}-${pmin})) -le 20 ]]
	then
		pinc=2
	else
		pinc=5
	fi
else
	pinc=10
fi

#
# Compute the coordinates of the lines on the chart.
#
# Bold pressure lines are drawn every ${pinc} hPa
# Fine pressure lines are drawn every 1 hPa
# Fine time lines are drawn every ${tinc} s
# Bold time lines are drawn every 86400 s
#

prs=${pmin}
while [[ ${prs} -le ${pmax} ]]
do
	if [[ $((${prs}%${pinc})) -eq 0 ]]
	then
		echo "> -Wthicker,${chartcol}"
	else
		echo "> -Wthinnest,${chartcol}"
	fi
	echo "${tmin} ${prs}" | barocoords ${plen} ${phgt} ${pmin} ${pmax} \
		${tmin} ${tmax} ${arm}
	echo "${tmax} ${prs}" | barocoords ${plen} ${phgt} ${pmin} ${pmax} \
		${tmin} ${tmax} ${arm}
	prs=$((${prs}+1))
done > "${tmpdir}/chart.txt"

t=${tmin}
while [[ ${t} -le ${tmax} ]]
do
	if [[ $((${t}%86400)) -eq 0 ]]
	then
		echo "> -Wthicker,${chartcol}"
	else
		echo "> -Wthinnest,${chartcol}"
	fi
	prs=${pmin}
	while [[ ${prs} -le ${pmax} ]]
	do
		echo "${t}	${prs}"
		prs=$((${prs}+1))
	done | barocoords ${plen} ${phgt} ${pmin} ${pmax} ${tmin} ${tmax} ${arm}
	t=$((${t}+${tinc}))
done >> "${tmpdir}/chart.txt"

#
# Compute the positions of the labels.
#

tophourpos=${pmax}
bothourpos=${pmin}
datepos=$((${pmin} + (${pmax}-${pmin})*0.95))
firstdate="Y"
t=${tmin}
while [[ ${t} -le ${tmax} ]]
do
	thour=$(((${t}%86400)/3600))
	printf "%f\t%f\t10p,Times-Italic,%s\t0\tCB\t%02d\n" \
		${t} ${tophourpos} ${chartcol} ${thour}
	printf "%f\t%f\t10p,Times-Italic,%s\t0\tCB\t%02d\n" \
		${t} ${bothourpos} ${chartcol} ${thour}
	if [[ ${thour} -eq 12 ]]
	then
		if [[ ${firstdate} = "Y" ]]
		then
			datestr=$(gdate -u -d "1970-01-01 ${t} seconds" +"%a %d %b %Y %Z")
			firstdate="N"
		else
			datestr=$(gdate -u -d "1970-01-01 ${t} seconds" +"%a %d %b")
		fi
		printf "%f\t%f\t12p,Times-Italic,%s\t0\tCM\t%s\n" \
			${t} ${datepos} ${chartcol} "${datestr}"
	fi
	if [[ $((${t}%linc)) -eq 0 && ${t} -lt $((${tmax}-${tinc})) ]]
	then
		prs=$((${pmin}+${pinc}))
		while [[ ${prs} -le $((${pmax}-${pinc})) ]]
		do
			pnext=$((${prs}+${pinc}))
			if [[ ${pnext} -gt $((${pmax}-${pinc})) ]]
			then
				printf "%f\t%f\t11p,Helvetica,%s\t0\tCM\t%s\n" \
					$((${t}+${tinc})) ${prs} ${chartcol} "${prs} hPa"
			else
				printf "%f\t%f\t11p,Helvetica,%s\t0\tCM\t%s\n" \
					$((${t}+${tinc})) ${prs} ${chartcol} "${prs}"
			fi
			prs=${pnext}
		done
	fi
	t=$((${t}+${tinc}))
done | barocoords ${plen} ${phgt} ${pmin} ${pmax} \
	${tmin} ${tmax} ${arm} > "${tmpdir}/labels.txt"

#
# Add the station name and time stamp to the labels file.
#
cat <<- END >> "${tmpdir}/labels.txt"
0 $((${phgt}/2+1.2)) 12p,Helvetica-Oblique,${chartcol} 0 LB STATION: ${stn}
END
#$((${plen}-2)) $((${phgt}/2+1.2)) 8p,Helvetica-Oblique,${chartcol} 0 BR Plotted by Tim Hume

#
# Now create the chart.
#

gmt begin "${ofile}" pdf,png A+m0.5c
	gmt gmtset FONT_LOGO auto,Helvetica,${chartcol}
	gmt plot "${tmpdir}/chart.txt" -JX${plen}/${phgt} ${chartreg} -N
	gmt text "${tmpdir}/labels.txt" -F+f+a+j -N -DJ0/0.2 -Gwhite
	barocoords ${plen} ${phgt} ${pmin} ${pmax} ${tmin} ${tmax} \
		${arm} < "${tmpdir}/data.txt" | gmt plot -Wthinnest,${pencol}
	#gmt logo -DjTR+w1.5+jBR+o0/0.5 -Sn
gmt end

#
# Remove the temporary directory.
#
rm -rf "${tmpdir}"

And the data file has this format:

station,station_name,valid(UTC),pres1,
PHNL,HONOLULU_INTL/OAHU_(ASOS),2022-01-01 10:00,29.823,
PHNL,HONOLULU_INTL/OAHU_(ASOS),2022-01-01 10:01,29.822,
PHNL,HONOLULU_INTL/OAHU_(ASOS),2022-01-01 10:02,29.822,
PHNL,HONOLULU_INTL/OAHU_(ASOS),2022-01-01 10:03,29.821,
PHNL,HONOLULU_INTL/OAHU_(ASOS),2022-01-01 10:04,29.82,
PHNL,HONOLULU_INTL/OAHU_(ASOS),2022-01-01 10:05,29.82,
PHNL,HONOLULU_INTL/OAHU_(ASOS),2022-01-01 10:06,29.819,
PHNL,HONOLULU_INTL/OAHU_(ASOS),2022-01-01 10:07,29.818,
PHNL,HONOLULU_INTL/OAHU_(ASOS),2022-01-01 10:08,29.818,
6 Likes

I love those instruments. During my degree we had some classes near by the old meteo observatory in Lisbon. I used to visit frequently the roof of the building where they were installed, which as a further bonus had a magnificent view over the city.

Those scripts are begging to be translated to GMT.jl functions.

Very cool.

Why are the ‘vertical’ lines slanted? Any function, or just for exotic presentation?

Because the pressure is traced by a pen on an arm (see the top picture). The nib of the pen can only move along a circular arc. The drum rotates under the pen.

Ah, of course. Thanks!

You wouldn’t happen to have a script to draw Skew-T diagram per chance? :smiley:

Somewhere I’ve got a script for drawing tephigrams (in my biased opinion, they’re better than skew-T charts). But I’ll have to look for it.

1 Like