public CelestialLongitudes(int year, int julianDay, int hours) { double doubleHour = hours; int leapDaysSince1901 = ((year - 1901) / 4); //number of leap days between 1901 and Jan 1 of year double yearsSince1900 = year - 1900; //number of years since 1900 double additionalDays = julianDay + leapDaysSince1901 - 1; // julian Day + leap days. this.LongitudeOfMoonsNode = 259.1560564 - 19.328185764 * yearsSince1900 - .0529539336 * additionalDays - .0022064139 * doubleHour; this.LongitudeOfMoonsNode = MathFuncs.Angle(LongitudeOfMoonsNode); this.LongitudeOfLunarPerigee = 334.3837214 + 40.66246584 * yearsSince1900 + .111404016 * additionalDays + .004641834 * doubleHour; this.LongitudeOfLunarPerigee = MathFuncs.Angle(this.LongitudeOfLunarPerigee); this.LongitudeOfSun = 280.1895014 - .238724988 * yearsSince1900 + .9856473288 * additionalDays + .0410686387 * doubleHour; this.LongitudeOfSun = MathFuncs.Angle(LongitudeOfSun); this.LongitudeOfSolarPerigee = 281.2208569 + .01717836 * yearsSince1900 + .000047064 * additionalDays + .000001961 * doubleHour; this.LongitudeOfSolarPerigee = MathFuncs.Angle(LongitudeOfSolarPerigee); this.LongitudeOfMoon = 277.0256206 + 129.38482032 * yearsSince1900 + 13.176396768 * additionalDays + .549016532 * doubleHour; this.LongitudeOfMoon = MathFuncs.Angle(LongitudeOfMoon); // the following calculations are based on the Manual Of Harmonic Analysis and Prediction of Tides by Paul Schureman // I, from Schureman p156 first equation. Values for each degree of longitude of Moon's node given in Schureman table 6. double N = MathFuncs.DegreesToRadians(LongitudeOfMoonsNode); double IRadians = Math.Acos(.9136949 - .0356926 * Math.Cos(N)); this.Schureman_I = MathFuncs.Angle(MathFuncs.RadiansToDegrees(IRadians)); // little V, Schureman p156. Values for each degree of longitude of Moon's node given in Schureman table 6. double vRadians = Math.Asin(.0897056 * Math.Sin(N) / Math.Sin(IRadians)); this.Schureman_v = MathFuncs.RadiansToDegrees(vRadians); //v // epsilon, Schureman p 156, third equation. Values for each degree of longitude of Moon's node given in Schureman table 6. double epsilonRadians = N - 2.0 * Math.Atan(.64412 * Math.Tan(N / 2.0)) - vRadians; this.Schureman_e = MathFuncs.RadiansToDegrees(epsilonRadians); // used for node factor calculations double NUP = Math.Atan(Math.Sin(vRadians) / (Math.Cos(vRadians) + .334766 / Math.Sin(2.0 * IRadians))); this.DNUP = MathFuncs.RadiansToDegrees(NUP); // used for node factor calculations double NUP2 = Math.Atan(Math.Sin(2.0 * vRadians) / (Math.Cos(2.0 * vRadians) + .0726184 / (Math.Sin(IRadians) * Math.Sin(IRadians)))) / 2.0; this.DNUP2 = MathFuncs.RadiansToDegrees(NUP2); // P is the mean longitude of the lunar perigee reckoned from the lunar intersection. // Equation 191, Schureman p 41 this.Schureman_P = MathFuncs.Angle(this.LongitudeOfLunarPerigee - this.Schureman_e); }
/// <summary> /// Calculate node factors that are used to modify the amplitude of each tidal constituent /// </summary> /// <param name="epochStartYear">The epoch start year</param> /// <param name="epochStartJulianDay">The epoch start day</param> /// <param name="halfEpochLengthInHours">One half of epoch length in hours</param> private void CalculateNodeFactors(int epochStartYear, int epochStartJulianDay, int halfEpochLengthInHours) { CelestialLongitudes middleOfEpochLongitudes = new CelestialLongitudes(epochStartYear, epochStartJulianDay, halfEpochLengthInHours); ///CalculateCelestialLongitudes(epochStartYear, epochStartJulianDay, halfEpochLengthInHours); double N = MathFuncs.DegreesToRadians(middleOfEpochLongitudes.LongitudeOfMoonsNode); double I = MathFuncs.DegreesToRadians(middleOfEpochLongitudes.Schureman_I); double NU = MathFuncs.DegreesToRadians(middleOfEpochLongitudes.Schureman_v); double XI = MathFuncs.DegreesToRadians(middleOfEpochLongitudes.Schureman_e); double P = MathFuncs.DegreesToRadians(middleOfEpochLongitudes.LongitudeOfLunarPerigee); double PC = MathFuncs.DegreesToRadians(middleOfEpochLongitudes.Schureman_P); double SINI = Math.Sin(I); double SINI2 = Math.Sin(I / 2.0); double SIN2I = Math.Sin(2.0 * I); double COSI2 = Math.Cos(I / 2.0); double TANI2 = Math.Tan(I / 2.0); // Equations are from Schureman pp 25-45 double EQ197 = Math.Sqrt(2.310 + 1.435 * Math.Cos(2.0 * PC)); double EQ213 = Math.Sqrt(1.0 - 12.0 * TANI2 * TANI2 * Math.Cos(2.0 * PC) + 36.0 * TANI2 * TANI2 * TANI2 * TANI2); double EQ73 = (2.0 / 3.0 - SINI * SINI) / 0.5021; double EQ74 = SINI * SINI / 0.1578; double EQ75 = SINI * COSI2 * COSI2 / 0.37988; double EQ76 = Math.Sin(2.0 * I) / 0.7214; double EQ77 = SINI * SINI2 * SINI2 / 0.0164; double EQ78 = (COSI2 * COSI2 * COSI2 * COSI2) / 0.91544; double EQ149 = COSI2 * COSI2 * COSI2 * COSI2 * COSI2 * COSI2 / 0.8758; double EQ207 = EQ75 * EQ197; double EQ215 = EQ78 * EQ213; double EQ227 = Math.Sqrt(0.8965 * SIN2I * SIN2I + 0.6001 * SIN2I * Math.Cos(NU) + 0.1006); double EQ235 = 0.001 + Math.Sqrt(19.0444 * SINI * SINI * SINI * SINI + 2.7702 * SINI * SINI * Math.Cos(2.0 * NU) + 0.0981); // Node factor calculations from Schureman p 25 this.Constituents[Constants.M2].NodeFactor = EQ78; this.Constituents[Constants.S2].NodeFactor = 1.0; this.Constituents[Constants.N2].NodeFactor = EQ78; this.Constituents[Constants.K1].NodeFactor = EQ227; this.Constituents[Constants.M4].NodeFactor = EQ78 * EQ78; this.Constituents[Constants.O1].NodeFactor = EQ75; this.Constituents[Constants.M6].NodeFactor = EQ78 * EQ78 * EQ78; this.Constituents[Constants.MK3].NodeFactor = EQ78 * EQ227; this.Constituents[Constants.S4].NodeFactor = 1.0; this.Constituents[Constants.MN4].NodeFactor = EQ78 * EQ78; this.Constituents[Constants.NU2].NodeFactor = EQ78; this.Constituents[Constants.S6].NodeFactor = 1.0; this.Constituents[Constants.MU2].NodeFactor = EQ78; this.Constituents[Constants._2N2].NodeFactor = EQ78; this.Constituents[Constants.OO1].NodeFactor = EQ77; this.Constituents[Constants.LAMBDA2].NodeFactor = EQ78; this.Constituents[Constants.S1].NodeFactor = 1.0; // equation 207 not working. //this.Constituents[Constants.M1].NodeFactor = EQ207; this.Constituents[Constants.M1].NodeFactor = 1.0; this.Constituents[Constants.J1].NodeFactor = EQ76; this.Constituents[Constants.MM].NodeFactor = EQ73; this.Constituents[Constants.SSA].NodeFactor = 1.0; this.Constituents[Constants.SA].NodeFactor = 1.0; this.Constituents[Constants.MSF].NodeFactor = EQ78; this.Constituents[Constants.MF].NodeFactor = EQ74; this.Constituents[Constants.RHO1].NodeFactor = EQ75; this.Constituents[Constants.Q1].NodeFactor = EQ75; this.Constituents[Constants.T2].NodeFactor = 1.0; this.Constituents[Constants.R2].NodeFactor = 1.0; this.Constituents[Constants._2Q1].NodeFactor = EQ75; this.Constituents[Constants.P1].NodeFactor = 1.0; this.Constituents[Constants._2SM2].NodeFactor = EQ78; this.Constituents[Constants.M3].NodeFactor = EQ149; // equation 215 is not working. //this.Constituents[Constants.L2].NodeFactor = EQ215; this.Constituents[Constants.L2].NodeFactor = 1.0; this.Constituents[Constants._2MK3].NodeFactor = EQ78 * EQ78 * EQ227; this.Constituents[Constants.K2].NodeFactor = EQ235; this.Constituents[Constants.M8].NodeFactor = EQ78 * EQ78 * EQ78 * EQ78; this.Constituents[Constants.MS4].NodeFactor = EQ78; }
/// <summary> /// Calculate the phases of all constituents of the theoretical equilibrium tide, for the start of the epoch /// </summary> /// <param name="epochStartYear">Year of epoch start</param> /// <param name="epochStartJulianDay">day of epoch start</param> /// <param name="epochStartHour">hour of epoch start</param> /// <param name="halfEpochLengthInHours">One half of epoch length in hours</param> private void CalculateEquilibriumPhases(int epochStartYear, int epochStartJulianDay, int epochStartHour, int halfEpochLengthInHours) { // get celestial longitudes for start of the epoch CelestialLongitudes startOfEpochLongitudes = new CelestialLongitudes(epochStartYear, epochStartJulianDay, epochStartHour); double S = startOfEpochLongitudes.LongitudeOfMoon; double P = startOfEpochLongitudes.LongitudeOfLunarPerigee; double H = startOfEpochLongitudes.LongitudeOfSun; double P1 = startOfEpochLongitudes.LongitudeOfSolarPerigee; double T = MathFuncs.Angle(180.0 + epochStartHour * (360.0 / 24.0)); // hour angle of mean sun at place of observation // get values based on longitude of moon's node, these are calculated from the middle of the epoch. CelestialLongitudes middleOfEpochLongitudes = new CelestialLongitudes(epochStartYear, epochStartJulianDay, halfEpochLengthInHours); double NU = middleOfEpochLongitudes.Schureman_v; //v double XI = middleOfEpochLongitudes.Schureman_e; //e double NUP = middleOfEpochLongitudes.DNUP; double NUP2 = middleOfEpochLongitudes.DNUP2; // now fill in the equilibrium phases of the Harmonic Constituents. // equations from The Manual of Harmonic analysis and Prediction of Tides by Paul Schureman page 22. this.Constituents[Constants.M2].EquilibriumPhase = MathFuncs.Angle(2.0 * (T - S + H) + 2.0 * (XI - NU)); this.Constituents[Constants.S2].EquilibriumPhase = MathFuncs.Angle(2.0 * T); this.Constituents[Constants.N2].EquilibriumPhase = MathFuncs.Angle(2.0 * (T + H) - 3.0 * S + P + 2.0 * (XI - NU)); this.Constituents[Constants.K1].EquilibriumPhase = MathFuncs.Angle(T + H - 90.0 - NUP); this.Constituents[Constants.M4].EquilibriumPhase = MathFuncs.Angle(4.0 * (T - S + H) + 4.0 * (XI - NU)); this.Constituents[Constants.O1].EquilibriumPhase = MathFuncs.Angle(T - 2.0 * S + H + 90.0 + 2.0 * XI - NU); this.Constituents[Constants.M6].EquilibriumPhase = MathFuncs.Angle(6.0 * (T - S + H) + 6.0 * (XI - NU)); this.Constituents[Constants.MK3].EquilibriumPhase = MathFuncs.Angle(3.0 * (T + H) - 2.0 * S - 90.0 + 2.0 * (XI - NU) - NUP); this.Constituents[Constants.S4].EquilibriumPhase = MathFuncs.Angle(4.0 * T); this.Constituents[Constants.MN4].EquilibriumPhase = MathFuncs.Angle(4.0 * (T + H) - 5.0 * S + P + 4.0 * (XI - NU)); this.Constituents[Constants.NU2].EquilibriumPhase = 2.0 * T - 3.0 * S + 4.0 * H - P + 2.0 * (XI - NU); this.Constituents[Constants.S6].EquilibriumPhase = 6.0 * T; this.Constituents[Constants.MU2].EquilibriumPhase = 2.0 * (T + 2.0 * (H - S)) + 2.0 * (XI - NU); this.Constituents[Constants._2N2].EquilibriumPhase = 2.0 * (T - 2.0 * S + H + P) + 2.0 * (XI - NU); this.Constituents[Constants.OO1].EquilibriumPhase = T + 2.0 * S + H - 90.0 - 2.0 * XI - NU; this.Constituents[Constants.LAMBDA2].EquilibriumPhase = 2.0 * T - S + P + 180.0 + 2.0 * (XI - NU); this.Constituents[Constants.S1].EquilibriumPhase = T; double I = MathFuncs.DegreesToRadians(middleOfEpochLongitudes.Schureman_I); double PC = MathFuncs.DegreesToRadians(middleOfEpochLongitudes.Schureman_P); double TOP = (5.0 * Math.Cos(I) - 1.0) * Math.Sin(PC); double BOTTOM = (7.0 * Math.Cos(I) + 1.0) * Math.Cos(PC); double Q = MathFuncs.Arctan(TOP, BOTTOM, 1); this.Constituents[Constants.M1].EquilibriumPhase = T - S + H - 90.0 + XI - NU + Q; this.Constituents[Constants.J1].EquilibriumPhase = T + S + H - P - 90.0 - NU; this.Constituents[Constants.MM].EquilibriumPhase = S - P; this.Constituents[Constants.SSA].EquilibriumPhase = 2.0 * H; this.Constituents[Constants.SA].EquilibriumPhase = H; this.Constituents[Constants.MSF].EquilibriumPhase = 2.0 * (S - H); this.Constituents[Constants.MF].EquilibriumPhase = 2.0 * S - 2.0 * XI; this.Constituents[Constants.RHO1].EquilibriumPhase = T + 3.0 * (H - S) - P + 90.0 + 2.0 * XI - NU; this.Constituents[Constants.Q1].EquilibriumPhase = T - 3.0 * S + H + P + 90.0 + 2.0 * XI - NU; this.Constituents[Constants.T2].EquilibriumPhase = 2.0 * T - H + P1; this.Constituents[Constants.R2].EquilibriumPhase = 2.0 * T + H - P1 + 180.0; this.Constituents[Constants._2Q1].EquilibriumPhase = T - 4.0 * S + H + 2.0 * P + 90.0 + 2.0 * XI - NU; this.Constituents[Constants.P1].EquilibriumPhase = T - H + 90.0; this.Constituents[Constants._2SM2].EquilibriumPhase = 2.0 * (T + S - H) + 2.0 * (NU - XI); this.Constituents[Constants.M3].EquilibriumPhase = 3.0 * (T - S + H) + 3.0 * (XI - NU); double R = Math.Sin(2.0 * PC) / ((1.0 / 6.0) * (1.0 / Math.Tan(0.5 * I)) * (1.0 / Math.Tan(0.5 * I)) - Math.Cos(2.0 * PC)); R = MathFuncs.RadiansToDegrees(Math.Atan(R)); this.Constituents[Constants.L2].EquilibriumPhase = 2.0 * (T + H) - S - P + 180.0 + 2.0 * (XI - NU) - R; this.Constituents[Constants._2MK3].EquilibriumPhase = 3.0 * (T + H) - 4.0 * S + 90.0 + 4.0 * (XI - NU) + NUP; this.Constituents[Constants.K2].EquilibriumPhase = 2.0 * (T + H) - 2.0 * NUP2; this.Constituents[Constants.M8].EquilibriumPhase = 8.0 * (T - S + H) + 8.0 * (XI - NU); this.Constituents[Constants.MS4].EquilibriumPhase = 2.0 * (2.0 * T - S + H) + 2.0 * (XI - NU); for (int index = 0; index < Constants.ConstitutentArraySize; index++) { this.Constituents[index].EquilibriumPhase = MathFuncs.Angle(this.Constituents[index].EquilibriumPhase); } }
/// <summary> /// this is the derivative of the RateOfChange function, it returns the acceleration of the tide /// Same units (probably feet or meters) per hour per hour as the amplitude /// </summary> /// <param name="hours">hours since epoch start</param> /// <returns>the acceleration of the tide</returns> public double AccelerationOfChange(double hours) { return(-1 * Amplitude * NodeFactor * (Math.PI / 180) * Speed * (Math.PI / 180) * Speed * Math.Cos(MathFuncs.DegreesToRadians(Speed * hours + EquilibriumPhase - Phase))); }
/// <summary> /// Calculate the height of this constituent. Same units (probably feet or meters) as the amplitude /// </summary> /// <param name="h">the number of hours since the start of the epoch that the equilibrium phases are based on.</param> /// <returns>the height of this harmonic constituent</returns> public double Height(double hours) { return(Amplitude * NodeFactor * Math.Cos(MathFuncs.DegreesToRadians(Speed * hours + EquilibriumPhase - Phase))); }