UNPKG

flight-planner

Version:
183 lines (182 loc) 6.9 kB
import { bbox, circle, degreesToRadians, point, radiansToDegrees } from '@turf/turf'; import { ISA_STANDARD_PRESSURE_HPA } from './constants.js'; import convert from 'convert-units'; /** * Calculates the wind vector relative to the given true track. * * @param wind - An object representing the wind, containing direction and speed. * @param trueTrack - The current true track in degrees. * @returns An object containing the wind angle, headwind, and crosswind components. */ export const calculateWindVector = (wind, trueTrack) => { if (wind.direction === undefined) { return { angle: 0, headwind: 0, crosswind: 0 }; } const windAngle = wind.direction - trueTrack; const windAngleRad = degreesToRadians(windAngle); const cosAngle = Math.cos(windAngleRad); const sinAngle = Math.sin(windAngleRad); const headwind = wind.speed * cosAngle; const crosswind = wind.speed * sinAngle; return { angle: windAngle, headwind: headwind, crosswind: crosswind, }; }; const SPECIFIC_GAS_CONSTANT_DRY_AIR = 287.05; // J/(kg·K) const STANDARD_SEA_LEVEL_DENSITY = 1.225; // kg/m^3 const FT_PER_HPA_APPROX = 27.3; const convertPressureAltitudeToStaticPressureHpa = (pressureAltitudeFt) => { // Standard formula for pressure from ICAO Standard Atmosphere const tempRatio = 1 - (0.0065 * (pressureAltitudeFt * 0.3048)) / 288.15; return ISA_STANDARD_PRESSURE_HPA * Math.pow(tempRatio, 5.25588); }; /** * Calculates air density. * * @param oatCelsius Outside Air Temperature in Celsius. * @param staticPressureHpa Static air pressure in hPa. * @returns Air density in kg/m^3. */ const calculateAirDensity = (oatCelsius, staticPressureHpa) => { const T_kelvin = convert(oatCelsius).from('C').to('K'); const P_pascals = staticPressureHpa * 100; return P_pascals / (SPECIFIC_GAS_CONSTANT_DRY_AIR * T_kelvin); }; /** * Calculates True Airspeed. * * @param indicatedAltitudeFt Indicated altitude in feet. * @param qnhHpa Altimeter setting in hPa. * @param oatCelsius Outside Air Temperature in Celsius. * @param kcas Knots Calibrated Airspeed (or KEAS if compressibility is negligible). * @returns Knots True Airspeed. */ export const calculateTrueAirspeed = (indicatedAltitudeFt, qnhHpa, oatCelsius, kcas // Assuming this is KCAS; for low speeds, KEAS approx KCAS ) => { const pressureDiffHpa = qnhHpa - ISA_STANDARD_PRESSURE_HPA; const pressureAltitudeFt = indicatedAltitudeFt - (pressureDiffHpa * FT_PER_HPA_APPROX); const staticPressureHpa = convertPressureAltitudeToStaticPressureHpa(pressureAltitudeFt); const airDensityKgM3 = calculateAirDensity(oatCelsius, staticPressureHpa); // For simplicity here, assuming KEAS approx = KCAS. // A full solution might add a KCAS -> KEAS step. const keas = kcas; const trueAirspeed = keas * Math.sqrt(STANDARD_SEA_LEVEL_DENSITY / airDensityKgM3); return trueAirspeed; }; /** * Calculates the wind correction angle for the given wind, true track, and airspeed. * * @param wind - An object representing the wind, containing direction and speed. * @param trueTrack - The true track in degrees. * @param airSpeed - The airspeed in knots. * @returns The wind correction angle in degrees. */ export const calculateWindCorrectionAngle = (wind, trueTrack, airSpeed) => { const windVector = calculateWindVector(wind, trueTrack); const ratio = Math.max(-1, Math.min(1, windVector.crosswind / airSpeed)); const wcaInRadians = Math.asin(ratio); const wca = radiansToDegrees(wcaInRadians); return wca; // Positive WCA means wind from the right }; /** * Calculates the groundspeed for the given wind, airspeed, and heading. * * @param wind - An object representing the wind, containing degrees and speed. * @param airSpeed - The airspeed in knots. * @param heading - The heading in degrees. * @returns The groundspeed in knots. */ export const calculateGroundspeed = (wind, airSpeed, heading) => { const windVector = calculateWindVector(wind, heading); const groundspeedSquared = Math.pow(airSpeed, 2) + Math.pow(wind.speed, 2) - 2 * airSpeed * windVector.headwind; return Math.sqrt(groundspeedSquared); }; /** * Creates a bounding box around a geographic center point with a specified radius. * * This function generates a circular polygon around the center point and returns * its bounding box, which can be used for geographic searches or spatial queries. * * @param center - The center point as a GeoJSON Position [longitude, latitude] * @param radiusKm - The radius in kilometers to extend from the center point * @returns A GeoJSON BBox [minLon, minLat, maxLon, maxLat] encompassing the circular area * * @example * ```typescript * // Create a bounding box 50km around Amsterdam * const bbox = createBoundingBox([4.9041, 52.3676], 50); * // Returns: [4.254, 51.918, 5.554, 52.817] * ``` */ export const createBoundingBox = (center, radiusKm) => { const locationPoint = point(center); const circlePolygon = circle(locationPoint, radiusKm, { units: 'kilometers' }); return bbox(circlePolygon); }; /** * Sorts an array of clouds by their height in ascending order. * * @param clouds - The array of clouds to sort * @returns The sorted array of clouds */ export const sortClouds = (clouds) => { if (!clouds || clouds.length === 0) return []; return [...clouds].sort((a, b) => { if (a.height === undefined) return 1; if (b.height === undefined) return -1; return a.height - b.height; }); }; /** * Checks if the given string is a valid ICAO code. * * @param icao - The string to check * @returns True if the string is a valid ICAO code, false otherwise */ export const isICAO = (icao) => { return /^[A-Z]{4}$/.test(icao.toUpperCase()); }; /** * Normalizes the given ICAO code to uppercase. * * @param icao - The ICAO code to normalize * @returns The normalized ICAO code */ export const normalizeICAO = (icao) => { return icao.toUpperCase(); }; /** * Checks if the given string is a valid IATA code. * * @param iata - The string to check * @returns True if the string is a valid IATA code, false otherwise */ export const isIATA = (iata) => { return /^[A-Z]{3}$/.test(iata.toUpperCase()); }; /** * Normalizes the given IATA code to uppercase. * * @param iata - The IATA code to normalize * @returns The normalized IATA code */ export const normalizeIATA = (iata) => { return iata.toUpperCase(); }; /** * Capitalizes the first letter of each word in a string * * @param text - The input text to capitalize * @returns The text with the first letter of each word capitalized */ export const capitalizeWords = (text) => { return text.toLowerCase() .split(' ') .map((word) => word.charAt(0).toUpperCase() + word.slice(1)) .join(' '); };