FT891.Core
1.2.0
Legacy code
See the version list below for details.
dotnet add package FT891.Core --version 1.2.0
NuGet\Install-Package FT891.Core -Version 1.2.0
<PackageReference Include="FT891.Core" Version="1.2.0" />
<PackageVersion Include="FT891.Core" Version="1.2.0" />
<PackageReference Include="FT891.Core" />
paket add FT891.Core --version 1.2.0
#r "nuget: FT891.Core, 1.2.0"
#:package FT891.Core@1.2.0
#addin nuget:?package=FT891.Core&version=1.2.0
#tool nuget:?package=FT891.Core&version=1.2.0
FT891.Core
The library at the heart of the solution: a complete, async CAT client for the Yaesu FT-891, the protocol engine that drives it, the command table it relies on, and the transport abstraction that lets it talk to either real hardware or the simulator.
Target framework: net48 · Language: C# latest · No runtime dependencies beyond
System.IO.Ports(for the serial transport).
Folder layout
FT891.Core/
├── Enums/
│ └── Enums.cs OperatingMode, AgcMode, MeterType, TunerState,
│ ScanMode, BreakInMode, BandSelect
├── Models/
│ ├── RadioInfo.cs record: frequency, mode, TX, S-meter, split, channel
│ └── MeterReading.cs record: meter type + value
├── Protocol/
│ ├── CatSpec.cs command → reply-length table (the source of truth)
│ ├── CatSerialEngine.cs the single byte-count-driven TransceiveAsync
│ ├── FT891Cat.cs ICatInterface implementation (one-liners)
│ ├── ICatTransport.cs the byte-pipe abstraction
│ ├── SerialPortTransport.cs real radio (RS-232 / USB, 8-N-2)
│ ├── TcpCatTransport.cs virtual serial port over TCP
│ └── Num.cs Clamp helper (Math.Clamp is absent on net48)
├── Interface/
│ └── ICatInterface.cs the full public surface
└── Properties/
└── IsExternalInit.cs polyfill so records/init compile on net48
How it fits together
1. CatSpec — the response-length table
A single dictionary maps each 2-letter command to the total number of bytes
its reply contains, including the prefix and the trailing ;:
{ "FA", 12 }, // "FA014250000;" → 2 + 9 + 1
{ "MD", 5 }, // "MD0" + 1 hex mode digit + ";"
{ "IF", 34 }, // full status frame
...
This is the only place that encodes per-command knowledge. 58 commands are
described. The lengths were derived from (and kept consistent with) the exact
frame formats FT891Cat builds and parses, so a count-driven read always lands
on the right boundary. (A handful of lengths were corrected from the original
single-file source for self-consistency — see the table at the bottom.)
2. CatSerialEngine.TransceiveAsync — the only I/O
public async Task<string> TransceiveAsync(string command, int expectedBytes = -1)
- Ensures the command ends with
;. - If
expectedBytesis-1, looks it up inCatSpecfrom the 2-char prefix. expectedBytes == 0→ a set-only command; write and return immediately.- Otherwise it reads exactly
expectedBytesbytes in a loop (handling partial TCP/serial reads) — never scanning for a delimiter. - A
SemaphoreSlimserialises access so concurrent callers can't interleave a write/read pair.
3. ICatTransport — serial or TCP
public interface ICatTransport : IDisposable
{
bool IsOpen { get; }
void Open(); void Close(); void DiscardInBuffer();
void Write(string data);
int Read(byte[] buffer, int offset, int count);
}
SerialPortTransport— wrapsSystem.IO.Ports.SerialPortconfigured the way the FT-891 expects: 38400 baud, 8 data bits, no parity, two stop bits, ASCII encoding, 2000 ms read/write timeout.TcpCatTransport— treats a TCP socket as a virtual serial port. A socket read-timeout is surfaced as aTimeoutException, so "the radio said nothing" always looks the same regardless of transport.
4. FT891Cat — one-liners over the engine
Every method is a single expression that builds a frame (for a set) or builds a frame, awaits the reply, and slices out the value (for a read):
public Task SetVfoAFrequencyAsync(long hz) => Set($"FA{hz:D9};");
public async Task<long> GetVfoAFrequencyAsync() => long.Parse((await Read("FA;")).Substring(2, 9));
Three constructors let you choose the transport:
new FT891Cat("COM3"); // real serial port
new FT891Cat(new TcpCatTransport("127.0.0.1", 4000)); // simulator / network
new FT891Cat(existingCatSerialEngine); // bring your own engine
API surface (ICatInterface)
| Group | Representative members |
|---|---|
| Connection | Connect, Disconnect, IsConnected, Dispose |
| VFO & frequency | Get/SetVfoAFrequencyAsync, Get/SetVfoBFrequencyAsync, CopyVfoAToVfoBAsync, CopyVfoBToVfoAAsync, SwapVfosAsync, BandUp/DownAsync, SelectBandAsync, FrequencyUp/DownAsync, ZeroInAsync |
| Mode | Get/SetModeAsync |
| Memory | Get/SetMemoryChannelAsync, CopyVfoAToMemoryAsync, CopyMemoryToVfoAAsync, ChannelUpDownAsync, WriteMemoryAsync, ReadMemoryAsync, Store/RecallQuickMemoryBankAsync |
| TX / PTT / split | IsTransmittingAsync, SetMoxAsync, Get/SetSplitAsync |
| Power | Get/SetPowerAsync, Get/SetTxPowerAsync |
| Audio levels | AF, RF, Mic, Squelch, Monitor — Get/Set…Async |
| RF / antenna | Get/SetRfAttenuatorAsync, Get/SetPreampAsync, Get/SetTunerStateAsync |
| DSP / filters | AGC, Noise Reduction (+level), Noise Blanker (+level), Auto Notch, Manual Notch, IF Shift, Bandwidth, Contour |
| CW | Get/SetKeySpeedAsync, Get/SetKeyPitchAsync, Get/SetBreakInModeAsync, Get/SetSemiBreakInDelayAsync, Get/SetCwSpotAsync, Get/SetKeyerAsync, SendCwAsync, Get/SetKeyerMemoryAsync |
| VOX | Get/SetVoxAsync, Get/SetVoxGainAsync, Get/SetVoxDelayAsync |
| Speech processor | Get/SetSpeechProcessorAsync, Get/SetSpeechProcessorLevelAsync |
| Clarifier | Get/SetClarifierAsync, ClarifierUp/DownAsync, ClarifierClearAsync |
| CTCSS / repeater | Get/SetCtcssAsync, Get/SetCtcssDcsNumberAsync, Get/SetOffsetAsync |
| Scan | Get/SetScanModeAsync |
| Meters / status | GetSMeterAsync, ReadMeterAsync, GetBusyAsync, GetRadioInfoAsync, GetRadioIdAsync, GetOppositeVfoInfoAsync |
| Display / UI | Dimmer, Lock, Fast-step, Auto-information — Get/Set…Async |
| Message memory | PlaybackAsync, LoadMessageAsync |
| Escape hatch | SendRawCommandAsync(command, expectedBytes = -1) |
Values are clamped to the radio's valid ranges before transmission (e.g. TX
power 5–100 W, key speed 4–60 WPM) via the Num.Clamp helper.
Running modern C# on .NET Framework 4.8
The original source used several APIs/syntax that don't exist on net48. They are handled so the code stays clean and idiomatic:
| Modern feature | net48 accommodation |
|---|---|
record types + init setters |
Properties/IsExternalInit.cs polyfill + <LangVersion>latest</LangVersion> |
Math.Clamp |
Num.Clamp(value, min, max) helper |
string.EndsWith(char) |
EndsWith(";", StringComparison.Ordinal) |
range slicing s[..2] |
s.Substring(0, 2) (no System.Index/Range on net48) |
System.IO.Ports |
NuGet package System.IO.Ports 8.0.0 |
| net48 reference assemblies | Microsoft.NETFramework.ReferenceAssemblies (cross-environment build) |
Reply lengths corrected from the original single-file source
| Command | Was | Now | Why |
|---|---|---|---|
FA / FB |
13 | 12 | FA + 9 digits + ; = 12 |
IS |
9 | 10 | flag + sign + 4 digits needs 10 |
CO |
11 | 13 | flag + 4-digit level + 4-digit bias |
KM |
55 | 54 | slot + 50-char message + ; |
IF |
30 | 34 | status fields parsed up to index 32 |
These keep the count-driven reader perfectly aligned with the bytes a real radio (and the simulator) emits.
READMEs written by Claude Code Opus 4.8m Context - From CodeBase - TSGBMPPT
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET Framework | net48 is compatible. net481 was computed. |
-
.NETFramework 4.8
- System.IO.Ports (>= 10.0.8)
NuGet packages (1)
Showing the top 1 NuGet packages that depend on FT891.Core:
| Package | Downloads |
|---|---|
|
FT891.Simulator
A virtual Yaesu FT-891 transceiver over TCP for developing and testing CAT clients without hardware. Embed SimulatorServer in-process (as FT891.Tests does) or run it standalone. |
GitHub repositories
This package is not used by any popular GitHub repositories.