From 0315d8d7dc7828ce7c8083b86d08f71e8913a9cd Mon Sep 17 00:00:00 2001 From: Andrea Santaniello Date: Mon, 4 Nov 2024 19:09:54 +0100 Subject: [PATCH] V1, Library and example GUI Application. --- RadioGUI/Radio.cs | 418 ++++++++++++++++++------------- RadioGUI/Radio.resx | 112 +++++++-- RadioGUI/RadioGUI.csproj | 18 +- RadioGUI/icon-lg-dark.ico | Bin 0 -> 3415 bytes kv4p-sharp/RadioController.cs | 11 +- kv4p-sharp/kv4p-sharp-lib.csproj | 2 +- 6 files changed, 351 insertions(+), 210 deletions(-) create mode 100644 RadioGUI/icon-lg-dark.ico diff --git a/RadioGUI/Radio.cs b/RadioGUI/Radio.cs index d4e1978..097cb2b 100644 --- a/RadioGUI/Radio.cs +++ b/RadioGUI/Radio.cs @@ -3,22 +3,36 @@ using System.IO; using System.Threading.Tasks; using System.Windows.Forms; using NAudio.Wave; +using System.Collections.Generic; +using System.Collections.Concurrent; namespace RadioControllerApp { public class FormRadio : Form { + + // Pre-buffering fields + private const int PreBufferSize = 5000; // Adjust as needed + private ConcurrentQueue preBufferQueue = new ConcurrentQueue(); + private int preBufferedBytes = 0; + private bool preBufferingComplete = false; + private RadioController radioController; private WaveInEvent waveIn; private BufferedWaveProvider receivedAudioBuffer; private WaveOutEvent waveOut; + private bool isRecording = false; + private bool isTransmitting = false; + + // PTT Key Handling + private Keys pttKey = Keys.Space; + private bool waitingForPTTKey = false; // UI Controls private TextBox txtPortName; private Button btnOpenConnection; private Button btnCloseConnection; - private Button btnInitialize; private TextBox txtTXFrequency; private TextBox txtRXFrequency; @@ -31,35 +45,38 @@ namespace RadioControllerApp private CheckBox chkLowpass; private Button btnSetFilters; - private Button btnStartRX; - private Button btnStartTX; - private Button btnEndTX; - private Button btnStop; - - private Button btnStartRecording; - private Button btnStopRecording; - private Button btnPlayReceivedAudio; + private Button btnSetPTTKey; private TextBox txtStatus; + private Panel pnlStatusIndicator; + public FormRadio() { InitializeComponent(); InitializeRadioController(); + LoadConfigurations(); } private void InitializeComponent() { - this.AutoScaleMode = AutoScaleMode.Dpi; // Enable DPI scaling - - // Form properties - this.Text = "Radio Controller"; - this.Size = new System.Drawing.Size(800, 700); - this.MinimumSize = new System.Drawing.Size(600, 600); - this.FormClosing += FormRadio_FormClosing; - - // Initialize controls - InitializeControls(); + System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(FormRadio)); + SuspendLayout(); + // + // FormRadio + // + AutoScaleDimensions = new SizeF(144F, 144F); + AutoScaleMode = AutoScaleMode.Dpi; + ClientSize = new Size(778, 644); + Icon = (Icon)resources.GetObject("$this.Icon"); + KeyPreview = true; + MinimumSize = new Size(600, 600); + Name = "FormRadio"; + Text = "Radio Controller"; + FormClosing += FormRadio_FormClosing; + KeyDown += FormRadio_KeyDown; + KeyUp += FormRadio_KeyUp; + ResumeLayout(false); } private void InitializeControls() @@ -74,33 +91,37 @@ namespace RadioControllerApp }; this.Controls.Add(mainLayout); - // Adjust row styles + // Insert row styles + mainLayout.RowStyles.Insert(0, new RowStyle(SizeType.Absolute, 20F)); // Status Indicator mainLayout.RowStyles.Add(new RowStyle(SizeType.AutoSize)); // Connection Controls mainLayout.RowStyles.Add(new RowStyle(SizeType.AutoSize)); // Frequency Controls mainLayout.RowStyles.Add(new RowStyle(SizeType.AutoSize)); // Filter Controls - mainLayout.RowStyles.Add(new RowStyle(SizeType.AutoSize)); // Mode Controls mainLayout.RowStyles.Add(new RowStyle(SizeType.AutoSize)); // Audio Controls mainLayout.RowStyles.Add(new RowStyle(SizeType.Percent, 100F)); // Status TextBox + // Status Indicator Panel + pnlStatusIndicator = new Panel + { + Height = 20, + Dock = DockStyle.Fill, + BackColor = System.Drawing.Color.Green // Assume green for RX + }; + mainLayout.Controls.Add(pnlStatusIndicator, 0, 0); + // Connection Controls GroupBox GroupBox grpConnection = CreateConnectionControls(); grpConnection.Dock = DockStyle.Fill; - mainLayout.Controls.Add(grpConnection, 0, 0); + mainLayout.Controls.Add(grpConnection, 0, 1); // Frequency Controls GroupBox GroupBox grpFrequency = CreateFrequencyControls(); grpFrequency.Dock = DockStyle.Fill; - mainLayout.Controls.Add(grpFrequency, 0, 1); + mainLayout.Controls.Add(grpFrequency, 0, 2); // Filter Controls GroupBox GroupBox grpFilters = CreateFilterControls(); grpFilters.Dock = DockStyle.Fill; - mainLayout.Controls.Add(grpFilters, 0, 2); - - // Mode Controls GroupBox - GroupBox grpModes = CreateModeControls(); - grpModes.Dock = DockStyle.Fill; - mainLayout.Controls.Add(grpModes, 0, 3); + mainLayout.Controls.Add(grpFilters, 0, 3); // Audio Controls GroupBox GroupBox grpAudio = CreateAudioControls(); @@ -130,7 +151,7 @@ namespace RadioControllerApp TableLayoutPanel layout = new TableLayoutPanel { Dock = DockStyle.Fill, - ColumnCount = 5, + ColumnCount = 4, AutoSize = true }; grpConnection.Controls.Add(layout); @@ -139,23 +160,19 @@ namespace RadioControllerApp txtPortName = new TextBox { Text = "COM3", Anchor = AnchorStyles.Left }; btnOpenConnection = new Button { Text = "Open Connection", AutoSize = true }; btnCloseConnection = new Button { Text = "Close Connection", AutoSize = true }; - btnInitialize = new Button { Text = "Initialize", AutoSize = true }; btnOpenConnection.Click += btnOpenConnection_Click; btnCloseConnection.Click += btnCloseConnection_Click; - btnInitialize.Click += btnInitialize_Click; layout.Controls.Add(lblPortName, 0, 0); layout.Controls.Add(txtPortName, 1, 0); layout.Controls.Add(btnOpenConnection, 2, 0); layout.Controls.Add(btnCloseConnection, 3, 0); - layout.Controls.Add(btnInitialize, 4, 0); layout.ColumnStyles.Add(new ColumnStyle(SizeType.AutoSize)); // Label layout.ColumnStyles.Add(new ColumnStyle(SizeType.AutoSize)); // TextBox - layout.ColumnStyles.Add(new ColumnStyle(SizeType.Percent, 33F)); // Button - layout.ColumnStyles.Add(new ColumnStyle(SizeType.Percent, 33F)); // Button - layout.ColumnStyles.Add(new ColumnStyle(SizeType.Percent, 34F)); // Button + layout.ColumnStyles.Add(new ColumnStyle(SizeType.Percent, 50F)); // Button + layout.ColumnStyles.Add(new ColumnStyle(SizeType.Percent, 50F)); // Button return grpConnection; } @@ -243,41 +260,6 @@ namespace RadioControllerApp return grpFilters; } - private GroupBox CreateModeControls() - { - GroupBox grpModes = new GroupBox - { - Text = "Mode Controls", - AutoSize = true, - Dock = DockStyle.Fill - }; - - FlowLayoutPanel layout = new FlowLayoutPanel - { - Dock = DockStyle.Fill, - AutoSize = true, - WrapContents = false, - }; - grpModes.Controls.Add(layout); - - btnStartRX = new Button { Text = "Start RX Mode", AutoSize = true }; - btnStartTX = new Button { Text = "Start TX Mode", AutoSize = true }; - btnEndTX = new Button { Text = "End TX Mode", AutoSize = true }; - btnStop = new Button { Text = "Stop", AutoSize = true }; - - btnStartRX.Click += btnStartRX_Click; - btnStartTX.Click += btnStartTX_Click; - btnEndTX.Click += btnEndTX_Click; - btnStop.Click += btnStop_Click; - - layout.Controls.Add(btnStartRX); - layout.Controls.Add(btnStartTX); - layout.Controls.Add(btnEndTX); - layout.Controls.Add(btnStop); - - return grpModes; - } - private GroupBox CreateAudioControls() { GroupBox grpAudio = new GroupBox @@ -295,17 +277,10 @@ namespace RadioControllerApp }; grpAudio.Controls.Add(layout); - btnStartRecording = new Button { Text = "Start Recording", AutoSize = true }; - btnStopRecording = new Button { Text = "Stop Recording", AutoSize = true }; - btnPlayReceivedAudio = new Button { Text = "Play Received Audio", AutoSize = true }; + btnSetPTTKey = new Button { Text = "Set PTT Key", AutoSize = true }; + btnSetPTTKey.Click += btnSetPTTKey_Click; - btnStartRecording.Click += btnStartRecording_Click; - btnStopRecording.Click += btnStopRecording_Click; - btnPlayReceivedAudio.Click += btnPlayReceivedAudio_Click; - - layout.Controls.Add(btnStartRecording); - layout.Controls.Add(btnStopRecording); - layout.Controls.Add(btnPlayReceivedAudio); + layout.Controls.Add(btnSetPTTKey); return grpAudio; } @@ -315,9 +290,16 @@ namespace RadioControllerApp // Disable controls that require an open connection ToggleControls(false); - receivedAudioBuffer = new BufferedWaveProvider(new WaveFormat(44100, 16, 1)); + receivedAudioBuffer = new BufferedWaveProvider(new WaveFormat(44100, 8, 1)) + { + BufferDuration = TimeSpan.FromSeconds(5), + DiscardOnBufferOverflow = true + }; + waveOut = new WaveOutEvent(); + waveOut.Init(receivedAudioBuffer); } + private void btnOpenConnection_Click(object sender, EventArgs e) { try @@ -334,7 +316,14 @@ namespace RadioControllerApp radioController.AudioDataReceived += RadioController_AudioDataReceived; radioController.OpenConnection(); + radioController.Initialize(); + btnTune_Click(null, null); + // Start RX mode + isTransmitting = false; + UpdateStatusIndicator(false); + AppendStatus($"Connection opened on {portName}."); + radioController.StartRXMode(); ToggleControls(true); } catch (Exception ex) @@ -362,19 +351,6 @@ namespace RadioControllerApp } } - private void btnInitialize_Click(object sender, EventArgs e) - { - try - { - radioController.Initialize(); - AppendStatus("Radio initialized."); - } - catch (Exception ex) - { - AppendStatus($"Error initializing radio: {ex.Message}"); - } - } - private void btnTune_Click(object sender, EventArgs e) { try @@ -410,59 +386,44 @@ namespace RadioControllerApp } } - private void btnStartRX_Click(object sender, EventArgs e) + private void btnSetPTTKey_Click(object sender, EventArgs e) { - try + AppendStatus("Press any key to set as PTT."); + waitingForPTTKey = true; + } + + private void FormRadio_KeyDown(object sender, KeyEventArgs e) + { + if (waitingForPTTKey) { - radioController.StartRXMode(); - AppendStatus("Started RX mode."); + pttKey = e.KeyCode; + waitingForPTTKey = false; + AppendStatus($"PTT key set to: {pttKey}"); + e.Handled = true; } - catch (Exception ex) + else if (e.KeyCode == pttKey) { - AppendStatus($"Error starting RX mode: {ex.Message}"); + if (!isTransmitting) + { + StartTransmission(); + } + e.Handled = true; } } - private void btnStartTX_Click(object sender, EventArgs e) + private void FormRadio_KeyUp(object sender, KeyEventArgs e) { - try + if (!waitingForPTTKey && e.KeyCode == pttKey) { - radioController.StartTXMode(); - AppendStatus("Started TX mode."); - } - catch (Exception ex) - { - AppendStatus($"Error starting TX mode: {ex.Message}"); + if (isTransmitting) + { + StopTransmission(); + } + e.Handled = true; } } - private void btnEndTX_Click(object sender, EventArgs e) - { - try - { - radioController.EndTXMode(); - AppendStatus("Ended TX mode."); - } - catch (Exception ex) - { - AppendStatus($"Error ending TX mode: {ex.Message}"); - } - } - - private void btnStop_Click(object sender, EventArgs e) - { - try - { - radioController.Stop(); - AppendStatus("Radio stopped."); - } - catch (Exception ex) - { - AppendStatus($"Error stopping radio: {ex.Message}"); - } - } - - private void btnStartRecording_Click(object sender, EventArgs e) + private void StartTransmission() { try { @@ -473,39 +434,45 @@ namespace RadioControllerApp } radioController.StartTXMode(); - isRecording = true; + isTransmitting = true; waveIn = new WaveInEvent(); - waveIn.WaveFormat = new WaveFormat(44100, 16, 1); + waveIn.WaveFormat = new WaveFormat(44100, 8, 1); waveIn.DataAvailable += WaveIn_DataAvailable; waveIn.StartRecording(); - AppendStatus("Recording started."); + AppendStatus("Transmission started."); + + // Update status indicator + UpdateStatusIndicator(true); } catch (Exception ex) { - AppendStatus($"Error starting recording: {ex.Message}"); + AppendStatus($"Error starting transmission: {ex.Message}"); } } - private void btnStopRecording_Click(object sender, EventArgs e) + private void StopTransmission() { try { - if (isRecording && waveIn != null) + if (isTransmitting && waveIn != null) { waveIn.StopRecording(); waveIn.Dispose(); waveIn = null; - isRecording = false; + isTransmitting = false; radioController.EndTXMode(); - AppendStatus("Recording stopped."); + AppendStatus("Transmission stopped."); + radioController.StartRXMode(); + // Update status indicator + UpdateStatusIndicator(false); } } catch (Exception ex) { - AppendStatus($"Error stopping recording: {ex.Message}"); + AppendStatus($"Error stopping transmission: {ex.Message}"); } } @@ -513,13 +480,13 @@ namespace RadioControllerApp { try { - if (isRecording && radioController != null) + if (isTransmitting && radioController != null) { byte[] buffer = new byte[e.BytesRecorded]; Array.Copy(e.Buffer, buffer, e.BytesRecorded); - // Convert audio data to the required format if necessary - await radioController.SendAudioDataAsync(buffer); + // Send audio data asynchronously + await radioController.SendAudioDataAsync(buffer, 0, buffer.Length); } } catch (Exception ex) @@ -528,25 +495,6 @@ namespace RadioControllerApp } } - private void btnPlayReceivedAudio_Click(object sender, EventArgs e) - { - try - { - if (waveOut == null) - { - waveOut = new WaveOutEvent(); - waveOut.Init(receivedAudioBuffer); - } - waveOut.Play(); - - AppendStatus("Playing received audio."); - } - catch (Exception ex) - { - AppendStatus($"Error playing received audio: {ex.Message}"); - } - } - private void RadioController_ErrorOccurred(object sender, ErrorEventArgs e) { AppendStatus($"Error: {e.GetException().Message}"); @@ -554,9 +502,39 @@ namespace RadioControllerApp private void RadioController_AudioDataReceived(object sender, byte[] data) { - // Buffer the received audio data - receivedAudioBuffer.AddSamples(data, 0, data.Length); - AppendStatus($"Audio data received. Length: {data.Length} bytes."); + Console.WriteLine($"AudioDataReceived called with {data.Length} bytes"); + + if (!preBufferingComplete) + { + preBufferQueue.Enqueue(data); + preBufferedBytes += data.Length; + + if (preBufferedBytes >= PreBufferSize) + { + // Pre-buffering complete, start playback + preBufferingComplete = true; + + // Add all pre-buffered data to the buffer + while (preBufferQueue.TryDequeue(out byte[] preBufferData)) + { + receivedAudioBuffer.AddSamples(preBufferData, 0, preBufferData.Length); + } + + // Start playback + waveOut.Play(); + } + } + else + { + // Add received audio data to the buffer + receivedAudioBuffer.AddSamples(data, 0, data.Length); + + // Ensure playback is ongoing + if (waveOut.PlaybackState != PlaybackState.Playing) + { + waveOut.Play(); + } + } } private void AppendStatus(string message) @@ -571,19 +549,30 @@ namespace RadioControllerApp } } + private void UpdateStatusIndicator(bool isTransmitting) + { + if (pnlStatusIndicator.InvokeRequired) + { + pnlStatusIndicator.Invoke(new Action(() => UpdateStatusIndicator(isTransmitting))); + } + else + { + if (isTransmitting) + { + pnlStatusIndicator.BackColor = System.Drawing.Color.Red; + } + else + { + pnlStatusIndicator.BackColor = System.Drawing.Color.Green; + } + } + } + private void ToggleControls(bool isEnabled) { - btnInitialize.Enabled = isEnabled; btnTune.Enabled = isEnabled; btnSetFilters.Enabled = isEnabled; - btnStartRX.Enabled = isEnabled; - btnStartTX.Enabled = isEnabled; - btnEndTX.Enabled = isEnabled; - btnStop.Enabled = isEnabled; - - btnStartRecording.Enabled = isEnabled; - btnStopRecording.Enabled = isEnabled; - btnPlayReceivedAudio.Enabled = isEnabled; + btnSetPTTKey.Enabled = isEnabled; // Disable connection buttons appropriately btnOpenConnection.Enabled = !isEnabled; @@ -610,6 +599,77 @@ namespace RadioControllerApp waveOut.Dispose(); waveOut = null; } + + // Save configurations + SaveConfigurations(); + } + + private void LoadConfigurations() + { + string configFile = "config.ini"; + if (File.Exists(configFile)) + { + string[] lines = File.ReadAllLines(configFile); + foreach (string line in lines) + { + if (line.StartsWith("PortName=")) + { + txtPortName.Text = line.Substring("PortName=".Length); + } + else if (line.StartsWith("PTTKey=")) + { + if (Enum.TryParse(line.Substring("PTTKey=".Length), out Keys key)) + { + pttKey = key; + } + } + else if (line.StartsWith("TXFrequency=")) + { + txtTXFrequency.Text = line.Substring("TXFrequency=".Length); + } + else if (line.StartsWith("RXFrequency=")) + { + txtRXFrequency.Text = line.Substring("RXFrequency=".Length); + } + else if (line.StartsWith("Tone=")) + { + txtTone.Text = line.Substring("Tone=".Length); + } + else if (line.StartsWith("SquelchLevel=")) + { + txtSquelchLevel.Text = line.Substring("SquelchLevel=".Length); + } + else if (line.StartsWith("Emphasis=")) + { + chkEmphasis.Checked = line.Substring("Emphasis=".Length) == "True"; + } + else if (line.StartsWith("Highpass=")) + { + chkHighpass.Checked = line.Substring("Highpass=".Length) == "True"; + } + else if (line.StartsWith("Lowpass=")) + { + chkLowpass.Checked = line.Substring("Lowpass=".Length) == "True"; + } + } + } + } + + private void SaveConfigurations() + { + string configFile = "config.ini"; + using (StreamWriter writer = new StreamWriter(configFile)) + { + writer.WriteLine($"PortName={txtPortName.Text}"); + writer.WriteLine($"PTTKey={pttKey}"); + writer.WriteLine($"TXFrequency={txtTXFrequency.Text}"); + writer.WriteLine($"RXFrequency={txtRXFrequency.Text}"); + writer.WriteLine($"Tone={txtTone.Text}"); + writer.WriteLine($"SquelchLevel={txtSquelchLevel.Text}"); + writer.WriteLine($"Emphasis={chkEmphasis.Checked}"); + writer.WriteLine($"Highpass={chkHighpass.Checked}"); + writer.WriteLine($"Lowpass={chkLowpass.Checked}"); + } } } } diff --git a/RadioGUI/Radio.resx b/RadioGUI/Radio.resx index 1af7de1..9ea5fa9 100644 --- a/RadioGUI/Radio.resx +++ b/RadioGUI/Radio.resx @@ -1,17 +1,17 @@  - @@ -117,4 +117,66 @@ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + + 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 + + \ No newline at end of file diff --git a/RadioGUI/RadioGUI.csproj b/RadioGUI/RadioGUI.csproj index ff8cbd7..e758777 100644 --- a/RadioGUI/RadioGUI.csproj +++ b/RadioGUI/RadioGUI.csproj @@ -2,13 +2,29 @@ WinExe - net8.0-windows + net7.0-windows enable true enable true + Monocul.us + Andrea Santaniello + Radio GUI for KV4P radio + icon-lg-dark.png + icon-lg-dark.ico + + + + + + + True + \ + + + diff --git a/RadioGUI/icon-lg-dark.ico b/RadioGUI/icon-lg-dark.ico new file mode 100644 index 0000000000000000000000000000000000000000..9959831323238f3d7a580dbd371e1f910b093420 GIT binary patch literal 3415 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 literal 0 HcmV?d00001 diff --git a/kv4p-sharp/RadioController.cs b/kv4p-sharp/RadioController.cs index c215ffd..9d1a5c2 100644 --- a/kv4p-sharp/RadioController.cs +++ b/kv4p-sharp/RadioController.cs @@ -3,6 +3,7 @@ using System.Collections.Generic; using System.Diagnostics; using System.IO.Ports; using System.Linq; +using System.Security.Cryptography; using System.Threading; using System.Threading.Tasks; @@ -73,6 +74,7 @@ public class RadioController : IDisposable WriteTimeout = writeTimeout }; serialPort.DataReceived += SerialPort_DataReceived; + } public void OpenConnection() @@ -181,7 +183,7 @@ public class RadioController : IDisposable SendCommand(ESP32Command.FILTERS, paramsStr); } - public async Task SendAudioDataAsync(byte[] audioData, CancellationToken cancellationToken = default) + public async Task SendAudioDataAsync(byte[] audioData, int offset, int count, CancellationToken cancellationToken = default) { lock (_syncLock) { @@ -193,10 +195,10 @@ public class RadioController : IDisposable int chunkSize = 512; Stopwatch stopwatch = new Stopwatch(); - for (int i = 0; i < audioData.Length; i += chunkSize) + for (int i = offset; i < count; i += chunkSize) { cancellationToken.ThrowIfCancellationRequested(); - int remaining = audioData.Length - i; + int remaining = count - i; int size = remaining > chunkSize ? chunkSize : remaining; byte[] chunk = new byte[size]; Array.Copy(audioData, i, chunk, 0, size); @@ -217,6 +219,7 @@ public class RadioController : IDisposable } } + private void SerialPort_DataReceived(object sender, SerialDataReceivedEventArgs e) { byte[] receivedData = null; @@ -246,7 +249,7 @@ public class RadioController : IDisposable { mode = currentMode; } - if (mode == RadioMode.RX) + if (mode != RadioMode.TX) { // Raise an event with the received audio data OnAudioDataReceived(data); diff --git a/kv4p-sharp/kv4p-sharp-lib.csproj b/kv4p-sharp/kv4p-sharp-lib.csproj index 8903659..b64627b 100644 --- a/kv4p-sharp/kv4p-sharp-lib.csproj +++ b/kv4p-sharp/kv4p-sharp-lib.csproj @@ -1,7 +1,7 @@  - net8.0 + net7.0 kv4p_sharp enable enable