public static float BodyNoiseTemp(RealAntenna rx, CelestialBody body, Vector3d rxPointing) => BodyNoiseTemp(new double3(rx.PrecisePosition.x, rx.PrecisePosition.y, rx.PrecisePosition.z), rx.Gain, new double3(rxPointing.x, rxPointing.y, rxPointing.z), new double3(body.position.x, body.position.y, body.position.z), (float)body.Radius, body.isStar ? StarRadioTemp(BodyBaseTemperature(body), rx.Frequency) : BodyBaseTemperature(body));
public static double BodyNoiseTemp(RealAntenna rx, CelestialBody body, Vector3d rxPointing) { if (rx.Shape == AntennaShape.Omni) { return(0); // No purpose in per-body noise temp for an omni. } Vector3 toBody = body.position - rx.Position; double angle = Vector3.Angle(rxPointing, toBody); // if (rx.GainAtAngle(Convert.ToSingle(angle)) < 0) if (rx.Beamwidth < angle) { return(0); // Pointed too far away } double bw = rx.Beamwidth; double t = body.GetTemperature(1); // TODO: Get the BLACKBODY temperature! double d = body.Radius * 2; double Rsqr = (rx.Position - body.position).sqrMagnitude; double G = RATools.LinearScale(rx.Gain); double angleRatio = angle / bw; double result = (t * G * d * d / (16 * Rsqr)) * Math.Pow(Math.E, -2.77 * angleRatio * angleRatio); // Debug.LogFormat("Planetary Body Noise Power Estimator: Body {0} base temp {1:F0} diameter {2:F0}km at {3:F2}Mm Gain {4:F1} vs HPBW {5:F1} incident angle {6:F1} yields {7:F1}K", body, t, d/1000, (rx.Position - body.position).magnitude/1e6, G, bw, angle, result); return(result); }
public RealAntennaDigital(RealAntenna orig) : base(orig) { if (orig is RealAntennaDigital o) { modulator = new RAModulator(o.modulator); } }
private static double AntennaMicrowaveTemp(RealAntenna rx) { if ((rx.ParentNode as RACommNode)?.ParentVessel is Vessel) { return(rx.TechLevelInfo.ReceiverNoiseTemperature); } return(rx.AMWTemp); }
public static float AtmosphericTemp(RealAntenna rx, Vector3d origin) { if (rx.ParentNode is RACommNode rxNode && rxNode.ParentBody != null) { Vector3d normal = rxNode.GetSurfaceNormalVector(); return(AtmosphericTemp(new double3(rx.Position.x, rx.Position.y, rx.Position.z), new double3(normal.x, normal.y, normal.z), new double3(origin.x, origin.y, origin.z), rx.Frequency)); } return(0); }
void GUIDisplay(int windowID) { string s = $"{(sourceAntenna?.ParentNode as RACommNode)?.ParentVessel?.vesselName} {sourceAntenna?.ToStringShort()}"; GUILayout.Label($"Antenna: {(sourceAntenna is RealAntenna ? s : "Not Selected")}"); GUILayout.BeginHorizontal(); GUILayout.BeginVertical(); if (GUILayout.Button($"Source Sort Mode: {sourceSortMode}")) { //(i1, i2) => i1.ToString().CompareTo(i2.ToString()) sourceSortMode = (SortMode)(((int)(sourceSortMode + 1)) % System.Enum.GetNames(typeof(SortMode)).Length); SortList(sourceVessels, sourceSortMode); } scrollSourcePos = GUILayout.BeginScrollView(scrollSourcePos, GUILayout.ExpandWidth(true)); foreach (var ra in from Vessel v in sourceVessels from RealAntenna ra in (v.Connection?.Comm as RACommNode)?.RAAntennaList?.Where(x => x.CanTarget) select ra) { if (GUILayout.Button($"{(ra.ParentNode as RACommNode)?.ParentVessel?.vesselName} {ra.ToStringShort()}")) { sourceAntenna = ra; } } GUILayout.EndScrollView(); GUILayout.EndVertical(); GUILayout.BeginVertical(); if (GUILayout.Button($"Target Sort Mode: {targetSortMode}")) { targetSortMode = (SortMode)(((int)(targetSortMode + 1)) % System.Enum.GetNames(typeof(SortMode)).Length); SortList(targetVessels, targetSortMode); } scrollTargetPos = GUILayout.BeginScrollView(scrollTargetPos, GUILayout.ExpandWidth(true)); foreach (var v in targetVessels) { if (sourceAntenna is RealAntenna && GUILayout.Button($"{v.vesselName}")) { sourceAntenna.Target = v; } } foreach (var v in FlightGlobals.Bodies) { if (sourceAntenna is RealAntenna && GUILayout.Button($"{v.name}")) { sourceAntenna.Target = v; } } GUILayout.EndScrollView(); GUILayout.EndVertical(); GUILayout.EndHorizontal(); GUI.DragWindow(); }
private static double AtmosphericTemp(RealAntenna rx, Vector3d origin) { if (rx.ParentNode is RACommNode rxNode && rxNode.ParentBody != null) { Vector3d normal = rxNode.GetSurfaceNormalVector(); Vector3d to_origin = origin - rx.Position; double angle = Vector3d.Angle(normal, to_origin); double elevation = Math.Max(0, 90.0 - angle); return(AtmosphereNoiseTemperature(elevation, rx.Frequency)); } return(0); }
private static float CosmicBackgroundTemp(RealAntenna rx, Vector3d origin) { float temp = 3; if (rx.ParentNode is RACommNode rxNode) { Vector3d normal = (rxNode.ParentBody is CelestialBody) ? rxNode.GetSurfaceNormalVector() : Vector3d.zero; Vector3d to_origin = origin - rx.Position; temp = CosmicBackgroundTemp(new double3(normal.x, normal.y, normal.z), new double3(to_origin.x, to_origin.y, to_origin.z), rx.Frequency, rxNode.isHome); } return(temp); }
private static double CosmicBackgroundTemp(RealAntenna rx, Vector3d origin) { double CMB = 2.725; double lossFactor = 1; if (rx.ParentNode is RACommNode rxNode && rxNode.ParentBody != null) { float CD = 0.5f; Vector3d normal = rxNode.GetSurfaceNormalVector(); Vector3d to_origin = origin - rx.Position; double angle = Vector3d.Angle(normal, to_origin); double elevation = Math.Max(0, 90.0 - angle); lossFactor = RATools.LinearScale(AtmosphereAttenuation(CD, elevation, rx.Frequency)); } return(CMB / lossFactor); }
public static RealAntenna HighestGainCompatibleDSNAntenna(List <CommNode> nodes, RealAntenna peer) { RealAntenna result = null; double highestGain = 0; foreach (RACommNode node in nodes.Where(obj => obj.isHome)) { foreach (RealAntenna ra in node.RAAntennaList) { if (peer.Compatible(ra) && ra.Gain > highestGain) { highestGain = ra.Gain; result = ra; } } } return(result); }
public static double AllBodyTemps(RealAntenna rx, Vector3d rxPointing) { double temp = 0; if (rx.Shape != AntennaShape.Omni) { Profiler.BeginSample("RA Physics AllBodyTemps MainLoop"); RACommNode node = rx.ParentNode as RACommNode; // Note there are ~33 bodies in RSS. foreach (CelestialBody body in FlightGlobals.Bodies) { if (!node.isHome || !node.ParentBody.Equals(body)) { temp += BodyNoiseTemp(rx, body, rxPointing); } } Profiler.EndSample(); } return(temp); }
public static double NoiseTemperature(RealAntenna rx, Vector3d origin) { double amt = AntennaMicrowaveTemp(rx); double atmos = AtmosphericTemp(rx, origin); double cosmic = CosmicBackgroundTemp(rx, origin); //double allbody = (rx.ParentNode.isHome) ? AllBodyTemps(rx, origin - rx.Position) : AllBodyTemps(rx, rx.ToTarget); // Home Stations are directional, but treated as always pointing towards the peer. double allbody = (rx.ParentNode.isHome) ? AllBodyTemps(rx, origin - rx.Position) : rx.cachedRemoteBodyNoiseTemp; double total = amt + atmos + cosmic + allbody; // Debug.LogFormat("NoiseTemp: Antenna {0:F2} Atmos: {1:F2} Cosmic: {2:F2} Bodies: {3:F2} Total: {4:F2}", amt, atmos, cosmic, allbody, total); return(total); // // https://www.itu.int/dms_pubrec/itu-r/rec/p/R-REC-P.372-7-200102-S!!PDF-E.pdf // // Sky Temperature: curves that vary based on atmosphere composition of a body. // Earth example: http://www.delmarnorth.com/microwave/requirements/satellite_noise.pdf // <10 deg K @ <15GHz and >30deg elevation in clear weather // Rain can be a very large contributor, tho. // At 0 deg elevation, sky temperature is effectively Earth ambient temperature = 290K. // An omni antenna near Earth has a temperature = Earth ambient temperature = 290K. // A directional antenna on a satellite pointed at Earth s.t. the entire main lobe of the ant // is the Earth also has a noise temperature ~= 290K. // A directional antenna pointed at the sun s.t. its main lobe encompasses ONLY the sun // (so very directional) has an antenna temperature ~= 6000K? // Ref: Galactic noise is high below 1000 MHz. At around 150 MHz, it is approximately 1000 K. At 2500 MHz, it has leveled off to around 10 K. // Atmospheric absorption: Earth example: <1dB @ <20GHz // Rainfall has a specific attenuation from absorption, and // and a correlated contribution to sky temperature from re-emission. // We should either track the effective temperature of the receiver electronics, // or calculate it from its more conventional notation (to me) of noise figure. // So we can get system temperature = antenna effective temperature + sky temperature + receiver effective temperature // Sky we can "define" based on the near celestial body, or set as 3K for deep space. // Receiver effective temperature we can derive from a noise figure. // Antenna temperature is basically the notion of "sky" temperature + rain temperature, or 3-4K in deep space. // // P(dBW) = 10*log10(Kb*T*bandwidth) = -228.59917 + 10*log10(T*BW) }
public static double ReceivedPower(RealAntenna tx, RealAntenna rx, double distance, double frequency = 1e9) => tx.TxPower + tx.Gain - PathLoss(distance, frequency) - PointingLoss(tx, rx.Position) - PointingLoss(rx, tx.Position) + rx.Gain;
private bool RenderPanel(SelectionMode mode, ref RealAntenna antenna, ref Vector2 scrollPos, in RealAntenna peer, GUIStyle buttonStyle)
private static double AntennaMicrowaveTemp(RealAntenna rx) => ((rx.ParentNode as RACommNode)?.ParentBody is CelestialBody) ? rx.AMWTemp : rx.TechLevelInfo.ReceiverNoiseTemperature;
public static double NoiseFloor(RealAntenna rx, double noiseTemp) => NoiseSpectralDensity(noiseTemp) + (10 * Math.Log10(rx.Bandwidth));
private bool RenderPanel(SelectionMode mode, ref RealAntenna antenna, ref Vector2 scrollPos) { bool res = false; scrollPos = GUILayout.BeginScrollView(scrollPos); if (mode == SelectionMode.Vessel) { if (HighLogic.LoadedSceneIsEditor) { foreach (var x in protoVesselAntennaCache) { foreach (RealAntenna ra in x.Value) { if (GUILayout.Button($"{x.Key.vesselName} {ra.ToStringShort()}")) { antenna = ra; res = true; } } } ShipConstruct sc = EditorLogic.RootPart.ship; foreach (Part p in sc.Parts) { foreach (ModuleRealAntenna mra in p.FindModulesImplementing <ModuleRealAntenna>()) { if (GUILayout.Button($"{sc.shipName} {mra.RAAntenna.ToStringShort()}")) { antenna = mra.RAAntenna; res = true; } } } } foreach (Vessel v in FlightGlobals.Vessels.Where(x => x.Connection?.Comm is RACommNode)) { foreach (RealAntenna ra in (v.Connection.Comm as RACommNode).RAAntennaList) { if (GUILayout.Button($"{v.GetDisplayName()} {ra.ToStringShort()}")) { antenna = ra; res = true; } } } } else { foreach (Network.RACommNetHome home in RACommNetScenario.GroundStations.Values.Where(x => x.Comm is RACommNode)) { foreach (RealAntenna ra in home.Comm.RAAntennaList) { if (GUILayout.Button($"{home.nodeName} {ra.ToStringShort()}")) { antenna = ra; res = true; } } } } GUILayout.EndScrollView(); return(res); }
public override double BestDataRateToPeer(RealAntenna rx) { double dataRate = (BestPeerModulator(rx, out double modRate, out double codeRate)) ? modRate * codeRate : 0; return(dataRate); }
public static double PointingLoss(RealAntenna ant, Vector3 origin) => (ant.CanTarget && ant.ToTarget != Vector3.zero) ? PointingLoss(Vector3.Angle(origin - ant.Position, ant.ToTarget), ant.Beamwidth) : 0;
public static float ReceivedPower(RealAntenna tx, RealAntenna rx, float distance, float frequency = 1e9f) => tx.TxPower + tx.Gain - PathLoss(distance, frequency) - PointingLoss(tx, rx.Position) - PointingLoss(rx, tx.Position) + rx.Gain;
public static double BodyNoiseTemp(RealAntenna rx, CelestialBody body, Vector3d rxPointing) { if (rx.Shape == AntennaShape.Omni) { return(0); // No purpose in per-body noise temp for an omni. } Vector3 toBody = body.position - rx.Position; double angle = Vector3.Angle(rxPointing, toBody); double distance = toBody.magnitude; double bodyRadiusAngularRad = (distance > 10 * body.Radius) ? Math.Atan2(body.Radius, distance) : MathUtils.AngularRadius(body.Radius, distance) * Mathf.Deg2Rad; double bodyRadiusAngularDeg = bodyRadiusAngularRad * Mathf.Rad2Deg; if (rx.Beamwidth < angle - bodyRadiusAngularDeg) { return(0); // Pointed too far away } double baseTemp = body.atmosphere ? body.GetTemperature(1) : GetEquilibriumTemperature(body) + body.coreTemperatureOffset; double t = body.isStar ? StarRadioTemp(baseTemp, rx.Frequency) : baseTemp; // TODO: Get the BLACKBODY temperature! if (t < double.Epsilon) { return(0); } double angleRad = angle * Mathf.Deg2Rad; double beamwidthRad = rx.Beamwidth * Mathf.Deg2Rad; double gainDelta; // Antenna Pointing adjustment double viewedAreaBase; // How much of the body is in view of the antenna? if (rx.Beamwidth < bodyRadiusAngularDeg - angle) // Antenna viewable area completely enclosed by body { viewedAreaBase = Mathf.PI * beamwidthRad * beamwidthRad; gainDelta = 0; } else if (rx.Beamwidth > bodyRadiusAngularDeg + angle) // Antenna viewable area completely encloses body { viewedAreaBase = Mathf.PI * bodyRadiusAngularRad * bodyRadiusAngularRad; gainDelta = rx.GainAtAngle(angle) - rx.Gain; } else { viewedAreaBase = MathUtils.CircleCircleIntersectionArea(beamwidthRad, bodyRadiusAngularRad, angleRad); double intersectionCenter = MathUtils.CircleCircleIntersectionOffset(beamwidthRad, bodyRadiusAngularRad, angleRad); gainDelta = rx.GainAtAngle((intersectionCenter + beamwidthRad) * Mathf.Rad2Deg / 2) - rx.Gain; } // How much of the antenna viewable area is occupied by the body double antennaViewableArea = Mathf.PI * beamwidthRad * beamwidthRad; double viewableAreaRatio = viewedAreaBase / antennaViewableArea; /* * double d = body.Radius * 2; * double Rsqr = toBody.sqrMagnitude; * double G = RATools.LinearScale(rx.Gain); * double angleRatio = angle / rx.Beamwidth; * * // https://deepspace.jpl.nasa.gov/dsndocs/810-005/Binder/810-005_Binder_Change51.pdf Module 105: 2.4.3 Planetary Noise estimator * // This estimator is correct for the DSN viewing planets, but wrong for the sun & moon. * double result = (t * G * d * d / (16 * Rsqr)) * Math.Pow(Math.E, -2.77 * angleRatio * angleRatio); */ double result = t * viewableAreaRatio * RATools.LinearScale(gainDelta); //Debug.Log($"Planetary Body Noise Power Estimator: Body {body} Temp: {t:F0} AngularDiameter: {bodyRadiusAngularDeg * 2:F1} @ {angle:F1} HPBW: {rx.Beamwidth:F1} ViewableAreaRatio: {viewableAreaRatio:F2} gainDelta: {gainDelta:F4} result: {result}"); return(result); }
private bool BestPeerModulator(RealAntenna rx, out double modRate, out double codeRate) { RealAntennaDigital tx = this; Antenna.Encoder encoder = Antenna.Encoder.BestMatching(tx.Encoder, rx.Encoder); codeRate = encoder.CodingRate; modRate = 0; if (!(rx is RealAntennaDigital)) { return(false); } if (!Compatible(rx)) { return(false); } if ((tx.Parent is ModuleRealAntenna) && !tx.Parent.CanComm()) { return(false); } if ((rx.Parent is ModuleRealAntenna) && !rx.Parent.CanComm()) { return(false); } Vector3 toSource = rx.Position - tx.Position; double distance = toSource.magnitude; RAModulator txMod = tx.modulator, rxMod = (rx as RealAntennaDigital).modulator; if ((distance < tx.MinimumDistance) || (distance < rx.MinimumDistance)) { return(false); } if (!txMod.Compatible(rxMod)) { return(false); } int maxBits = Math.Min(txMod.ModulationBits, rxMod.ModulationBits); double maxSymbolRate = Math.Min(txMod.SymbolRate, rxMod.SymbolRate); double minSymbolRate = Math.Max(txMod.MinSymbolRate, rxMod.MinSymbolRate); double RxPower = Physics.ReceivedPower(tx, rx, distance, tx.Frequency); double temp = Physics.NoiseTemperature(rx, tx.Position); double N0 = Physics.NoiseSpectralDensity(temp); // In dBm double minEb = encoder.RequiredEbN0 + N0; // in dBm double maxBitRateLog = RxPower - minEb; // in dB*Hz double maxBitRate = RATools.LinearScale(maxBitRateLog); /* * Vessel tv = (tx.ParentNode as RACommNode).ParentVessel; * Vessel rv = (rx.ParentNode as RACommNode).ParentVessel; * if (tv != null && rv != null) * { * string debugStr = $"{ModTag} {tx} to {rx} RxP {RxPower:F2} vs temp {temp:F2}. NSD {N0:F1}, ReqEb/N0 {encoder.RequiredEbN0:F1} -> minEb {minEb:F1} gives maxRate {RATools.PrettyPrint(maxBitRate)}bps vs symbol rates {RATools.PrettyPrint(minSymbolRate)}Sps-{RATools.PrettyPrint(maxSymbolRate)}Sps"; * Debug.Log(debugStr); * } */ // We cannot slow our modulation enough to achieve the required Eb/N0, so fail. if (maxBitRate < minSymbolRate) { return(false); } double targetRate; int negotiatedBits; if (maxBitRate <= maxSymbolRate) { // The required Eb/N0 occurs at a lower symbol rate than we are capable of at 1 bit/sec/Hz. // Step down the symbol rate and modulate at 1 bit/sec/Hz (BPSK). // (What if the modulator only supports schemes with >1 bits/symbol?) // (Then our minimum EbN0 is an underestimate.) float ratio = Convert.ToSingle(maxBitRate / maxSymbolRate); double log2 = Math.Floor(Mathf.Log(ratio, 2)); targetRate = maxSymbolRate * Math.Pow(2, log2); negotiatedBits = 1; //debugStr += $" Selected rate {RATools.PrettyPrint(targetRate)}bps (MaxSymbolRate * log2 {log2})"; } else { // We need to go to SNR here and rely a bit more on Shannon-Hartley double Noise = N0 + RATools.LogScale(maxSymbolRate); double CI = RxPower - Noise; double margin = CI - encoder.RequiredEbN0; targetRate = maxSymbolRate; negotiatedBits = Math.Min(maxBits, Convert.ToInt32(1 + Math.Floor(margin / 3))); //debugStr += $" Noise {Noise:F2} CI {CI:F2} margin {margin:F1}"; } modRate = targetRate * negotiatedBits; //Debug.LogFormat(debugStr); return(true); // Energy/bit (Eb) = Received Power / datarate // N0 = Noise Spectral Density = K*T // Noise = N0 * BW // SNR = RxPower / Noise = RxPower / (N0 * BW) = Eb*datarate / N0*BW = (Eb/N0) * (datarate/BW) // I < B * log(1 + S/N) where I = information rate, B=Bandwidth, S=Total Power, N=Total Noise Power = N0*B // // Es/N0 = (Total Power / Symbol Rate) / N0 // = Eb/N0 * log(modulation order) }
private static double AntennaMicrowaveTemp(RealAntenna rx) => rx.AMWTemp;