2024-10-28 00:44:38 +08:00
|
|
|
using System;
|
|
|
|
using System.IO;
|
|
|
|
using System.Threading.Tasks;
|
|
|
|
using System.Windows.Forms;
|
|
|
|
using NAudio.Wave;
|
2024-11-05 21:56:30 +08:00
|
|
|
using NAudio.CoreAudioApi; // For volume control and level meters
|
2024-11-05 02:09:54 +08:00
|
|
|
using System.Collections.Generic;
|
|
|
|
using System.Collections.Concurrent;
|
2024-11-05 21:56:30 +08:00
|
|
|
using System.IO.Ports;
|
|
|
|
using System.Management;
|
|
|
|
using System.Text.RegularExpressions;
|
2024-10-28 00:44:38 +08:00
|
|
|
|
|
|
|
namespace RadioControllerApp
|
|
|
|
{
|
|
|
|
public class FormRadio : Form
|
|
|
|
{
|
2024-11-05 02:09:54 +08:00
|
|
|
// Pre-buffering fields
|
|
|
|
private const int PreBufferSize = 5000; // Adjust as needed
|
|
|
|
private ConcurrentQueue<byte[]> preBufferQueue = new ConcurrentQueue<byte[]>();
|
|
|
|
private int preBufferedBytes = 0;
|
|
|
|
private bool preBufferingComplete = false;
|
|
|
|
|
2024-10-28 00:44:38 +08:00
|
|
|
private RadioController radioController;
|
|
|
|
private WaveInEvent waveIn;
|
|
|
|
private BufferedWaveProvider receivedAudioBuffer;
|
|
|
|
private WaveOutEvent waveOut;
|
2024-11-05 02:09:54 +08:00
|
|
|
|
2024-10-28 00:44:38 +08:00
|
|
|
private bool isRecording = false;
|
2024-11-05 02:09:54 +08:00
|
|
|
private bool isTransmitting = false;
|
|
|
|
|
|
|
|
// PTT Key Handling
|
|
|
|
private Keys pttKey = Keys.Space;
|
|
|
|
private bool waitingForPTTKey = false;
|
2024-10-28 00:44:38 +08:00
|
|
|
|
2024-11-05 21:56:30 +08:00
|
|
|
// ESP32 Vendor IDs and Product IDs
|
|
|
|
private readonly List<int> ESP32_VENDOR_IDS = new List<int> { 4292 };
|
|
|
|
private readonly List<int> ESP32_PRODUCT_IDS = new List<int> { 60000 };
|
|
|
|
|
2024-10-28 00:44:38 +08:00
|
|
|
// UI Controls
|
2024-11-05 21:56:30 +08:00
|
|
|
private ComboBox cmbPortName;
|
2024-10-28 00:44:38 +08:00
|
|
|
private Button btnOpenConnection;
|
|
|
|
private Button btnCloseConnection;
|
|
|
|
|
|
|
|
private TextBox txtTXFrequency;
|
|
|
|
private TextBox txtRXFrequency;
|
2024-11-05 21:56:30 +08:00
|
|
|
private ComboBox cmbTone;
|
|
|
|
private ComboBox cmbSquelchLevel;
|
2024-10-28 00:44:38 +08:00
|
|
|
private Button btnTune;
|
|
|
|
|
|
|
|
private CheckBox chkEmphasis;
|
|
|
|
private CheckBox chkHighpass;
|
|
|
|
private CheckBox chkLowpass;
|
|
|
|
private Button btnSetFilters;
|
|
|
|
|
2024-11-05 02:09:54 +08:00
|
|
|
private Button btnSetPTTKey;
|
2024-10-28 00:44:38 +08:00
|
|
|
|
|
|
|
private TextBox txtStatus;
|
|
|
|
|
2024-11-05 02:09:54 +08:00
|
|
|
private Panel pnlStatusIndicator;
|
|
|
|
|
2024-11-05 21:56:30 +08:00
|
|
|
private ComboBox cmbRecordingDevice;
|
|
|
|
private ComboBox cmbPlaybackDevice;
|
|
|
|
|
|
|
|
private TrackBar trkRecordingVolume;
|
|
|
|
private TrackBar trkPlaybackVolume;
|
|
|
|
private Label lblRecordingVolume;
|
|
|
|
private Label lblPlaybackVolume;
|
|
|
|
|
|
|
|
// Tone mappings
|
|
|
|
private Dictionary<string, int> toneMappings = new Dictionary<string, int>
|
|
|
|
{
|
|
|
|
{ "None", 0 },
|
|
|
|
{ "67.0", 1 },
|
|
|
|
{ "71.9", 2 },
|
|
|
|
{ "74.4", 3 },
|
|
|
|
{ "77.0", 4 },
|
|
|
|
{ "79.7", 5 },
|
|
|
|
{ "82.5", 6 },
|
|
|
|
{ "85.4", 7 },
|
|
|
|
{ "88.5", 8 },
|
|
|
|
{ "91.5", 9 },
|
|
|
|
{ "94.8", 10 },
|
|
|
|
{ "97.4", 11 },
|
|
|
|
{ "100.0", 12 },
|
|
|
|
{ "103.5", 13 },
|
|
|
|
{ "107.2", 14 },
|
|
|
|
{ "110.9", 15 },
|
|
|
|
{ "114.8", 16 },
|
|
|
|
{ "118.8", 17 },
|
|
|
|
{ "123.0", 18 },
|
|
|
|
{ "127.3", 19 },
|
|
|
|
{ "131.8", 20 },
|
|
|
|
{ "136.5", 21 },
|
|
|
|
{ "141.3", 22 },
|
|
|
|
{ "146.2", 23 },
|
|
|
|
{ "151.4", 24 },
|
|
|
|
{ "156.7", 25 },
|
|
|
|
{ "162.2", 26 },
|
|
|
|
{ "167.9", 27 },
|
|
|
|
{ "173.8", 28 },
|
|
|
|
{ "179.9", 29 },
|
|
|
|
{ "186.2", 30 },
|
|
|
|
{ "192.8", 31 },
|
|
|
|
{ "203.5", 32 },
|
|
|
|
{ "210.7", 33 },
|
|
|
|
{ "218.1", 34 },
|
|
|
|
{ "225.7", 35 },
|
|
|
|
{ "233.6", 36 },
|
|
|
|
{ "241.8", 37 },
|
|
|
|
{ "250.3", 38 }
|
|
|
|
};
|
|
|
|
|
|
|
|
|
2024-10-28 00:44:38 +08:00
|
|
|
public FormRadio()
|
|
|
|
{
|
|
|
|
InitializeComponent();
|
|
|
|
InitializeRadioController();
|
2024-11-05 21:56:30 +08:00
|
|
|
PopulateSerialPorts();
|
|
|
|
PopulateAudioDevices();
|
|
|
|
InitializeWaveOut();
|
2024-11-05 02:09:54 +08:00
|
|
|
LoadConfigurations();
|
2024-10-28 00:44:38 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
private void InitializeComponent()
|
|
|
|
{
|
2024-11-05 02:13:47 +08:00
|
|
|
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;
|
|
|
|
|
|
|
|
// Enable KeyPreview to capture key events
|
|
|
|
this.KeyPreview = true;
|
|
|
|
this.KeyDown += FormRadio_KeyDown;
|
|
|
|
this.KeyUp += FormRadio_KeyUp;
|
|
|
|
|
|
|
|
// Initialize controls
|
|
|
|
InitializeControls();
|
2024-10-28 00:44:38 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
private void InitializeControls()
|
|
|
|
{
|
|
|
|
// Main TableLayoutPanel
|
|
|
|
TableLayoutPanel mainLayout = new TableLayoutPanel
|
|
|
|
{
|
|
|
|
Dock = DockStyle.Fill,
|
|
|
|
ColumnCount = 1,
|
|
|
|
RowCount = 6,
|
|
|
|
AutoSize = true,
|
|
|
|
};
|
|
|
|
this.Controls.Add(mainLayout);
|
|
|
|
|
2024-11-05 02:09:54 +08:00
|
|
|
// Insert row styles
|
|
|
|
mainLayout.RowStyles.Insert(0, new RowStyle(SizeType.Absolute, 20F)); // Status Indicator
|
2024-10-28 00:44:38 +08:00
|
|
|
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)); // Audio Controls
|
|
|
|
mainLayout.RowStyles.Add(new RowStyle(SizeType.Percent, 100F)); // Status TextBox
|
|
|
|
|
2024-11-05 02:09:54 +08:00
|
|
|
// 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);
|
|
|
|
|
2024-10-28 00:44:38 +08:00
|
|
|
// Connection Controls GroupBox
|
|
|
|
GroupBox grpConnection = CreateConnectionControls();
|
|
|
|
grpConnection.Dock = DockStyle.Fill;
|
2024-11-05 02:09:54 +08:00
|
|
|
mainLayout.Controls.Add(grpConnection, 0, 1);
|
2024-10-28 00:44:38 +08:00
|
|
|
|
|
|
|
// Frequency Controls GroupBox
|
|
|
|
GroupBox grpFrequency = CreateFrequencyControls();
|
|
|
|
grpFrequency.Dock = DockStyle.Fill;
|
2024-11-05 02:09:54 +08:00
|
|
|
mainLayout.Controls.Add(grpFrequency, 0, 2);
|
2024-10-28 00:44:38 +08:00
|
|
|
|
|
|
|
// Filter Controls GroupBox
|
|
|
|
GroupBox grpFilters = CreateFilterControls();
|
|
|
|
grpFilters.Dock = DockStyle.Fill;
|
2024-11-05 02:09:54 +08:00
|
|
|
mainLayout.Controls.Add(grpFilters, 0, 3);
|
2024-10-28 00:44:38 +08:00
|
|
|
|
|
|
|
// Audio Controls GroupBox
|
|
|
|
GroupBox grpAudio = CreateAudioControls();
|
|
|
|
grpAudio.Dock = DockStyle.Fill;
|
|
|
|
mainLayout.Controls.Add(grpAudio, 0, 4);
|
|
|
|
|
|
|
|
// Status TextBox
|
|
|
|
txtStatus = new TextBox
|
|
|
|
{
|
|
|
|
Multiline = true,
|
|
|
|
ReadOnly = true,
|
|
|
|
Dock = DockStyle.Fill,
|
|
|
|
ScrollBars = ScrollBars.Vertical
|
|
|
|
};
|
|
|
|
mainLayout.Controls.Add(txtStatus, 0, 5);
|
2024-11-05 21:56:30 +08:00
|
|
|
|
|
|
|
// Add padding to each group box
|
|
|
|
foreach (Control control in mainLayout.Controls)
|
|
|
|
{
|
|
|
|
if (control is GroupBox)
|
|
|
|
{
|
|
|
|
control.Padding = new Padding(10, 10, 10, 10);
|
|
|
|
control.Margin = new Padding(0, 0, 0, 10); // Add bottom margin
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-10-28 00:44:38 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
private GroupBox CreateConnectionControls()
|
|
|
|
{
|
|
|
|
GroupBox grpConnection = new GroupBox
|
|
|
|
{
|
|
|
|
Text = "Connection Controls",
|
|
|
|
AutoSize = true,
|
|
|
|
Dock = DockStyle.Fill
|
|
|
|
};
|
|
|
|
|
|
|
|
TableLayoutPanel layout = new TableLayoutPanel
|
|
|
|
{
|
|
|
|
Dock = DockStyle.Fill,
|
2024-11-05 02:09:54 +08:00
|
|
|
ColumnCount = 4,
|
2024-10-28 00:44:38 +08:00
|
|
|
AutoSize = true
|
|
|
|
};
|
|
|
|
grpConnection.Controls.Add(layout);
|
|
|
|
|
|
|
|
Label lblPortName = new Label { Text = "Port Name:", Anchor = AnchorStyles.Right };
|
2024-11-05 21:56:30 +08:00
|
|
|
cmbPortName = new ComboBox { DropDownStyle = ComboBoxStyle.DropDownList, Anchor = AnchorStyles.Left, Width = 200 };
|
2024-10-28 00:44:38 +08:00
|
|
|
btnOpenConnection = new Button { Text = "Open Connection", AutoSize = true };
|
|
|
|
btnCloseConnection = new Button { Text = "Close Connection", AutoSize = true };
|
|
|
|
|
|
|
|
btnOpenConnection.Click += btnOpenConnection_Click;
|
|
|
|
btnCloseConnection.Click += btnCloseConnection_Click;
|
|
|
|
|
|
|
|
layout.Controls.Add(lblPortName, 0, 0);
|
2024-11-05 21:56:30 +08:00
|
|
|
layout.Controls.Add(cmbPortName, 1, 0);
|
2024-10-28 00:44:38 +08:00
|
|
|
layout.Controls.Add(btnOpenConnection, 2, 0);
|
|
|
|
layout.Controls.Add(btnCloseConnection, 3, 0);
|
|
|
|
|
|
|
|
layout.ColumnStyles.Add(new ColumnStyle(SizeType.AutoSize)); // Label
|
2024-11-05 21:56:30 +08:00
|
|
|
layout.ColumnStyles.Add(new ColumnStyle(SizeType.AutoSize)); // ComboBox
|
2024-11-05 02:09:54 +08:00
|
|
|
layout.ColumnStyles.Add(new ColumnStyle(SizeType.Percent, 50F)); // Button
|
|
|
|
layout.ColumnStyles.Add(new ColumnStyle(SizeType.Percent, 50F)); // Button
|
2024-10-28 00:44:38 +08:00
|
|
|
|
|
|
|
return grpConnection;
|
|
|
|
}
|
|
|
|
|
|
|
|
private GroupBox CreateFrequencyControls()
|
|
|
|
{
|
|
|
|
GroupBox grpFrequency = new GroupBox
|
|
|
|
{
|
|
|
|
Text = "Frequency Controls",
|
|
|
|
AutoSize = true,
|
|
|
|
Dock = DockStyle.Fill
|
|
|
|
};
|
|
|
|
|
|
|
|
TableLayoutPanel layout = new TableLayoutPanel
|
|
|
|
{
|
|
|
|
Dock = DockStyle.Fill,
|
|
|
|
ColumnCount = 6,
|
|
|
|
AutoSize = true
|
|
|
|
};
|
|
|
|
grpFrequency.Controls.Add(layout);
|
|
|
|
|
|
|
|
Label lblTXFrequency = new Label { Text = "TX Frequency:", Anchor = AnchorStyles.Right };
|
|
|
|
txtTXFrequency = new TextBox { Text = "146.520", Anchor = AnchorStyles.Left };
|
|
|
|
Label lblRXFrequency = new Label { Text = "RX Frequency:", Anchor = AnchorStyles.Right };
|
|
|
|
txtRXFrequency = new TextBox { Text = "146.520", Anchor = AnchorStyles.Left };
|
|
|
|
Label lblTone = new Label { Text = "Tone:", Anchor = AnchorStyles.Right };
|
2024-11-05 21:56:30 +08:00
|
|
|
cmbTone = new ComboBox { DropDownStyle = ComboBoxStyle.DropDownList, Anchor = AnchorStyles.Left };
|
2024-10-28 00:44:38 +08:00
|
|
|
Label lblSquelchLevel = new Label { Text = "Squelch Level:", Anchor = AnchorStyles.Right };
|
2024-11-05 21:56:30 +08:00
|
|
|
cmbSquelchLevel = new ComboBox { DropDownStyle = ComboBoxStyle.DropDownList, Anchor = AnchorStyles.Left };
|
2024-10-28 00:44:38 +08:00
|
|
|
btnTune = new Button { Text = "Tune to Frequency", AutoSize = true };
|
|
|
|
|
|
|
|
btnTune.Click += btnTune_Click;
|
|
|
|
|
|
|
|
layout.Controls.Add(lblTXFrequency, 0, 0);
|
|
|
|
layout.Controls.Add(txtTXFrequency, 1, 0);
|
|
|
|
layout.Controls.Add(lblRXFrequency, 2, 0);
|
|
|
|
layout.Controls.Add(txtRXFrequency, 3, 0);
|
|
|
|
layout.Controls.Add(lblTone, 4, 0);
|
2024-11-05 21:56:30 +08:00
|
|
|
layout.Controls.Add(cmbTone, 5, 0);
|
2024-10-28 00:44:38 +08:00
|
|
|
|
|
|
|
layout.Controls.Add(lblSquelchLevel, 0, 1);
|
2024-11-05 21:56:30 +08:00
|
|
|
layout.Controls.Add(cmbSquelchLevel, 1, 1);
|
2024-10-28 00:44:38 +08:00
|
|
|
layout.SetColumnSpan(btnTune, 4);
|
|
|
|
layout.Controls.Add(btnTune, 2, 1);
|
2024-11-05 21:56:30 +08:00
|
|
|
// Populate cmbTone
|
|
|
|
foreach (var tone in toneMappings.Keys)
|
|
|
|
{
|
|
|
|
cmbTone.Items.Add(tone);
|
|
|
|
}
|
|
|
|
cmbTone.SelectedIndex = 0; // Default to "None"
|
|
|
|
for (int i = 0; i <= 9; i++)
|
|
|
|
{
|
|
|
|
cmbSquelchLevel.Items.Add(i.ToString());
|
|
|
|
}
|
|
|
|
cmbSquelchLevel.SelectedIndex = 1; // Default to 1
|
2024-10-28 00:44:38 +08:00
|
|
|
// Adjust column styles
|
|
|
|
for (int i = 0; i < 6; i += 2)
|
|
|
|
{
|
|
|
|
layout.ColumnStyles.Add(new ColumnStyle(SizeType.AutoSize)); // Label
|
|
|
|
layout.ColumnStyles.Add(new ColumnStyle(SizeType.Percent, 16.66F)); // TextBox
|
|
|
|
}
|
|
|
|
|
|
|
|
return grpFrequency;
|
|
|
|
}
|
|
|
|
|
|
|
|
private GroupBox CreateFilterControls()
|
|
|
|
{
|
|
|
|
GroupBox grpFilters = new GroupBox
|
|
|
|
{
|
|
|
|
Text = "Filter Controls",
|
|
|
|
AutoSize = true,
|
|
|
|
Dock = DockStyle.Fill
|
|
|
|
};
|
|
|
|
|
|
|
|
FlowLayoutPanel layout = new FlowLayoutPanel
|
|
|
|
{
|
|
|
|
Dock = DockStyle.Fill,
|
|
|
|
AutoSize = true,
|
|
|
|
WrapContents = false,
|
|
|
|
};
|
|
|
|
grpFilters.Controls.Add(layout);
|
|
|
|
|
|
|
|
chkEmphasis = new CheckBox { Text = "Emphasis", AutoSize = true };
|
|
|
|
chkHighpass = new CheckBox { Text = "Highpass", AutoSize = true };
|
|
|
|
chkLowpass = new CheckBox { Text = "Lowpass", AutoSize = true };
|
|
|
|
btnSetFilters = new Button { Text = "Set Filters", AutoSize = true };
|
|
|
|
|
|
|
|
btnSetFilters.Click += btnSetFilters_Click;
|
|
|
|
|
|
|
|
layout.Controls.Add(chkEmphasis);
|
|
|
|
layout.Controls.Add(chkHighpass);
|
|
|
|
layout.Controls.Add(chkLowpass);
|
|
|
|
layout.Controls.Add(btnSetFilters);
|
|
|
|
|
|
|
|
return grpFilters;
|
|
|
|
}
|
|
|
|
|
|
|
|
private GroupBox CreateAudioControls()
|
|
|
|
{
|
|
|
|
GroupBox grpAudio = new GroupBox
|
|
|
|
{
|
|
|
|
Text = "Audio Controls",
|
|
|
|
AutoSize = true,
|
|
|
|
Dock = DockStyle.Fill
|
|
|
|
};
|
|
|
|
|
2024-11-05 21:56:30 +08:00
|
|
|
// Adjusted layout to place recording and playback controls side by side
|
|
|
|
TableLayoutPanel layout = new TableLayoutPanel
|
2024-10-28 00:44:38 +08:00
|
|
|
{
|
|
|
|
Dock = DockStyle.Fill,
|
2024-11-05 21:56:30 +08:00
|
|
|
ColumnCount = 4,
|
|
|
|
RowCount = 3,
|
|
|
|
AutoSize = true
|
2024-10-28 00:44:38 +08:00
|
|
|
};
|
|
|
|
grpAudio.Controls.Add(layout);
|
|
|
|
|
2024-11-05 21:56:30 +08:00
|
|
|
Label lblRecordingDevice = new Label { Text = "Recording Device:", Anchor = AnchorStyles.Right };
|
|
|
|
cmbRecordingDevice = new ComboBox { DropDownStyle = ComboBoxStyle.DropDownList, Anchor = AnchorStyles.Left, Width = 200 };
|
|
|
|
Label lblPlaybackDevice = new Label { Text = "Playback Device:", Anchor = AnchorStyles.Right };
|
|
|
|
cmbPlaybackDevice = new ComboBox { DropDownStyle = ComboBoxStyle.DropDownList, Anchor = AnchorStyles.Left, Width = 200 };
|
|
|
|
|
|
|
|
lblRecordingVolume = new Label { Text = "Recording Volume:", Anchor = AnchorStyles.Right };
|
|
|
|
trkRecordingVolume = new TrackBar { Minimum = 0, Maximum = 100, Value = 100, TickFrequency = 10, Anchor = AnchorStyles.Left, Width = 150 };
|
|
|
|
lblPlaybackVolume = new Label { Text = "Playback Volume:", Anchor = AnchorStyles.Right };
|
|
|
|
trkPlaybackVolume = new TrackBar { Minimum = 0, Maximum = 100, Value = 100, TickFrequency = 10, Anchor = AnchorStyles.Left, Width = 150 };
|
|
|
|
|
2024-11-05 02:09:54 +08:00
|
|
|
btnSetPTTKey = new Button { Text = "Set PTT Key", AutoSize = true };
|
|
|
|
btnSetPTTKey.Click += btnSetPTTKey_Click;
|
2024-10-28 00:44:38 +08:00
|
|
|
|
2024-11-05 21:56:30 +08:00
|
|
|
// First row: Recording and Playback Device labels and comboboxes
|
|
|
|
layout.Controls.Add(lblRecordingDevice, 0, 0);
|
|
|
|
layout.Controls.Add(cmbRecordingDevice, 1, 0);
|
|
|
|
layout.Controls.Add(lblPlaybackDevice, 2, 0);
|
|
|
|
layout.Controls.Add(cmbPlaybackDevice, 3, 0);
|
|
|
|
|
|
|
|
// Second row: Recording and Playback Volume labels and trackbars
|
|
|
|
layout.Controls.Add(lblRecordingVolume, 0, 1);
|
|
|
|
layout.Controls.Add(trkRecordingVolume, 1, 1);
|
|
|
|
layout.Controls.Add(lblPlaybackVolume, 2, 1);
|
|
|
|
layout.Controls.Add(trkPlaybackVolume, 3, 1);
|
|
|
|
|
|
|
|
// Third row: PTT Key button
|
|
|
|
layout.Controls.Add(btnSetPTTKey, 0, 2);
|
|
|
|
layout.SetColumnSpan(btnSetPTTKey, 4);
|
|
|
|
|
|
|
|
layout.RowStyles.Add(new RowStyle(SizeType.AutoSize));
|
|
|
|
layout.RowStyles.Add(new RowStyle(SizeType.AutoSize));
|
|
|
|
layout.RowStyles.Add(new RowStyle(SizeType.AutoSize));
|
|
|
|
|
|
|
|
layout.ColumnStyles.Add(new ColumnStyle(SizeType.AutoSize));
|
|
|
|
layout.ColumnStyles.Add(new ColumnStyle(SizeType.AutoSize));
|
|
|
|
layout.ColumnStyles.Add(new ColumnStyle(SizeType.AutoSize));
|
|
|
|
layout.ColumnStyles.Add(new ColumnStyle(SizeType.AutoSize));
|
|
|
|
|
|
|
|
cmbPlaybackDevice.SelectedIndexChanged += CmbPlaybackDevice_SelectedIndexChanged;
|
|
|
|
cmbRecordingDevice.SelectedIndexChanged += CmbRecordingDevice_SelectedIndexChanged;
|
|
|
|
|
|
|
|
trkPlaybackVolume.ValueChanged += TrkPlaybackVolume_ValueChanged;
|
|
|
|
trkRecordingVolume.ValueChanged += TrkRecordingVolume_ValueChanged;
|
2024-10-28 00:44:38 +08:00
|
|
|
|
|
|
|
return grpAudio;
|
|
|
|
}
|
|
|
|
|
|
|
|
private void InitializeRadioController()
|
|
|
|
{
|
|
|
|
// Disable controls that require an open connection
|
|
|
|
ToggleControls(false);
|
2024-11-05 21:56:30 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
private void PopulateSerialPorts()
|
|
|
|
{
|
|
|
|
cmbPortName.Items.Clear();
|
2024-10-28 00:44:38 +08:00
|
|
|
|
2024-11-05 21:56:30 +08:00
|
|
|
// Get all available serial ports
|
|
|
|
var portNames = SerialPort.GetPortNames();
|
|
|
|
|
|
|
|
// Use ManagementObjectSearcher to get more info
|
|
|
|
var searcher = new ManagementObjectSearcher("SELECT * FROM WIN32_SerialPort");
|
|
|
|
var ports = searcher.Get();
|
|
|
|
|
|
|
|
List<SerialPortInfo> portList = new List<SerialPortInfo>();
|
|
|
|
|
|
|
|
foreach (var port in ports)
|
|
|
|
{
|
|
|
|
string name = port["Name"].ToString();
|
|
|
|
string deviceId = port["DeviceID"].ToString();
|
|
|
|
string pnpDeviceId = port["PNPDeviceID"].ToString();
|
|
|
|
|
|
|
|
// Extract Vendor ID and Product ID from PNPDeviceID
|
|
|
|
// Example PNPDeviceID: USB\VID_10C4&PID_EA60\0001
|
|
|
|
string vid = GetPropertyFromDeviceID(pnpDeviceId, "VID");
|
|
|
|
string pid = GetPropertyFromDeviceID(pnpDeviceId, "PID");
|
|
|
|
|
|
|
|
int vendorId = 0;
|
|
|
|
int productId = 0;
|
|
|
|
|
|
|
|
try
|
|
|
|
{
|
|
|
|
vendorId = Convert.ToInt32(vid, 16);
|
|
|
|
productId = Convert.ToInt32(pid, 16);
|
|
|
|
}
|
|
|
|
catch
|
|
|
|
{
|
|
|
|
// Ignore if unable to parse VID and PID
|
|
|
|
}
|
|
|
|
|
|
|
|
bool isEsp32 = ESP32_VENDOR_IDS.Contains(vendorId) && ESP32_PRODUCT_IDS.Contains(productId);
|
|
|
|
|
|
|
|
portList.Add(new SerialPortInfo
|
|
|
|
{
|
|
|
|
Name = name,
|
|
|
|
DeviceId = deviceId,
|
|
|
|
PnpDeviceId = pnpDeviceId,
|
|
|
|
IsEsp32 = isEsp32,
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
// Now populate cmbPortName
|
|
|
|
foreach (var portInfo in portList)
|
|
|
|
{
|
|
|
|
cmbPortName.Items.Add(portInfo);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Select the first port
|
|
|
|
if (cmbPortName.Items.Count > 0)
|
|
|
|
cmbPortName.SelectedIndex = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
private void PopulateAudioDevices()
|
|
|
|
{
|
|
|
|
// Populate recording devices
|
|
|
|
for (int n = 0; n < WaveIn.DeviceCount; n++)
|
|
|
|
{
|
|
|
|
var capabilities = WaveIn.GetCapabilities(n);
|
|
|
|
cmbRecordingDevice.Items.Add(new WaveInDevice { DeviceNumber = n, ProductName = capabilities.ProductName });
|
|
|
|
}
|
|
|
|
|
|
|
|
// Select default recording device
|
|
|
|
if (cmbRecordingDevice.Items.Count > 0)
|
|
|
|
cmbRecordingDevice.SelectedIndex = 0;
|
|
|
|
|
|
|
|
// Populate playback devices
|
|
|
|
var enumerator = new MMDeviceEnumerator();
|
|
|
|
var devices = enumerator.EnumerateAudioEndPoints(DataFlow.Render, DeviceState.Active);
|
|
|
|
foreach (var device in devices)
|
|
|
|
{
|
|
|
|
cmbPlaybackDevice.Items.Add(new WaveOutDevice { DeviceNumber = 0, ProductName = device.FriendlyName, MMDevice = device });
|
|
|
|
}
|
|
|
|
|
|
|
|
// Select default playback device
|
|
|
|
if (cmbPlaybackDevice.Items.Count > 0)
|
|
|
|
cmbPlaybackDevice.SelectedIndex = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
private void InitializeWaveOut()
|
|
|
|
{
|
2024-11-05 02:09:54 +08:00
|
|
|
receivedAudioBuffer = new BufferedWaveProvider(new WaveFormat(44100, 8, 1))
|
|
|
|
{
|
|
|
|
BufferDuration = TimeSpan.FromSeconds(5),
|
|
|
|
DiscardOnBufferOverflow = true
|
|
|
|
};
|
|
|
|
waveOut = new WaveOutEvent();
|
2024-11-05 21:56:30 +08:00
|
|
|
|
|
|
|
var selectedPlaybackDevice = cmbPlaybackDevice.SelectedItem as WaveOutDevice;
|
|
|
|
if (selectedPlaybackDevice != null)
|
|
|
|
{
|
|
|
|
//waveOut.DeviceNumber = selectedPlaybackDevice.DeviceNumber;
|
|
|
|
// Use NAudio.CoreAudioApi to control playback volume
|
|
|
|
//waveOut.DeviceNumber = -1; // Use default device
|
|
|
|
}
|
|
|
|
|
2024-11-05 02:09:54 +08:00
|
|
|
waveOut.Init(receivedAudioBuffer);
|
2024-10-28 00:44:38 +08:00
|
|
|
}
|
|
|
|
|
2024-11-05 21:56:30 +08:00
|
|
|
private void CmbPlaybackDevice_SelectedIndexChanged(object sender, EventArgs e)
|
|
|
|
{
|
|
|
|
// Re-initialize waveOut with the selected device
|
|
|
|
if (waveOut != null)
|
|
|
|
{
|
|
|
|
waveOut.Stop();
|
|
|
|
waveOut.Dispose();
|
|
|
|
}
|
|
|
|
InitializeWaveOut();
|
|
|
|
}
|
|
|
|
|
|
|
|
private void CmbRecordingDevice_SelectedIndexChanged(object sender, EventArgs e)
|
|
|
|
{
|
|
|
|
// Do nothing for now. Recording device is set when starting transmission.
|
|
|
|
}
|
|
|
|
|
|
|
|
private void TrkPlaybackVolume_ValueChanged(object sender, EventArgs e)
|
|
|
|
{
|
|
|
|
SetPlaybackVolume(trkPlaybackVolume.Value);
|
|
|
|
}
|
|
|
|
|
|
|
|
private void TrkRecordingVolume_ValueChanged(object sender, EventArgs e)
|
|
|
|
{
|
|
|
|
SetRecordingVolume(trkRecordingVolume.Value);
|
|
|
|
}
|
|
|
|
|
|
|
|
private void SetPlaybackVolume(int volume)
|
|
|
|
{
|
|
|
|
var selectedPlaybackDevice = cmbPlaybackDevice.SelectedItem as WaveOutDevice;
|
|
|
|
if (selectedPlaybackDevice != null && selectedPlaybackDevice.MMDevice != null)
|
|
|
|
{
|
|
|
|
selectedPlaybackDevice.MMDevice.AudioEndpointVolume.MasterVolumeLevelScalar = volume / 100.0f;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
private void SetRecordingVolume(int volume)
|
|
|
|
{
|
|
|
|
var selectedRecordingDevice = cmbRecordingDevice.SelectedItem as WaveInDevice;
|
|
|
|
if (selectedRecordingDevice != null)
|
|
|
|
{
|
|
|
|
// Setting recording volume is not straightforward with WaveInEvent
|
|
|
|
// Need to use NAudio.CoreAudioApi
|
|
|
|
var enumerator = new MMDeviceEnumerator();
|
|
|
|
var devices = enumerator.EnumerateAudioEndPoints(DataFlow.Capture, DeviceState.Active);
|
|
|
|
foreach (var device in devices)
|
|
|
|
{
|
|
|
|
if (device.FriendlyName == selectedRecordingDevice.ProductName)
|
|
|
|
{
|
|
|
|
device.AudioEndpointVolume.MasterVolumeLevelScalar = volume / 100.0f;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2024-11-05 02:09:54 +08:00
|
|
|
|
2024-10-28 00:44:38 +08:00
|
|
|
private void btnOpenConnection_Click(object sender, EventArgs e)
|
|
|
|
{
|
|
|
|
try
|
|
|
|
{
|
2024-11-05 21:56:30 +08:00
|
|
|
var selectedPortInfo = cmbPortName.SelectedItem as SerialPortInfo;
|
|
|
|
if (selectedPortInfo != null)
|
2024-10-28 00:44:38 +08:00
|
|
|
{
|
2024-11-05 21:56:30 +08:00
|
|
|
string portName = selectedPortInfo.DeviceId;
|
|
|
|
radioController = new RadioController(portName);
|
|
|
|
radioController.ErrorOccurred += RadioController_ErrorOccurred;
|
|
|
|
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);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
AppendStatus("Please select a valid port.");
|
2024-10-28 00:44:38 +08:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
catch (Exception ex)
|
|
|
|
{
|
|
|
|
AppendStatus($"Error opening connection: {ex.Message}");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
private void btnCloseConnection_Click(object sender, EventArgs e)
|
|
|
|
{
|
|
|
|
try
|
|
|
|
{
|
|
|
|
if (radioController != null)
|
|
|
|
{
|
|
|
|
radioController.CloseConnection();
|
|
|
|
radioController.Dispose();
|
|
|
|
radioController = null;
|
|
|
|
AppendStatus("Connection closed.");
|
|
|
|
ToggleControls(false);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
catch (Exception ex)
|
|
|
|
{
|
|
|
|
AppendStatus($"Error closing connection: {ex.Message}");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
private void btnTune_Click(object sender, EventArgs e)
|
|
|
|
{
|
|
|
|
try
|
|
|
|
{
|
|
|
|
string txFreq = txtTXFrequency.Text.Trim();
|
|
|
|
string rxFreq = txtRXFrequency.Text.Trim();
|
|
|
|
|
2024-11-05 21:56:30 +08:00
|
|
|
// Get selected tone
|
|
|
|
string selectedTone = cmbTone.SelectedItem.ToString();
|
|
|
|
int toneValue = toneMappings[selectedTone];
|
|
|
|
|
|
|
|
// Get selected squelch level
|
|
|
|
int squelch = int.Parse(cmbSquelchLevel.SelectedItem.ToString());
|
|
|
|
|
|
|
|
radioController.TuneToFrequency(txFreq, rxFreq, toneValue, squelch);
|
|
|
|
AppendStatus($"Tuned to TX: {txFreq}, RX: {rxFreq}, Tone: {selectedTone}, Squelch Level: {squelch}");
|
2024-10-28 00:44:38 +08:00
|
|
|
}
|
|
|
|
catch (Exception ex)
|
|
|
|
{
|
|
|
|
AppendStatus($"Error tuning frequency: {ex.Message}");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-11-05 21:56:30 +08:00
|
|
|
|
2024-10-28 00:44:38 +08:00
|
|
|
private void btnSetFilters_Click(object sender, EventArgs e)
|
|
|
|
{
|
|
|
|
try
|
|
|
|
{
|
|
|
|
bool emphasis = chkEmphasis.Checked;
|
|
|
|
bool highpass = chkHighpass.Checked;
|
|
|
|
bool lowpass = chkLowpass.Checked;
|
|
|
|
|
|
|
|
radioController.SetFilters(emphasis, highpass, lowpass);
|
|
|
|
AppendStatus("Filters set.");
|
|
|
|
}
|
|
|
|
catch (Exception ex)
|
|
|
|
{
|
|
|
|
AppendStatus($"Error setting filters: {ex.Message}");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-11-05 02:09:54 +08:00
|
|
|
private void btnSetPTTKey_Click(object sender, EventArgs e)
|
2024-10-28 00:44:38 +08:00
|
|
|
{
|
2024-11-05 02:09:54 +08:00
|
|
|
AppendStatus("Press any key to set as PTT.");
|
|
|
|
waitingForPTTKey = true;
|
2024-10-28 00:44:38 +08:00
|
|
|
}
|
|
|
|
|
2024-11-05 02:09:54 +08:00
|
|
|
private void FormRadio_KeyDown(object sender, KeyEventArgs e)
|
2024-10-28 00:44:38 +08:00
|
|
|
{
|
2024-11-05 02:09:54 +08:00
|
|
|
if (waitingForPTTKey)
|
2024-10-28 00:44:38 +08:00
|
|
|
{
|
2024-11-05 02:09:54 +08:00
|
|
|
pttKey = e.KeyCode;
|
|
|
|
waitingForPTTKey = false;
|
|
|
|
AppendStatus($"PTT key set to: {pttKey}");
|
|
|
|
e.Handled = true;
|
2024-10-28 00:44:38 +08:00
|
|
|
}
|
2024-11-05 02:09:54 +08:00
|
|
|
else if (e.KeyCode == pttKey)
|
2024-10-28 00:44:38 +08:00
|
|
|
{
|
2024-11-05 02:09:54 +08:00
|
|
|
if (!isTransmitting)
|
|
|
|
{
|
|
|
|
StartTransmission();
|
|
|
|
}
|
|
|
|
e.Handled = true;
|
2024-10-28 00:44:38 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-11-05 02:09:54 +08:00
|
|
|
private void FormRadio_KeyUp(object sender, KeyEventArgs e)
|
2024-10-28 00:44:38 +08:00
|
|
|
{
|
2024-11-05 02:09:54 +08:00
|
|
|
if (!waitingForPTTKey && e.KeyCode == pttKey)
|
2024-10-28 00:44:38 +08:00
|
|
|
{
|
2024-11-05 02:09:54 +08:00
|
|
|
if (isTransmitting)
|
|
|
|
{
|
|
|
|
StopTransmission();
|
|
|
|
}
|
|
|
|
e.Handled = true;
|
2024-10-28 00:44:38 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-11-05 02:09:54 +08:00
|
|
|
private void StartTransmission()
|
2024-10-28 00:44:38 +08:00
|
|
|
{
|
|
|
|
try
|
|
|
|
{
|
|
|
|
if (radioController == null)
|
|
|
|
{
|
|
|
|
AppendStatus("Please open the connection first.");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
radioController.StartTXMode();
|
2024-11-05 02:09:54 +08:00
|
|
|
isTransmitting = true;
|
2024-10-28 00:44:38 +08:00
|
|
|
|
|
|
|
waveIn = new WaveInEvent();
|
2024-11-05 21:56:30 +08:00
|
|
|
|
|
|
|
var selectedRecordingDevice = cmbRecordingDevice.SelectedItem as WaveInDevice;
|
|
|
|
if (selectedRecordingDevice != null)
|
|
|
|
{
|
|
|
|
waveIn.DeviceNumber = selectedRecordingDevice.DeviceNumber;
|
|
|
|
}
|
|
|
|
|
2024-11-05 02:09:54 +08:00
|
|
|
waveIn.WaveFormat = new WaveFormat(44100, 8, 1);
|
2024-10-28 00:44:38 +08:00
|
|
|
waveIn.DataAvailable += WaveIn_DataAvailable;
|
|
|
|
waveIn.StartRecording();
|
|
|
|
|
2024-11-05 02:09:54 +08:00
|
|
|
AppendStatus("Transmission started.");
|
|
|
|
|
2024-11-05 21:56:30 +08:00
|
|
|
// Disable recording device selection during transmission
|
|
|
|
cmbRecordingDevice.Enabled = false;
|
|
|
|
|
2024-11-05 02:09:54 +08:00
|
|
|
// Update status indicator
|
|
|
|
UpdateStatusIndicator(true);
|
2024-10-28 00:44:38 +08:00
|
|
|
}
|
|
|
|
catch (Exception ex)
|
|
|
|
{
|
2024-11-05 02:09:54 +08:00
|
|
|
AppendStatus($"Error starting transmission: {ex.Message}");
|
2024-10-28 00:44:38 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-11-05 02:09:54 +08:00
|
|
|
private void StopTransmission()
|
2024-10-28 00:44:38 +08:00
|
|
|
{
|
|
|
|
try
|
|
|
|
{
|
2024-11-05 02:09:54 +08:00
|
|
|
if (isTransmitting && waveIn != null)
|
2024-10-28 00:44:38 +08:00
|
|
|
{
|
|
|
|
waveIn.StopRecording();
|
|
|
|
waveIn.Dispose();
|
|
|
|
waveIn = null;
|
2024-11-05 02:09:54 +08:00
|
|
|
isTransmitting = false;
|
2024-10-28 00:44:38 +08:00
|
|
|
|
|
|
|
radioController.EndTXMode();
|
2024-11-05 02:09:54 +08:00
|
|
|
AppendStatus("Transmission stopped.");
|
|
|
|
radioController.StartRXMode();
|
2024-11-05 21:56:30 +08:00
|
|
|
|
|
|
|
// Enable recording device selection
|
|
|
|
cmbRecordingDevice.Enabled = true;
|
|
|
|
|
2024-11-05 02:09:54 +08:00
|
|
|
// Update status indicator
|
|
|
|
UpdateStatusIndicator(false);
|
2024-10-28 00:44:38 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
catch (Exception ex)
|
|
|
|
{
|
2024-11-05 02:09:54 +08:00
|
|
|
AppendStatus($"Error stopping transmission: {ex.Message}");
|
2024-10-28 00:44:38 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
private async void WaveIn_DataAvailable(object sender, WaveInEventArgs e)
|
|
|
|
{
|
|
|
|
try
|
|
|
|
{
|
2024-11-05 02:09:54 +08:00
|
|
|
if (isTransmitting && radioController != null)
|
2024-10-28 00:44:38 +08:00
|
|
|
{
|
|
|
|
byte[] buffer = new byte[e.BytesRecorded];
|
|
|
|
Array.Copy(e.Buffer, buffer, e.BytesRecorded);
|
|
|
|
|
2024-11-05 02:09:54 +08:00
|
|
|
// Send audio data asynchronously
|
|
|
|
await radioController.SendAudioDataAsync(buffer, 0, buffer.Length);
|
2024-10-28 00:44:38 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
catch (Exception ex)
|
|
|
|
{
|
|
|
|
AppendStatus($"Error sending audio data: {ex.Message}");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
private void RadioController_ErrorOccurred(object sender, ErrorEventArgs e)
|
|
|
|
{
|
|
|
|
AppendStatus($"Error: {e.GetException().Message}");
|
|
|
|
}
|
|
|
|
|
|
|
|
private void RadioController_AudioDataReceived(object sender, byte[] data)
|
|
|
|
{
|
2024-11-05 02:09:54 +08:00
|
|
|
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();
|
|
|
|
}
|
|
|
|
}
|
2024-10-28 00:44:38 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
private void AppendStatus(string message)
|
|
|
|
{
|
|
|
|
if (txtStatus.InvokeRequired)
|
|
|
|
{
|
|
|
|
txtStatus.Invoke(new Action(() => AppendStatus(message)));
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
txtStatus.AppendText($"{DateTime.Now}: {message}{Environment.NewLine}");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-11-05 02:09:54 +08:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-10-28 00:44:38 +08:00
|
|
|
private void ToggleControls(bool isEnabled)
|
|
|
|
{
|
|
|
|
btnTune.Enabled = isEnabled;
|
|
|
|
btnSetFilters.Enabled = isEnabled;
|
2024-11-05 02:09:54 +08:00
|
|
|
btnSetPTTKey.Enabled = isEnabled;
|
2024-10-28 00:44:38 +08:00
|
|
|
|
|
|
|
// Disable connection buttons appropriately
|
|
|
|
btnOpenConnection.Enabled = !isEnabled;
|
|
|
|
btnCloseConnection.Enabled = isEnabled;
|
|
|
|
}
|
|
|
|
|
|
|
|
private void FormRadio_FormClosing(object sender, FormClosingEventArgs e)
|
|
|
|
{
|
|
|
|
// Clean up resources
|
|
|
|
if (radioController != null)
|
|
|
|
{
|
|
|
|
radioController.Dispose();
|
|
|
|
radioController = null;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (waveIn != null)
|
|
|
|
{
|
|
|
|
waveIn.Dispose();
|
|
|
|
waveIn = null;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (waveOut != null)
|
|
|
|
{
|
|
|
|
waveOut.Dispose();
|
|
|
|
waveOut = null;
|
|
|
|
}
|
2024-11-05 02:09:54 +08:00
|
|
|
|
|
|
|
// 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="))
|
|
|
|
{
|
2024-11-05 21:56:30 +08:00
|
|
|
string portName = line.Substring("PortName=".Length);
|
|
|
|
foreach (SerialPortInfo portInfo in cmbPortName.Items)
|
|
|
|
{
|
|
|
|
if (portInfo.DeviceId == portName)
|
|
|
|
{
|
|
|
|
cmbPortName.SelectedItem = portInfo;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
2024-11-05 02:09:54 +08:00
|
|
|
}
|
|
|
|
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="))
|
|
|
|
{
|
2024-11-05 21:56:30 +08:00
|
|
|
if (int.TryParse(line.Substring("Tone=".Length), out int toneValue))
|
|
|
|
{
|
|
|
|
foreach (var item in toneMappings)
|
|
|
|
{
|
|
|
|
if (item.Value == toneValue)
|
|
|
|
{
|
|
|
|
cmbTone.SelectedItem = item.Key;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2024-11-05 02:09:54 +08:00
|
|
|
}
|
|
|
|
else if (line.StartsWith("SquelchLevel="))
|
|
|
|
{
|
2024-11-05 21:56:30 +08:00
|
|
|
cmbSquelchLevel.SelectedItem = line.Substring("SquelchLevel=".Length);
|
2024-11-05 02:09:54 +08:00
|
|
|
}
|
|
|
|
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";
|
|
|
|
}
|
2024-11-05 21:56:30 +08:00
|
|
|
else if (line.StartsWith("RecordingDevice="))
|
|
|
|
{
|
|
|
|
if (int.TryParse(line.Substring("RecordingDevice=".Length), out int deviceNumber))
|
|
|
|
{
|
|
|
|
foreach (WaveInDevice device in cmbRecordingDevice.Items)
|
|
|
|
{
|
|
|
|
if (device.DeviceNumber == deviceNumber)
|
|
|
|
{
|
|
|
|
cmbRecordingDevice.SelectedItem = device;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else if (line.StartsWith("PlaybackDevice="))
|
|
|
|
{
|
|
|
|
string playbackDeviceName = line.Substring("PlaybackDevice=".Length);
|
|
|
|
foreach (WaveOutDevice device in cmbPlaybackDevice.Items)
|
|
|
|
{
|
|
|
|
if (device.ProductName == playbackDeviceName)
|
|
|
|
{
|
|
|
|
cmbPlaybackDevice.SelectedItem = device;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else if (line.StartsWith("RecordingVolume="))
|
|
|
|
{
|
|
|
|
if (int.TryParse(line.Substring("RecordingVolume=".Length), out int volume))
|
|
|
|
{
|
|
|
|
trkRecordingVolume.Value = volume;
|
|
|
|
SetRecordingVolume(volume);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else if (line.StartsWith("PlaybackVolume="))
|
|
|
|
{
|
|
|
|
if (int.TryParse(line.Substring("PlaybackVolume=".Length), out int volume))
|
|
|
|
{
|
|
|
|
trkPlaybackVolume.Value = volume;
|
|
|
|
SetPlaybackVolume(volume);
|
|
|
|
}
|
|
|
|
}
|
2024-11-05 02:09:54 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
private void SaveConfigurations()
|
|
|
|
{
|
|
|
|
string configFile = "config.ini";
|
2024-11-05 21:56:30 +08:00
|
|
|
string selectedTone = cmbTone.SelectedItem.ToString();
|
|
|
|
int toneValue = toneMappings[selectedTone];
|
2024-11-05 02:09:54 +08:00
|
|
|
using (StreamWriter writer = new StreamWriter(configFile))
|
|
|
|
{
|
2024-11-05 21:56:30 +08:00
|
|
|
// Save selected port
|
|
|
|
var selectedPortInfo = cmbPortName.SelectedItem as SerialPortInfo;
|
|
|
|
if (selectedPortInfo != null)
|
|
|
|
{
|
|
|
|
writer.WriteLine($"PortName={selectedPortInfo.DeviceId}");
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
writer.WriteLine("PortName=");
|
|
|
|
}
|
|
|
|
|
2024-11-05 02:09:54 +08:00
|
|
|
writer.WriteLine($"PTTKey={pttKey}");
|
|
|
|
writer.WriteLine($"TXFrequency={txtTXFrequency.Text}");
|
|
|
|
writer.WriteLine($"RXFrequency={txtRXFrequency.Text}");
|
2024-11-05 21:56:30 +08:00
|
|
|
writer.WriteLine($"Tone={toneValue}");
|
|
|
|
writer.WriteLine($"SquelchLevel={cmbSquelchLevel.SelectedItem}");
|
2024-11-05 02:09:54 +08:00
|
|
|
writer.WriteLine($"Emphasis={chkEmphasis.Checked}");
|
|
|
|
writer.WriteLine($"Highpass={chkHighpass.Checked}");
|
|
|
|
writer.WriteLine($"Lowpass={chkLowpass.Checked}");
|
2024-11-05 21:56:30 +08:00
|
|
|
|
|
|
|
// Save selected recording device
|
|
|
|
var selectedRecordingDevice = cmbRecordingDevice.SelectedItem as WaveInDevice;
|
|
|
|
if (selectedRecordingDevice != null)
|
|
|
|
{
|
|
|
|
writer.WriteLine($"RecordingDevice={selectedRecordingDevice.DeviceNumber}");
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
writer.WriteLine("RecordingDevice=");
|
|
|
|
}
|
|
|
|
|
|
|
|
// Save selected playback device
|
|
|
|
var selectedPlaybackDevice = cmbPlaybackDevice.SelectedItem as WaveOutDevice;
|
|
|
|
if (selectedPlaybackDevice != null)
|
|
|
|
{
|
|
|
|
writer.WriteLine($"PlaybackDevice={selectedPlaybackDevice.ProductName}");
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
writer.WriteLine("PlaybackDevice=");
|
|
|
|
}
|
|
|
|
|
|
|
|
writer.WriteLine($"RecordingVolume={trkRecordingVolume.Value}");
|
|
|
|
writer.WriteLine($"PlaybackVolume={trkPlaybackVolume.Value}");
|
2024-11-05 02:09:54 +08:00
|
|
|
}
|
2024-10-28 00:44:38 +08:00
|
|
|
}
|
2024-11-05 21:56:30 +08:00
|
|
|
|
|
|
|
private string GetPropertyFromDeviceID(string deviceId, string property)
|
|
|
|
{
|
|
|
|
string result = "";
|
|
|
|
string pattern = property + "_([0-9A-F]{4})";
|
|
|
|
var match = Regex.Match(deviceId, pattern, RegexOptions.IgnoreCase);
|
|
|
|
if (match.Success)
|
|
|
|
{
|
|
|
|
result = match.Groups[1].Value;
|
|
|
|
}
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
class SerialPortInfo
|
|
|
|
{
|
|
|
|
public string Name { get; set; }
|
|
|
|
public string DeviceId { get; set; }
|
|
|
|
public string PnpDeviceId { get; set; }
|
|
|
|
public bool IsEsp32 { get; set; }
|
|
|
|
|
|
|
|
public override string ToString()
|
|
|
|
{
|
|
|
|
if (IsEsp32)
|
|
|
|
return Name + " (ESP32)";
|
|
|
|
else
|
|
|
|
return Name;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
class WaveInDevice
|
|
|
|
{
|
|
|
|
public int DeviceNumber { get; set; }
|
|
|
|
public string ProductName { get; set; }
|
|
|
|
public override string ToString()
|
|
|
|
{
|
|
|
|
return ProductName;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
class WaveOutDevice
|
|
|
|
{
|
|
|
|
public int DeviceNumber { get; set; }
|
|
|
|
public string ProductName { get; set; }
|
|
|
|
public MMDevice MMDevice { get; set; } // Added for volume control
|
|
|
|
public override string ToString()
|
|
|
|
{
|
|
|
|
return ProductName;
|
|
|
|
}
|
2024-10-28 00:44:38 +08:00
|
|
|
}
|
|
|
|
}
|