Verified against local source trees (2026-08-02): baseline FlexLib 4.1.5.39794 and 4.2.18.41174 (the version this repo builds against), cross-checked with the corrected general developer record in FlexLib-API-Docs at commit
7983f65(verified there against 4.2.20.41343). Each "new" or "changed" claim in the 4.1.5-to-4.2.x section was confirmed against the 4.1.5 source rather than assumed. Earlier editions of this guide carried eight incorrect "new in 4.2.x" claims; see issue #60 for the corrections history.
This guide covers upgrading between FlexLib versions.
FlexLib 4.2.x adds the NAVTEX subsystem, substantially expands HAAPI, and makes a small number of breaking API changes. Most 4.1.5 programs will compile with only one or two targeted changes.
SmartStreamer4 migrated to 4.2.18.41174 (2026-04-29, project-file changes only) and then to 4.2.20.41343 (2026-08-02, issue #61). The 4.2.18 to 4.2.20 source delta is two files with no public API change; the 4.2.20 release-note audio-drop/mute fixes are firmware/DAX server-side, so the client upgrade required only repointing the project reference.
The scope is narrower than it looks. Radio model support, the RadioPlatform enum, TlsCommandCommunication, and most of the properties an upgrading reader might expect to be new were already present in 4.1.5.39794 and need no action.
FirmwareRequiredVersion.cs:18 reads 3.3.32.8203 in both the 4.1.5 and the 4.2.x trees. An earlier version of this guide called that "the minimum compatible firmware version". That was wrong twice over, corrected 2026-08-03:
- It is not a minimum.
Radio.cs:542compares_version != _req_version, an exact-equality test. Any firmware that is not that precise build sets_updateRequired. - The constant is stale. The file header says Jenkins populates it from the most recent firmware built, but it is byte-identical across the 4.1.5 and 4.2.20 drops, so a 4.2.20 library still asks for a 3.3.x build. FlexRadio evidently stopped bumping it.
In practice it is inert for a non-GUI client. _updateRequired only feeds UpdateConnectedState(), which sets the ConnectedState display string to Update; nothing in FlexLib gates Connect() on it, and a smoothlake_dev marker file forces it false. A FlexRadio's firmware version tracks its SmartSDR version, so the requirement worth stating to operators is the SmartSDR version, not a firmware number. Upgrading FlexLib does not by itself require a firmware update.
The fault event on the HAAPI class was renamed, and the delegate gained a
second parameter. Renaming alone is not enough: your handler must also take
the new noun argument, or it will no longer bind to the event.
| 4.1.5 | 4.2.x |
|---|---|
event AmplifierFaultEventHandler AmplifierFault |
event HaapiFaultEventHandler HaapiFault |
delegate void AmplifierFaultEventHandler(string reason) |
delegate void HaapiFaultEventHandler(string noun, string reason) |
Old:
radio.HAAPI.AmplifierFault += OnAmplifierFault;
void OnAmplifierFault(string reason)
{
Console.WriteLine($"Fault: {reason}");
}New:
radio.HAAPI.HaapiFault += OnHaapiFault;
void OnHaapiFault(string noun, string reason)
{
Console.WriteLine($"Fault [{noun}]: {reason}");
}noun names the subsystem that reported the fault. A handler carried over
unchanged from 4.1.5 takes one parameter and will not compile against the
two-parameter delegate.
Earlier editions of this guide said the handler signature was unchanged and showed a two-parameter 4.1.5 handler that never existed. If you migrated by renaming only, that is why your build failed.
The AmpIsSelected property has been removed from HAAPI with no direct replacement. If your code read this property, remove the reference. Use AmpMode to track the amplifier's operating state instead.
// Old (no longer compiles)
if (radio.HAAPI.AmpIsSelected) { ... }
// Alternative: check operating mode
if (radio.HAAPI.AmpMode == AmplifierMode.OPERATE) { ... }These properties still compile but emit [Obsolete] warnings. They were
already marked obsolete in 4.1.5 (Radio.cs:897 and Radio.cs:912 in that
tree), so this is not a 4.2.x change. It is listed here because it is worth
clearing while you are already touching the code, before a future version
removes them.
| Deprecated | Replacement |
|---|---|
Radio.InUseIP |
Radio.GuiClientIPs |
Radio.InUseHost |
Radio.GuiClientHosts |
// Deprecated (still works, but warns)
string ip = radio.InUseIP;
string host = radio.InUseHost;
// Correct
string ip = radio.GuiClientIPs;
string host = radio.GuiClientHosts;NAVTEX (Narrow-band direct-printing) is now fully supported for FLEX-9x00 series radios. Access it via radio.NAVTEX.
// Toggle NAVTEX mode on/off
radio.NAVTEX.TryToggleNAVTEX();
// Or specify the broadcast frequency (default is international 518 kHz)
radio.NAVTEX.TryToggleNAVTEX(NAVTEX.LOCAL_BROADCAST_FREQ_HZ); // 490 kHz
// Monitor status
radio.NAVTEX.PropertyChanged += (s, e) =>
{
if (e.PropertyName == nameof(NAVTEX.Status))
Console.WriteLine($"NAVTEX status: {radio.NAVTEX.Status}");
};
// Send a NAVTEX message
var msg = new NAVTEXMsg(
dateTime: null,
idx: null,
serial: null,
txIdent: 'A',
subjInd: 'B',
msgStr: "NAVTEX TEST MESSAGE",
status: NAVTEXMsgStatus.Pending
);
radio.NAVTEX.Send(msg);NAVTEXStatus values: Inactive, Active, Transmitting, QueueFull, Unlicensed, Error
NAVTEXMsgStatus values: Pending, Queued, Sent, Error
Broadcast frequency constants:
NAVTEX.INTERNATIONAL_BROADCAST_FREQ_HZ // 518,000 Hz
NAVTEX.LOCAL_BROADCAST_FREQ_HZ // 490,000 Hz
NAVTEX.MARINE_SAFETY_INFORMATION_BROADCAST_FREQ_HZ // 4,209,500 HzIn addition to the renamed fault event, HAAPI now exposes warning events and an explicit mode-change method.
New events:
// Warning raised (state becomes WARNING)
radio.HAAPI.HaapiWarning += (noun, reason) =>
{
Console.WriteLine($"Warning [{noun}]: {reason}");
};
// Warning cleared (state returns to OK)
radio.HAAPI.HaapiWarningCleared += (noun) =>
{
Console.WriteLine($"Warning cleared: {noun}");
};New method, change amplifier mode:
// Request mode change (result confirmed via AmpMode PropertyChanged)
radio.HAAPI.HaapiChangeMode(AmplifierMode.OPERATE);
radio.HAAPI.HaapiChangeMode(AmplifierMode.STANDBY);New metering events (subscribe to live data from the Overlord PA):
radio.HAAPI.HaapiFwdPwrDataReady += (data) => { /* forward power, watts */ };
radio.HAAPI.HaapiVswrDataReady += (data) => { /* SWR */ };
radio.HAAPI.HaapiHVDataReady += (data) => { /* HV supply voltage */ };
radio.HAAPI.HaapiCurrentDataReady += (data) => { /* HV supply current */ };
radio.HAAPI.HaapiTempPsuDataReady += (data) => { /* PSU temperature */ };
radio.HAAPI.HaapiTempPa0DataReady += (data) => { /* PA module 0 temperature */ };
radio.HAAPI.HaapiTempPa1DataReady += (data) => { /* PA module 1 temperature */ };
radio.HAAPI.HaapiTempDrvADataReady+= (data) => { /* Driver A temperature */ };
radio.HAAPI.HaapiTempDrvBDataReady+= (data) => { /* Driver B temperature */ };
radio.HAAPI.HaapiTempCombDataReady+= (data) => { /* Combiner temperature */ };
radio.HAAPI.HaapiTempHpfDataReady += (data) => { /* HPF load temperature */ };All eleven use the same delegate, HAAPI.MeterDataReadyEventHandler(float data).
Note that HaapiTempDrvBDataReady is declared but not wired to a meter
subscription in either 4.2.18 or 4.2.20, so it never fires; the other ten are
wired.
| Property | Type | Description |
|---|---|---|
IsSystemModel |
bool |
Read/write; identifies system and government model variants |
TurfRegion |
string |
Read/write; turf and region string from the radio |
Both are new in 4.2.x (Radio.cs:683 and Radio.cs:698). Nothing else on
Radio needs attention when upgrading.
The package references in FlexLib.csproj moved:
| Package | 4.1.5 | 4.2.x |
|---|---|---|
| AsyncAwaitBestPractices | 9.0.0 | 10.0.0 |
| System.Threading.Tasks.Dataflow | not referenced | 10.0.1 |
DotNetZip (1.16.0), Newtonsoft.Json (13.0.3), and
System.Collections.Immutable (9.0.0) are unchanged. If you reference
FlexLib as a project rather than a package, restore after upgrading so the
new Dataflow reference resolves.
The DAX internals were significantly rewritten in 4.2.x. The public surface is mostly backward-compatible, but there are behavioral changes and one new method that multiFLEX applications must adopt.
A new RequestTX(bool tx) method lets a client explicitly claim or yield DAX TX ownership when another client currently holds it. This is required in multiFLEX scenarios. In 4.1.5, there was no programmatic way to take back TX; you had to rely on the radio's automatic arbitration.
// Request TX ownership (e.g. your client wants to transmit)
txStream.RequestTX(true);
// Yield TX ownership (e.g. allow another client to transmit)
txStream.RequestTX(false);
// Watch Transmit property to see if the radio granted ownership
txStream.PropertyChanged += (s, e) =>
{
if (e.PropertyName == "Transmit")
Console.WriteLine($"TX state: {txStream.Transmit}");
};The Transmit property still reflects the radio's actual grant; setting RequestTX does not immediately set Transmit.
The AddTXData(float[] tx_data_stereo, bool sendReducedBW = false) signature is unchanged, but the implementation was completely rewritten with pre-allocated packet buffers. There are no per-call heap allocations in 4.2.x.
Action required: none, no code changes needed. Applications that call AddTXData at high rates will see lower GC pressure and reduced latency jitter.
Packet size is still fixed at 128 stereo float pairs (256 floats total). Passing a buffer of any other length will be ignored with a debug warning, same as before.
RXGain takes 0 to 100 and maps it linearly onto -10 dB to +10 dB, so 50 is
exactly unity gain. This is identical in both versions (the mapping in
DAXMICAudioStream.cs is unchanged between the two trees). No re-tuning is
needed.
In 4.1.5, the Slice property could be resolved before the client handle was
known, causing the lookup to fail and leaving Slice null. In 4.2.x, slice
resolution is deferred until every status key in an update has been parsed,
and the stream's gain is re-applied once the slice reference is established.
Action required: none, this is a bug fix. Code that worked around a null
Slice by polling or subscribing to PropertyChanged continues to work, and
will now also see Slice set correctly on the first status update.
The stream creation and teardown methods on Radio are unchanged:
// Create streams (same as 4.1.5)
radio.RequestDAXRXAudioStream(int channel);
radio.RequestDAXTXAudioStream();
radio.RequestDAXMICAudioStream();
radio.RequestDAXIQStream(int channel);
radio.RequestRXRemoteAudioStream();
radio.RequestRXRemoteAudioStream(bool isCompressed);
radio.RequestRemoteAudioTXStream();
// Remove streams (same as 4.1.5)
radio.RemoveAudioStream(uint stream_id);
radio.RemoveDAXTXAudioStream(uint stream_id);
radio.RemoveDAXMICAudioStream(uint stream_id);
radio.RemoveDAXIQStream(uint stream_id);
radio.RemoveRXRemoteAudioStream(uint stream_id);
radio.RemoveTXRemoteAudioStream(uint stream_id);
// Events (same as 4.1.5)
radio.DAXRXAudioStreamAdded += ...;
radio.DAXRXAudioStreamRemoved += ...;
radio.DAXTXAudioStreamAdded += ...;
radio.DAXTXAudioStreamRemoved += ...;
radio.DAXMICAudioStreamAdded += ...;
radio.DAXMICAudioStreamRemoved+= ...;
radio.DAXIQStreamAdded += ...;
radio.DAXIQStreamRemoved += ...;- Rename
HAAPI.AmplifierFaultsubscriptions toHAAPI.HaapiFault - Add the
string nounparameter to every fault handler, and renameAmplifierFaultEventHandlertoHaapiFaultEventHandler - Remove any references to
HAAPI.AmpIsSelected - Restore packages so the new
System.Threading.Tasks.Dataflowreference resolves - Optional: replace the already-obsolete
Radio.InUseIPandRadio.InUseHostwithRadio.GuiClientIPsandRadio.GuiClientHosts - DAX: Add
RequestTX(bool tx)calls where multiFLEX TX ownership handoff is needed - DAX: Remove any workarounds for null
DAXRXAudioStream.Sliceon first status update
Primarily a maintenance release: .NET 8.0 multi-targeting, dependency updates, no breaking API changes.
Previous:
- .NET Framework 4.6.2
Current:
- .NET Framework 4.6.2
- .NET 8.0 (new)
<!-- Recommended: target .NET 8.0 -->
<PropertyGroup>
<TargetFramework>net8.0-windows</TargetFramework>
</PropertyGroup>
<!-- Or continue using .NET Framework 4.6.2 -->
<PropertyGroup>
<TargetFramework>net462</TargetFramework>
</PropertyGroup>| Package | Old Version | New Version |
|---|---|---|
| AsyncAwaitBestPractices | 7.x | 9.0.0 |
| System.Collections.Immutable | 6.x | 9.0.0 |
| Newtonsoft.Json | 13.0.1 | 13.0.3 |
dotnet add package AsyncAwaitBestPractices --version 9.0.0
dotnet add package System.Collections.Immutable --version 9.0.0This was a major version update with significant breaking changes.
// Old
using FlexLib;
// New
using Flex.Smoothlake.FlexLib;// Old (v3.x)
Discovery.Start();
Discovery.RadioDiscovered += OnRadioDiscovered;
// New (v4.x)
API.RadioAdded += (radio) => { /* handle radio */ };
API.Init();// Old (v3.x)
radio.Connect("192.168.1.100");
// New (v4.x)
radio.Connect(); // IP comes from discovery// Old (v3.x)
Slice slice = radio.CreateSlice();
// New (v4.x)
radio.SliceAdded += (slice) => { slice.Freq = 14.200; };
radio.RequestSlice();// Old (v3.x)
AudioStream stream = new AudioStream(radio, 1);
stream.DataAvailable += OnAudioData;
// New (v4.x)
radio.DAXRXAudioStreamAdded += (audioStream) =>
{
audioStream.DataReady += OnAudioData;
};
radio.RequestDAXRXAudioStream(1);| Old (v3.x) | New (v4.x) |
|---|---|
Radio.IsConnected |
Radio.Connected |
Slice.Frequency |
Slice.Freq |
Slice.RxAntenna |
Slice.RXAnt |
Slice.TxAntenna |
Slice.TXAnt |
Before (v3.x):
using FlexLib;
class Program
{
static void Main()
{
Discovery.Start();
Discovery.RadioDiscovered += (radio) =>
{
radio.Connect("192.168.1.100");
if (radio.IsConnected)
{
Slice slice = radio.CreateSlice();
slice.Frequency = 14.200;
}
};
}
}After (v4.x):
using Flex.Smoothlake.FlexLib;
class Program
{
static async Task Main()
{
API.ProgramName = "MyApp";
API.RadioAdded += async (radio) =>
{
radio.Connect();
for (int i = 0; i < 50 && !radio.Connected; i++)
await Task.Delay(100);
if (radio.Connected)
{
radio.SliceAdded += (slice) => { slice.Freq = 14.200; };
radio.RequestSlice();
}
};
API.Init();
await Task.Delay(-1);
}
}| Removed | Use Instead |
|---|---|
Radio.CreateSlice() |
radio.RequestSlice() |
Discovery.Start() |
API.Init() |
Discovery.Stop() |
API.CloseSession() |
Update using statements: using Flex.Smoothlake.FlexLib;
Remove the IP address argument: radio.Connect();
Subscribe to radio.SliceAdded before calling radio.RequestSlice().
Subscribe to events before calling API.Init() so you do not miss radios discovered immediately on startup.
Events fire on background threads. Dispatch UI updates:
radio.PropertyChanged += (s, e) =>
Dispatcher.Invoke(() => { /* update UI */ });| Version | Notable Changes |
|---|---|
| 4.2.x | NAVTEX support, HAAPI warning and metering events, HaapiChangeMode, DAX TX ownership via RequestTX |
| 4.1.5 | .NET 8.0 multi-targeting, dependency updates |
| 4.1.0 | Bug fixes, performance improvements |
| 4.0.0 | Major refactor, new API design |
| 3.x | Original FlexLib implementation |
Questions? See Getting Started or contact support@flexradio.com.