UNPKG

flight-planner

Version:
178 lines (177 loc) 6.42 kB
import { degreesToRadians, radiansToDegrees } from '@turf/turf'; import { ISA_STANDARD_PRESSURE_HPA } from './index.js'; import convert from 'convert-units'; import CRC32 from 'crc-32'; import baseX from 'base-x'; /** * 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) => { 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, }; }; // --- Constants (ideally in a separate file) --- const SPECIFIC_GAS_CONSTANT_DRY_AIR = 287.05; // J/(kg·K) const STANDARD_SEA_LEVEL_DENSITY = 1.225; // kg/m^3 // const TEMP_LAPSE_RATE_C_PER_1000_FT = 1.98; 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 wcaInRadians = Math.asin(windVector.crosswind / airSpeed); 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); }; /** * 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(' '); }; /** * Generates a Base32 hash key from the given input. * * @param input - The input to hash * @returns The Base32 hash key */ export const generateBase32HashKey = (input) => { const BASE32 = baseX('ABCDEFGHIJKLMNOPQRSTUVWXYZ234567'); const hash = CRC32.str(input); const hashUint8 = new Uint8Array([ (hash >> 24) & 0xff, (hash >> 16) & 0xff, (hash >> 8) & 0xff, hash & 0xff, ]); return BASE32.encode(hashUint8); };