Pensacola 2026-02 Predictions: Difference between revisions
→Code: Average multiple coordinates; output in yards |
→Ion: Typo fix: Euclidean → Cartesian |
||
| (5 intermediate revisions by the same user not shown) | |||
| Line 22: | Line 22: | ||
A prediction for the line-of-sight distance between two GPS coordinates. | A prediction for the line-of-sight distance between two GPS coordinates. | ||
The methodology follows below. The results are: | |||
{| class="wikitable" | |||
|+ Line-of-sight distance prediction | |||
|- | |||
! Earth model !! meters !! yards !! feet | |||
|- | |||
| Flat earth || 617 || 675 || 2025 | |||
|- | |||
| Spherical earth || 516 || 564 || 1691 | |||
|- | |||
| Ellipsoidal earth || 516 || 565 || 1694 | |||
|} | |||
==== Flat earth prediction ==== | ==== Flat earth prediction ==== | ||
===== 1. Calculating the | ===== 1. Calculating the Cartesian coordinates from the GPS coordinates ===== | ||
We model the Earth as a flat plane where: | We model the Earth as a flat plane where: | ||
| Line 58: | Line 72: | ||
</math> | </math> | ||
===== 2. Calculating the straight-line distance between two | ===== 2. Calculating the straight-line distance between two Cartesian coordinates ===== | ||
[[File:Ion_Pensacola_2026-02_GPS_Distance_Prediction_Flat_2.png|frameless]] | [[File:Ion_Pensacola_2026-02_GPS_Distance_Prediction_Flat_2.png|frameless]] | ||
| Line 70: | Line 84: | ||
==== Spherical earth prediction ==== | ==== Spherical earth prediction ==== | ||
===== 1. Calculating the | ===== 1. Calculating the Cartesian coordinates from the GPS coordinates ===== | ||
We model the Earth as a sphere with a radius of 6,371 km. | We model the Earth as a sphere with a radius of 6,371 km. | ||
| Line 92: | Line 106: | ||
</math> | </math> | ||
===== 2. Calculating the straight-line distance between two | ===== 2. Calculating the straight-line distance between two Cartesian coordinates ===== | ||
[[File:Ion_Pensacola_2026-02_GPS_Distance_Prediction_Globe_2.png|frameless]] | [[File:Ion_Pensacola_2026-02_GPS_Distance_Prediction_Globe_2.png|frameless]] | ||
| Line 104: | Line 118: | ||
==== Ellipsoidal earth prediction ==== | ==== Ellipsoidal earth prediction ==== | ||
===== Calculating the | ===== Calculating the Cartesian coordinates from the GPS coordinates ===== | ||
We model the Earth as an ellipsoid with the parameters as per WGS-84. | We model the Earth as an ellipsoid with the parameters as per WGS-84. | ||
| Line 152: | Line 166: | ||
<pre> | <pre> | ||
function main() { | function main() { | ||
const point1GPS = [ | |||
const point1GPS = | [30.332329, -87.131779, 1], | ||
const point2GPS = | [30.332330, -87.131783, 1], | ||
]; | |||
const point2GPS = [ | |||
[30.331353, -87.137035, 3], | |||
[30.331352, -87.137029, 3], | |||
]; | |||
const | const results = predictDistance(point1GPS, point2GPS); | ||
printWikiTable(results); | |||
} | } | ||
/** | /** | ||
function | * Output a table of the results in the wikitable format. | ||
* | |||
* @param {Map<string, number>} results For each model, the distance prediction in meters. | |||
* @returns {void} | |||
*/ | |||
function printWikiTable(results) { | |||
console.log('{| class="wikitable"'); | |||
console.log("|+ Line-of-sight distance prediction"); | |||
console.log("|-"); | |||
console.log("! Earth model !! meters !! yards !! feet"); | |||
const | const YARDS_PER_METER = 1 / 0.9144; | ||
const FEET_PER_YARD = 3; | |||
const | |||
for (const | for (const [model, distanceMeters] of results) { | ||
const distanceYards = distanceMeters * YARDS_PER_METER; | |||
const distanceFeet = distanceYards * FEET_PER_YARD; | |||
console.log("|-"); | |||
console.log( | |||
"| " + | |||
[ | |||
model, | |||
`${distanceMeters.toFixed(0)}`, | |||
`${distanceYards.toFixed(0)}`, | |||
`${distanceFeet.toFixed(0)}`, | |||
].join(" || "), | |||
); | |||
} | } | ||
console.log("|}"); | |||
} | } | ||
| Line 185: | Line 217: | ||
* Output flat earth and globe predictions for the straight-line distance | * Output flat earth and globe predictions for the straight-line distance | ||
* between a pair of GPS coordinates. | * between a pair of GPS coordinates. | ||
* | |||
* @param {[number, number, number][]} point1GPS The samples for the first GPS coordinate. | |||
* @param {[number, number, number][]} point2GPS The samples for the second GPS coordinate. | |||
* @returns {Map<string, number>} The distance predictions in meters. | |||
*/ | */ | ||
function predictDistance(point1GPS, point2GPS) { | function predictDistance(point1GPS, point2GPS) { | ||
const distanceFlatEarth = euclideanDistance( | const distanceFlatEarth = euclideanDistance( | ||
elementwiseAverage(point1GPS.map(cartesianFlatEarth)), | |||
elementwiseAverage(point2GPS.map(cartesianFlatEarth)), | |||
); | ); | ||
const distanceSphere = euclideanDistance( | const distanceSphere = euclideanDistance( | ||
elementwiseAverage(point1GPS.map(cartesianSphere)), | |||
elementwiseAverage(point2GPS.map(cartesianSphere)), | |||
); | ); | ||
const distanceEllipsoid = euclideanDistance( | const distanceEllipsoid = euclideanDistance( | ||
elementwiseAverage(point1GPS.map(cartesianEllipsoid)), | |||
elementwiseAverage(point2GPS.map(cartesianEllipsoid)), | |||
); | ); | ||
return | return new Map([ | ||
["Flat earth", distanceFlatEarth], | |||
["Spherical earth", distanceSphere], | |||
"Flat earth" | ["Ellipsoidal earth", distanceEllipsoid], | ||
"Spherical earth" | ]); | ||
"Ellipsoidal earth" | |||
} | } | ||
/** | /** | ||
* Compute the element-wise average of arrays. | |||
* | |||
* @param {number[][]} arrays An array of constant-dimension arrays. | |||
* @returns {number[]} An array with the dimension of the input sub-arrays. | |||
*/ | |||
function elementwiseAverage(arrays) { | |||
if (arrays.length === 0) throw new Error("At least one array required"); | |||
const dimension = arrays[0].length; | |||
const | |||
const | const result = new Array(dimension).fill(0); | ||
for (const array of arrays) { | |||
for (let ix = 0; ix < dimension; ++ix) { | |||
result[ix] += array[ix]; | |||
} | |||
} | |||
for (let ix = 0; ix < dimension; ++ix) { | |||
result[ix] /= arrays.length; | |||
} | |||
return result; | |||
} | } | ||
/** | /** | ||
* Compute the | * Compute the Cartesian coordinates for the given GPS coordinates on a flat | ||
* earth. | * earth. | ||
* | |||
* @param {[number, number, number]} pointGPS A GPS coordinate (latitude in degrees, longitude in degrees, altitude in meters). | |||
* @returns {[number, number, number]} A Cartesian coordinate. | |||
*/ | */ | ||
function | function cartesianFlatEarth([latitudeDeg, longitudeDeg, altitudeMeters]) { | ||
const RADIUS_EQUATOR = 10002 * 1000; | const RADIUS_EQUATOR = 10002 * 1000; | ||
| Line 246: | Line 291: | ||
/** | /** | ||
* Compute the | * Compute the Cartesian coordinates for the given GPS coordinates on a | ||
* spherical earth. | * spherical earth. | ||
* | |||
* @param {[number, number, number]} pointGPS A GPS coordinate (latitude in degrees, longitude in degrees, altitude in meters). | |||
* @returns {[number, number, number]} A Cartesian coordinate. | |||
*/ | */ | ||
function | function cartesianSphere([latitudeDeg, longitudeDeg, altitudeMeters]) { | ||
const RADIUS_SPHERE = 6371 * 1000; | const RADIUS_SPHERE = 6371 * 1000; | ||
| Line 267: | Line 315: | ||
/** | /** | ||
* Compute the | * Compute the Cartesian coordinates for the given GPS coordinates on an | ||
* ellipsoidal earth. | * ellipsoidal earth. | ||
* | |||
* @param {[number, number, number]} pointGPS A GPS coordinate (latitude in degrees, longitude in degrees, altitude in meters). | |||
* @returns {[number, number, number]} A Cartesian coordinate. | |||
*/ | */ | ||
function | function cartesianEllipsoid([latitudeDeg, longitudeDeg, altitudeMeters]) { | ||
const SEMI_MAJOR_AXIS = 6378137; | const SEMI_MAJOR_AXIS = 6378137; | ||
const FLATTENING_FACTOR = 1 / 298.257223563; | const FLATTENING_FACTOR = 1 / 298.257223563; | ||
| Line 295: | Line 346: | ||
/** | /** | ||
* Compute the distance between two Euclidean coordinates. | * Compute the Euclidean distance between two Cartesian coordinates. | ||
* | |||
* @param {[number, number, number]} point1Cartesian First Cartesian coordinate. | |||
* @param {[number, number, number]} point2Cartesian Second Cartesian coordinate. | |||
* @returns {number} The Euclidean distance between the coordinates. | |||
*/ | */ | ||
function euclideanDistance([x1, y1, z1], [x2, y2, z2]) { | function euclideanDistance([x1, y1, z1], [x2, y2, z2]) { | ||
| Line 303: | Line 358: | ||
} | } | ||
/** Convert an angle in degrees into radians. */ | /** | ||
* Convert an angle in degrees into radians. | |||
* | |||
* @param {number} angleDeg The angle in degrees. | |||
* @returns {number} The angle in radians. | |||
*/ | |||
function deg2rad(angleDeg) { | function deg2rad(angleDeg) { | ||
return angleDeg * (Math.PI / 180); | return angleDeg * (Math.PI / 180); | ||
Latest revision as of 05:09, 18 March 2026
MCToon: I predict no flerf can make a prediction.
SThurston2 (not a flerf)
Map = North pole AE map. Geometry = On a flat Earth the Sun (A), the Observer (B), and the North pole (C), are three points which make a triangle. The capital letters are used for both points and angles. The distances will be measured in units of the distance you have to travel due South to change your latitude by 1 degree and will be called degrees. The Sun's latitude will be the predicted declination at Noon UTC as provided by SunCalc. Good enough for an approximation. The distance A to C is named b = (90 - the Sun's latitude). The distance B to C is named a = (90 - the Observer's latitude). The distance A to B is named c = the distance required to change the elevation of the Sun by 90 degrees. That is the same as 90 degrees of latitude change using the rule 60NM per degree of elevation change and 1 NM = 1 arcminute of latitude change. Using the known 3 sides of the triangle, use the Law of Cosines (Cos(C) = (a^2 + b^2 - c^2)/2ab) to get the angles. The UTC time is given by (12 + 24 * (angle C + Mod(360 - Obs Longitude, 360)) / 360). The heading is (360 - angle B).
- The flat Earth predictions are
- Feb 23 at 22:00:01 heading 279.2;
- Feb 24 at 22:01:01 heading 279.6;
- Feb 25 at 22:02:01 heading 280;
- Feb 26 at 22:04:00 heading 280.4;
- Feb 27 at 22:05:00 heading 280.8
Ion
A prediction for the line-of-sight distance between two GPS coordinates.
The methodology follows below. The results are:
| Earth model | meters | yards | feet |
|---|---|---|---|
| Flat earth | 617 | 675 | 2025 |
| Spherical earth | 516 | 564 | 1691 |
| Ellipsoidal earth | 516 | 565 | 1694 |
Flat earth prediction
1. Calculating the Cartesian coordinates from the GPS coordinates
We model the Earth as a flat plane where:
- the equator is a circle,
- the distance from the North Pole to the equator is 10,002 km,
- lines of latitude are equally spaced concentric circles around the North Pole,
- lines of longitude extend outward from the North Pole at equally increasing angles, and
- all verticals are perpendicular to the plane (and thus parallel with each other).
Let .
The distance from the North Pole to a given latitude:
The coordinates:
2. Calculating the straight-line distance between two Cartesian coordinates
We use the Pythagorean theorem.
Spherical earth prediction
1. Calculating the Cartesian coordinates from the GPS coordinates
We model the Earth as a sphere with a radius of 6,371 km.
Let .
The x and y coordinates lie on a small circle scaled by the cosine of the latitude.
2. Calculating the straight-line distance between two Cartesian coordinates
Again, we use the Pythagorean theorem.
Ellipsoidal earth prediction
Calculating the Cartesian coordinates from the GPS coordinates
We model the Earth as an ellipsoid with the parameters as per WGS-84.
The semi-major axis:
The flattening factor:
Eccentricity squared:
Radius of curvature:
The coordinates:
The Euclidean distance calculation between coordinates is once again done using the Pythagorean theorem as shown above.
Code
function main() {
const point1GPS = [
[30.332329, -87.131779, 1],
[30.332330, -87.131783, 1],
];
const point2GPS = [
[30.331353, -87.137035, 3],
[30.331352, -87.137029, 3],
];
const results = predictDistance(point1GPS, point2GPS);
printWikiTable(results);
}
/**
* Output a table of the results in the wikitable format.
*
* @param {Map<string, number>} results For each model, the distance prediction in meters.
* @returns {void}
*/
function printWikiTable(results) {
console.log('{| class="wikitable"');
console.log("|+ Line-of-sight distance prediction");
console.log("|-");
console.log("! Earth model !! meters !! yards !! feet");
const YARDS_PER_METER = 1 / 0.9144;
const FEET_PER_YARD = 3;
for (const [model, distanceMeters] of results) {
const distanceYards = distanceMeters * YARDS_PER_METER;
const distanceFeet = distanceYards * FEET_PER_YARD;
console.log("|-");
console.log(
"| " +
[
model,
`${distanceMeters.toFixed(0)}`,
`${distanceYards.toFixed(0)}`,
`${distanceFeet.toFixed(0)}`,
].join(" || "),
);
}
console.log("|}");
}
/**
* Output flat earth and globe predictions for the straight-line distance
* between a pair of GPS coordinates.
*
* @param {[number, number, number][]} point1GPS The samples for the first GPS coordinate.
* @param {[number, number, number][]} point2GPS The samples for the second GPS coordinate.
* @returns {Map<string, number>} The distance predictions in meters.
*/
function predictDistance(point1GPS, point2GPS) {
const distanceFlatEarth = euclideanDistance(
elementwiseAverage(point1GPS.map(cartesianFlatEarth)),
elementwiseAverage(point2GPS.map(cartesianFlatEarth)),
);
const distanceSphere = euclideanDistance(
elementwiseAverage(point1GPS.map(cartesianSphere)),
elementwiseAverage(point2GPS.map(cartesianSphere)),
);
const distanceEllipsoid = euclideanDistance(
elementwiseAverage(point1GPS.map(cartesianEllipsoid)),
elementwiseAverage(point2GPS.map(cartesianEllipsoid)),
);
return new Map([
["Flat earth", distanceFlatEarth],
["Spherical earth", distanceSphere],
["Ellipsoidal earth", distanceEllipsoid],
]);
}
/**
* Compute the element-wise average of arrays.
*
* @param {number[][]} arrays An array of constant-dimension arrays.
* @returns {number[]} An array with the dimension of the input sub-arrays.
*/
function elementwiseAverage(arrays) {
if (arrays.length === 0) throw new Error("At least one array required");
const dimension = arrays[0].length;
const result = new Array(dimension).fill(0);
for (const array of arrays) {
for (let ix = 0; ix < dimension; ++ix) {
result[ix] += array[ix];
}
}
for (let ix = 0; ix < dimension; ++ix) {
result[ix] /= arrays.length;
}
return result;
}
/**
* Compute the Cartesian coordinates for the given GPS coordinates on a flat
* earth.
*
* @param {[number, number, number]} pointGPS A GPS coordinate (latitude in degrees, longitude in degrees, altitude in meters).
* @returns {[number, number, number]} A Cartesian coordinate.
*/
function cartesianFlatEarth([latitudeDeg, longitudeDeg, altitudeMeters]) {
const RADIUS_EQUATOR = 10002 * 1000;
const longitudeRad = deg2rad(longitudeDeg);
const radiusLatitude = RADIUS_EQUATOR * (1 - latitudeDeg / 90);
const x = radiusLatitude * Math.cos(longitudeRad);
const y = radiusLatitude * Math.sin(longitudeRad);
const z = altitudeMeters;
return [x, y, z];
}
/**
* Compute the Cartesian coordinates for the given GPS coordinates on a
* spherical earth.
*
* @param {[number, number, number]} pointGPS A GPS coordinate (latitude in degrees, longitude in degrees, altitude in meters).
* @returns {[number, number, number]} A Cartesian coordinate.
*/
function cartesianSphere([latitudeDeg, longitudeDeg, altitudeMeters]) {
const RADIUS_SPHERE = 6371 * 1000;
const radiusPoint = RADIUS_SPHERE + altitudeMeters;
const latitudeRad = deg2rad(latitudeDeg);
const longitudeRad = deg2rad(longitudeDeg);
const cosLat = Math.cos(latitudeRad);
const z = radiusPoint * Math.sin(latitudeRad);
const x = radiusPoint * cosLat * Math.cos(longitudeRad);
const y = radiusPoint * cosLat * Math.sin(longitudeRad);
return [x, y, z];
}
/**
* Compute the Cartesian coordinates for the given GPS coordinates on an
* ellipsoidal earth.
*
* @param {[number, number, number]} pointGPS A GPS coordinate (latitude in degrees, longitude in degrees, altitude in meters).
* @returns {[number, number, number]} A Cartesian coordinate.
*/
function cartesianEllipsoid([latitudeDeg, longitudeDeg, altitudeMeters]) {
const SEMI_MAJOR_AXIS = 6378137;
const FLATTENING_FACTOR = 1 / 298.257223563;
const ECCENTRICITY_SQUARED = FLATTENING_FACTOR * (2 - FLATTENING_FACTOR);
const latitudeRad = deg2rad(latitudeDeg);
const longitudeRad = deg2rad(longitudeDeg);
const cosLat = Math.cos(latitudeRad);
const sinLat = Math.sin(latitudeRad);
const radiusOfCurvature =
SEMI_MAJOR_AXIS / Math.sqrt(1 - ECCENTRICITY_SQUARED * sinLat * sinLat);
const x =
(radiusOfCurvature + altitudeMeters) * cosLat * Math.cos(longitudeRad);
const y =
(radiusOfCurvature + altitudeMeters) * cosLat * Math.sin(longitudeRad);
const z =
((1 - ECCENTRICITY_SQUARED) * radiusOfCurvature + altitudeMeters) * sinLat;
return [x, y, z];
}
/**
* Compute the Euclidean distance between two Cartesian coordinates.
*
* @param {[number, number, number]} point1Cartesian First Cartesian coordinate.
* @param {[number, number, number]} point2Cartesian Second Cartesian coordinate.
* @returns {number} The Euclidean distance between the coordinates.
*/
function euclideanDistance([x1, y1, z1], [x2, y2, z2]) {
const [dx, dy, dz] = [x2 - x1, y2 - y1, z2 - z1];
return Math.sqrt(dx * dx + dy * dy + dz * dz);
}
/**
* Convert an angle in degrees into radians.
*
* @param {number} angleDeg The angle in degrees.
* @returns {number} The angle in radians.
*/
function deg2rad(angleDeg) {
return angleDeg * (Math.PI / 180);
}
main();