Jump to content

Pensacola 2026-02 Predictions: Difference between revisions

From Flerf Wiki
Bakta (talk | contribs)
m Added some formatting to satisfy my CDO...
Ion: Typo fix: Euclidean → Cartesian
 
(14 intermediate revisions by the same user not shown)
Line 17: Line 17:
: Feb 26 at 22:04:00 heading 280.4;  
: Feb 26 at 22:04:00 heading 280.4;  
: Feb 27 at 22:05:00 heading 280.8
: 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:
{| 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 ====
===== 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).
[[File:Ion_Pensacola_2026-02_GPS_Distance_Prediction_Flat_1.png|frameless]]
Let <math>r_{\text{equator}} = 10002 \text{ km}</math>.
The distance from the North Pole to a given latitude:
<math>
r_{\text{lat}} = r_{\text{equator}} \, \left(1 - \frac{\text{lat}}{90^\circ} \right)
</math>
The coordinates:
<math>
x = r_{\text{lat}} \, \cos(\text{lon})
</math>
<math>
y = r_{\text{lat}} \, \sin(\text{lon})
</math>
<math>
z = \text{altitude}
</math>
===== 2. Calculating the straight-line distance between two Cartesian coordinates =====
[[File:Ion_Pensacola_2026-02_GPS_Distance_Prediction_Flat_2.png|frameless]]
We use the Pythagorean theorem.
<math>
l = \sqrt{(x_2 - x_1)^2 + (y_2 - y_1)^2 + (z_2 - z_1)^2}
</math>
==== 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.
[[File:Ion_Pensacola_2026-02_GPS_Distance_Prediction_Globe_1.png|frameless]]
Let <math>r_{\text{point}} = 6371 \text{ km} + \text{altitude}</math>.
<math>
z = r_{\text{point}} \, \sin(\text{lat})
</math>
The x and y coordinates lie on a small circle scaled by the cosine of the latitude.
<math>
x = r_{\text{point}} \, \cos(\text{lat}) \, \cos(\text{lon})
</math>
<math>
y = r_{\text{point}} \, \cos(\text{lat}) \, \sin(\text{lon})
</math>
===== 2. Calculating the straight-line distance between two Cartesian coordinates =====
[[File:Ion_Pensacola_2026-02_GPS_Distance_Prediction_Globe_2.png|frameless]]
Again, we use the Pythagorean theorem.
<math>
l = \sqrt{(x_2 - x_1)^2 + (y_2 - y_1)^2 + (z_2 - z_1)^2}
</math>
==== 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:
<math>
a = 6378137 \text{ m}
</math>
The flattening factor:
<math>
f = \frac{1}{298.257223563}
</math>
Eccentricity squared:
<math>
e^2 = f \, (2 - f)
</math>
Radius of curvature:
<math>
N = \frac{a}{\sqrt{1 - e^2 \, \sin^2(\text{lat})}}
</math>
The coordinates:
<math>
x = (N + \text{altitude}) \, \cos(\text{lat}) \, \cos(\text{lon})
</math>
<math>
y = (N + \text{altitude}) \, \cos(\text{lat}) \, \sin(\text{lon})
</math>
<math>
z = \left((1 - e^2)N + \text{altitude}\right) \, \sin(\text{lat})
</math>
The Euclidean distance calculation between coordinates is once again done using the Pythagorean theorem as shown above.
==== Code ====
<pre>
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();
</pre>

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:

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

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 requator=10002 km.

The distance from the North Pole to a given latitude:

rlat=requator(1lat90)

The coordinates:

x=rlatcos(lon)

y=rlatsin(lon)

z=altitude

2. Calculating the straight-line distance between two Cartesian coordinates

We use the Pythagorean theorem.

l=(x2x1)2+(y2y1)2+(z2z1)2

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 rpoint=6371 km+altitude.

z=rpointsin(lat)

The x and y coordinates lie on a small circle scaled by the cosine of the latitude.

x=rpointcos(lat)cos(lon)

y=rpointcos(lat)sin(lon)

2. Calculating the straight-line distance between two Cartesian coordinates

Again, we use the Pythagorean theorem.

l=(x2x1)2+(y2y1)2+(z2z1)2

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:

a=6378137 m

The flattening factor:

f=1298.257223563

Eccentricity squared:

e2=f(2f)

Radius of curvature:

N=a1e2sin2(lat)

The coordinates:

x=(N+altitude)cos(lat)cos(lon)

y=(N+altitude)cos(lat)sin(lon)

z=((1e2)N+altitude)sin(lat)

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();