using System.Net.Sockets; using Avalonia.Threading; using Nonemm.Core; using Nonemm.Network; using Nonemm.Session; namespace Nonemm.App; /// Records the frequency, mode and split of a radio that another computer /// broadcasts as this station's own, for a station whose radio port is taken. /// Only the log's idea of the radio changes; no radio is commanded. /// Config > Configure Radios. public sealed partial class AppSession { private RadioMirror? mirror; /// Starts, restarts or stops the listener, following what the settings now /// say. A port that cannot be opened is reported and the listener stays off. public void ApplyMirrorRadioSettings() { mirror?.Dispose(); mirror = null; if (!Settings.MirrorRadioEnabled) { return; } try { mirror = new RadioMirror(Settings.MirrorRadioPort); } catch (SocketException e) { Reported?.Invoke(this, $"could not listen for radio data on port {Settings.MirrorRadioPort}: {e.Message}"); return; } mirror.Failed += (_, problem) => Reported?.Invoke(this, problem); mirror.Received += (_, message) => Dispatcher.UIThread.Post(() => Mirror(message)); } /// Sets the target position to where the message says. This computer's own /// messages are skipped, because the radio data goes to this port too when /// the default address is used. private void Mirror(RadioBroadcast message) { RadioReport source = message.Radio; if (message.StationName == ComputerName || source.RadioNumber != Settings.MirrorRadioSource || (Settings.MirrorRadioStation.Length > 0 && !string.Equals(message.StationName, Settings.MirrorRadioStation, StringComparison.OrdinalIgnoreCase))) { return; } OperatingPosition? position = positions.FirstOrDefault(p => p.RadioNumber == Settings.MirrorRadioTarget); if (position is null || source.Frequency <= 0) { return; } Frequency frequency = Frequency.FromHertz(source.Frequency); Frequency transmit = source.IsSplit ? Frequency.FromHertz(source.TransmitFrequency) : Frequency.Zero; Mode? mode = MirroredMode(source.Mode); bool frequencyChanged = frequency != position.Frequency; bool modeChanged = mode is not null && mode != position.Mode; bool splitChanged = transmit != position.TransmitFrequency; if (!frequencyChanged && !modeChanged && !splitChanged) { return; } TuneAndSpot(position, frequency, mode, transmit); } /// N1MM names reverse CW and FSK differently from our modes. Null for a /// mode we have no name for, which leaves the mode where it is. private static Mode? MirroredMode(string name) => name.Trim().ToUpperInvariant() switch { "CW-R" or "CWR" => Modes.Cw, "FSK" or "RTTY-R" or "RTTYR" => Modes.Rtty, var other => Modes.Parse(other), }; }