V1, Library and example GUI Application.

This commit is contained in:
Andrea Santaniello 2024-11-04 19:09:54 +01:00
parent 357047bc40
commit 0315d8d7dc
6 changed files with 351 additions and 210 deletions

View File

@ -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<byte[]> preBufferQueue = new ConcurrentQueue<byte[]>();
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}");
}
}
}
}

View File

@ -1,17 +1,17 @@
<?xml version="1.0" encoding="utf-8"?>
<root>
<!--
<!--
Microsoft ResX Schema
Version 2.0
The primary goals of this format is to allow a simple XML format
that is mostly human readable. The generation and parsing of the
various data types are done through the TypeConverter classes
The primary goals of this format is to allow a simple XML format
that is mostly human readable. The generation and parsing of the
various data types are done through the TypeConverter classes
associated with the data types.
Example:
... ado.net/XML headers & schema ...
<resheader name="resmimetype">text/microsoft-resx</resheader>
<resheader name="version">2.0</resheader>
@ -26,36 +26,36 @@
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
<comment>This is a comment</comment>
</data>
There are any number of "resheader" rows that contain simple
There are any number of "resheader" rows that contain simple
name/value pairs.
Each data row contains a name, and value. The row also contains a
type or mimetype. Type corresponds to a .NET class that support
text/value conversion through the TypeConverter architecture.
Classes that don't support this are serialized and stored with the
Each data row contains a name, and value. The row also contains a
type or mimetype. Type corresponds to a .NET class that support
text/value conversion through the TypeConverter architecture.
Classes that don't support this are serialized and stored with the
mimetype set.
The mimetype is used for serialized objects, and tells the
ResXResourceReader how to depersist the object. This is currently not
The mimetype is used for serialized objects, and tells the
ResXResourceReader how to depersist the object. This is currently not
extensible. For a given mimetype the value must be set accordingly:
Note - application/x-microsoft.net.object.binary.base64 is the format
that the ResXResourceWriter will generate, however the reader can
Note - application/x-microsoft.net.object.binary.base64 is the format
that the ResXResourceWriter will generate, however the reader can
read any of the formats listed below.
mimetype: application/x-microsoft.net.object.binary.base64
value : The object must be serialized with
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.soap.base64
value : The object must be serialized with
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.bytearray.base64
value : The object must be serialized into a byte array
value : The object must be serialized into a byte array
: using a System.ComponentModel.TypeConverter
: and then encoded with base64 encoding.
-->
@ -117,4 +117,66 @@
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<assembly alias="System.Drawing" name="System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" />
<data name="$this.Icon" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>
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
</value>
</data>
</root>

View File

@ -2,13 +2,29 @@
<PropertyGroup>
<OutputType>WinExe</OutputType>
<TargetFramework>net8.0-windows</TargetFramework>
<TargetFramework>net7.0-windows</TargetFramework>
<Nullable>enable</Nullable>
<UseWindowsForms>true</UseWindowsForms>
<ImplicitUsings>enable</ImplicitUsings>
<EnableWindowsTargeting>true</EnableWindowsTargeting>
<Company>Monocul.us</Company>
<Authors>Andrea Santaniello</Authors>
<Description>Radio GUI for KV4P radio</Description>
<PackageIcon>icon-lg-dark.png</PackageIcon>
<ApplicationIcon>icon-lg-dark.ico</ApplicationIcon>
</PropertyGroup>
<ItemGroup>
<Content Include="icon-lg-dark.ico" />
</ItemGroup>
<ItemGroup>
<None Include="E:\malattie\malattie_json\icon-lg-dark.png">
<Pack>True</Pack>
<PackagePath>\</PackagePath>
</None>
</ItemGroup>
<ItemGroup>
<PackageReference Include="NAudio" Version="2.2.1" />
</ItemGroup>

BIN
RadioGUI/icon-lg-dark.ico Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.3 KiB

View File

@ -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);

View File

@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<TargetFramework>net7.0</TargetFramework>
<RootNamespace>kv4p_sharp</RootNamespace>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>