/* ---------- LATEST CODE ---------- */
'Date',
1 => 'Time',
2 => 'Temperature (°C)',
5 => 'Humidity (%)',
6 => 'Dew Point (°C)',
7 => 'Wind Speed',
8 => 'Wind Direction',
13 => 'Heat Index (°C)',
16 => 'Pressure (hPa)',
17 => 'Rain Today (mm)',
18 => 'Rain Rate (mm/hr)',
27 => 'Indoor Temp (°C)',
28 => 'Indoor Humidity (%)',
];
function fetch_to_cache(string $url, string $name, int $ttl): ?string
{
$cacheFile = sys_get_temp_dir() . '/uob_weather_' . $name . '.cache';
if (is_file($cacheFile) && (time() - filemtime($cacheFile)) < $ttl) {
return $cacheFile;
}
$tmpFile = $cacheFile . '.' . getmypid() . '.tmp';
$ok = false;
if (function_exists('curl_init')) {
$fp = fopen($tmpFile, 'w');
if ($fp) {
$ch = curl_init($url);
curl_setopt_array($ch, [
CURLOPT_FILE => $fp,
CURLOPT_CONNECTTIMEOUT => 10,
CURLOPT_TIMEOUT => 120,
CURLOPT_FOLLOWLOCATION => true,
CURLOPT_USERAGENT => 'UoB-SpaceWeather-Site',
]);
$ok = curl_exec($ch) === true
&& curl_getinfo($ch, CURLINFO_RESPONSE_CODE) === 200;
fclose($fp);
}
} else {
$ctx = stream_context_create(['http' => ['timeout' => 120]]);
$data = @file_get_contents($url, false, $ctx);
$ok = $data !== false && @file_put_contents($tmpFile, $data) !== false;
}
if ($ok && filesize($tmpFile) > 0) {
rename($tmpFile, $cacheFile);
touch($cacheFile);
} else {
@unlink($tmpFile);
if (is_file($cacheFile)) {
touch($cacheFile);
}
}
return is_file($cacheFile) ? $cacheFile : null;
}
function row_timestamp(string $date, string $time): int
{
$d = explode('/', trim($date));
if (count($d) !== 3 || !ctype_digit($d[0]) || !ctype_digit($d[1])) {
return 0;
}
$y = (int) $d[2];
if ($y < 100) {
$y += 2000;
}
// Hours may be single-digit in the source files ("0:30" as well as "00:30").
if (!preg_match('/^(\d{1,2}):(\d{2})\b/', trim($time), $m)
|| (int) $m[1] > 23 || (int) $m[2] > 59) {
return 0;
}
return ((((int) sprintf('%04d%02d%02d', $y, (int) $d[1], (int) $d[0])) * 100)
+ (int) $m[1]) * 100 + (int) $m[2];
}
function normalize_time(string $time): string
{
if (preg_match('/^(\d{1,2}):(\d{2})\b/', trim($time), $m)) {
return sprintf('%02d:%02d', (int) $m[1], (int) $m[2]);
}
return trim($time);
}
function normalize_date(string $date): string
{
$d = explode('/', trim($date));
if (count($d) === 3 && strlen($d[2]) === 2) {
$d[2] = '20' . $d[2];
}
return implode('/', $d);
}
function parse_live_rows(?string $cacheFile): array
{
if ($cacheFile === null) {
return [];
}
$rows = [];
foreach (preg_split('/\r\n|\r|\n/', (string) file_get_contents($cacheFile)) as $line) {
$line = trim($line);
if ($line === '') {
continue;
}
$rows[] = str_getcsv($line, ',', '"', '\\');
}
return $rows;
}
function dedupe_rows(array $rows): array
{
$byTs = [];
foreach ($rows as $r) {
$ts = row_timestamp($r[0] ?? '', $r[1] ?? '');
if ($ts === 0) {
continue;
}
$byTs[$ts] = $r;
}
ksort($byTs, SORT_NUMERIC);
return array_values($byTs);
}
function csv_field(string $v): string
{
return (strpbrk($v, ",\"\r\n") !== false)
? '"' . str_replace('"', '""', $v) . '"'
: $v;
}
function project_row(array $fields, array $columns): array
{
$out = [];
foreach (array_keys($columns) as $idx) {
$val = trim($fields[$idx] ?? '');
$out[] = ($val === '' || $val === '---') ? '—' : $val;
}
return $out;
}
/* ---------- CSV download ---------- */
if (isset($_GET['download']) && $_GET['download'] === 'csv') {
set_time_limit(300);
$validDate = function (string $s): bool {
return preg_match('/^(\d{4})-(\d{2})-(\d{2})$/', $s, $m)
&& checkdate((int) $m[2], (int) $m[3], (int) $m[1]);
};
$fromParam = (isset($_GET['from']) && $validDate($_GET['from'])) ? $_GET['from'] : '';
$toParam = (isset($_GET['to']) && $validDate($_GET['to'])) ? $_GET['to'] : '';
$fromTs = $fromParam !== '' ? ((int) str_replace('-', '', $fromParam)) * 10000 : 0;
$toTs = $toParam !== '' ? ((int) str_replace('-', '', $toParam)) * 10000 + 2359 : PHP_INT_MAX;
$suffix = ($fromParam !== '' || $toParam !== '')
? '_' . ($fromParam !== '' ? $fromParam : DATA_START) . '_to_' . ($toParam !== '' ? $toParam : date('Y-m-d'))
: '_full';
$map = [];
$lastArchiveTs = 0;
$archiveFile = fetch_to_cache(ARCHIVE_URL, 'archive', ARCHIVE_TTL);
if ($archiveFile !== null && ($fh = fopen($archiveFile, 'r'))) {
while (($line = fgets($fh)) !== false) {
$fields = explode("\t", rtrim($line, "\r\n"));
$ts = row_timestamp($fields[0] ?? '', $fields[1] ?? '');
if ($ts === 0) {
continue;
}
$lastArchiveTs = max($lastArchiveTs, $ts);
if ($ts < $fromTs || $ts > $toTs || isset($map[$ts])) {
continue;
}
$fields[0] = normalize_date($fields[0]);
$fields[1] = normalize_time($fields[1]);
$fields = array_map('trim', array_pad(array_slice($fields, 0, 38), 38, ''));
$map[$ts] = implode(',', array_map('csv_field', $fields));
}
fclose($fh);
}
foreach (parse_live_rows(fetch_to_cache(LIVE_URL, 'live', LIVE_TTL)) as $fields) {
$ts = row_timestamp($fields[0] ?? '', $fields[1] ?? '');
if ($ts === 0 || $ts <= $lastArchiveTs || $ts < $fromTs || $ts > $toTs) {
continue;
}
$fields[0] = normalize_date($fields[0]);
$fields[1] = normalize_time($fields[1]);
$fields = array_map('trim', array_pad(array_slice($fields, 0, 38), 38, ''));
$map[$ts] = implode(',', array_map('csv_field', $fields));
}
ksort($map, SORT_NUMERIC);
header('Content-Type: text/csv; charset=utf-8');
header('Content-Disposition: attachment; filename="bradford_weatherstation' . $suffix . '.csv"');
echo implode(',', array_map('csv_field', $CSV_HEADERS)), "\r\n";
foreach ($map as $line) {
echo $line, "\r\n";
}
exit;
}
$rows = dedupe_rows(parse_live_rows(fetch_to_cache(LIVE_URL, 'live', LIVE_TTL)));
if (isset($_GET['format']) && $_GET['format'] === 'json') {
$recent = array_reverse(array_slice($rows, -TABLE_ROWS)); // newest first
header('Content-Type: application/json; charset=utf-8');
header('Cache-Control: no-cache');
echo json_encode([
'generated' => date('c'),
'headers' => array_values($COLUMNS),
'rows' => array_map(fn ($r) => project_row($r, $COLUMNS), $recent),
]);
exit;
}
/* ---------- HTML page ---------- */
$recent = array_reverse(array_slice($rows, -TABLE_ROWS)); // newest first
$latest = $recent[0] ?? [];
$headers = array_values($COLUMNS);
$today = date('Y-m-d');
function h(?string $s): string
{
return htmlspecialchars($s ?? '', ENT_QUOTES, 'UTF-8');
}
function cell(array $fields, int $idx): string
{
$v = trim($fields[$idx] ?? '');
return ($v === '' || $v === '---') ? '—' : $v;
}
?>
Live Weather Station — University of Bradford
Last reading: = h(cell($latest, 1)) ?>
Temperature
= h(cell($latest, 2)) ?> °C
Heat Index
= h(cell($latest, 13)) ?> °C
Humidity
= h(cell($latest, 5)) ?> %
Dew Point
= h(cell($latest, 6)) ?> °C
Pressure
= h(cell($latest, 16)) ?> hPa
Rain Today
= h(cell($latest, 17)) ?> mm
| = h($hd) ?> |
| = h(cell($r, $idx)) ?> |
Showing the latest = TABLE_ROWS ?>