Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions ft8af/app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,10 @@
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" /> <!-- For GPS location access -->
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
<!-- Required to hold a WifiManager.MulticastLock so the Wi-Fi chip delivers
FlexRadio UDP discovery broadcasts to us (Android filters broadcast/
multicast packets without it). -->
<uses-permission android:name="android.permission.CHANGE_WIFI_MULTICAST_STATE" />
<uses-permission
android:name="android.permission.BLUETOOTH"
android:maxSdkVersion="30" />
Expand Down
18 changes: 18 additions & 0 deletions ft8af/app/src/main/java/com/k1af/ft8af/GeneralVariables.java
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,9 @@ public static boolean isConfiguredUsbAudioOutput(int vid, int pid) {

public static int flexMaxRfPower = 10;//Flex radio max transmit power
public static int flexMaxTunePower = 10;//Flex radio max tune power
// Last FlexRadio address connected to (discovered or typed). Persisted so the
// CAT chip can reconnect on a cold start without re-opening the picker.
public static String flexLastIp = "";

// Hidden debug mode (unlocked by tapping the version 7 times in About).
// When true, Settings exposes the Debug screen for log viewing/sharing.
Expand Down Expand Up @@ -366,6 +369,21 @@ public static Context getMainContext() {
public static int alcTargetLow = 60; // ALC target window low (0-255)
public static int alcTargetHigh = 100; // ALC target window high (0-255)

// Meters HUD: which meters the pull-down HUD shows. The HUD adapts per rig —
// only meters the connected rig actually reports are shown — and these flags
// gate which of the available ones the user wants. SWR + ALC are the two
// universal across CAT rigs and default on; the richer meters (only some
// rigs, e.g. FlexRadio/Xiegu network, report them) default off.
public static boolean meterShowSwr = true;
public static boolean meterShowAlc = true;
// Power defaults on: only the network rigs (Flex/Xiegu) report output watts,
// and on those it's the natural companion to the in-HUD TX-power control.
// Serial rigs don't report it, so "adapt per rig" hides it there anyway.
public static boolean meterShowPower = true;
public static boolean meterShowSMeter = false;
public static boolean meterShowVoltage = false;
public static boolean meterShowTemp = false;

public static MutableLiveData<Float> mutableBaseFrequency = new MutableLiveData<>();

private static int spectrumWidth = 3500;//Spectrum display width in Hz
Expand Down
24 changes: 24 additions & 0 deletions ft8af/app/src/main/java/com/k1af/ft8af/MainViewModel.java
Original file line number Diff line number Diff line change
Expand Up @@ -1283,13 +1283,37 @@ public void connectFlexRadioRig(Context context, FlexRadio flexRadio) {
}
}
GeneralVariables.controlMode = ControlMode.CAT;//network control mode
// Remember the address so the CAT chip can reconnect on a cold start
// without re-opening the picker.
if (flexRadio != null && flexRadio.getIp() != null && !flexRadio.getIp().isEmpty()) {
GeneralVariables.flexLastIp = flexRadio.getIp();
databaseOpr.writeConfig("flexLastIp", flexRadio.getIp(), null);
}
// Reflect the in-progress attempt on the CAT chip immediately; the Flex
// connect is async (TCP), so without this the chip looks idle until/unless
// the link comes up.
setCatConnectionState(CatConnectionState.CONNECTING);
FlexConnector flexConnector = new FlexConnector(context, flexRadio, GeneralVariables.controlMode);
flexConnector.setOnWaveDataReceived(new FlexConnector.OnWaveDataReceived() {
@Override
public void OnDataReceived(int bufferLen, float[] buffer) {
hamRecorder.doOnWaveDataReceived(bufferLen, buffer);
}
});
// Surface success/failure: the Flex path doesn't fire the BaseRig
// onConnected() callback, so without this the CAT chip never turns
// connected and there's no "connected" confirmation toast.
flexConnector.setOnConnectionResult(new FlexConnector.OnConnectionResult() {
@Override
public void onConnected() {
setCatConnectionState(CatConnectionState.CONNECTED);
ToastMessage.show(getStringFromResource(R.string.connected_rig));
}
@Override
public void onFailed() {
setCatConnectionState(CatConnectionState.ERROR);
}
});
flexConnector.connect();
connectRig();

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,12 @@ public class FlexConnector extends BaseRigConnector {
public interface OnWaveDataReceived{
void OnDataReceived(int bufferLen,float[] buffer);
}
/** Fired when the Flex link is up (TCP connected + GUI client created) or fails,
* so MainViewModel can surface the CAT connection state + a success/failure toast. */
public interface OnConnectionResult{
void onConnected();
void onFailed();
}
public int maxRfPower;
public int maxTunePower;

Expand All @@ -40,6 +46,11 @@ public interface OnWaveDataReceived{
private FlexRadio flexRadio;

private OnWaveDataReceived onWaveDataReceived;
private OnConnectionResult onConnectionResult;

public void setOnConnectionResult(OnConnectionResult listener){
this.onConnectionResult = listener;
}


public FlexConnector(Context context, FlexRadio flexRadio, int controlMode) {
Expand Down Expand Up @@ -209,13 +220,14 @@ public void onConnectSuccess(RadioTcpClient tcpClient) {
//flexRadio.sendCommand("c1|client gui\n");
//playData();


if (onConnectionResult != null) onConnectionResult.onConnected();
}

@Override
public void onConnectFail(RadioTcpClient tcpClient) {
ToastMessage.show(String.format(GeneralVariables.getStringFromResource
(R.string.flex_connect_failed),flexRadio.getModel()));
if (onConnectionResult != null) onConnectionResult.onFailed();
}
});

Expand Down
22 changes: 22 additions & 0 deletions ft8af/app/src/main/java/com/k1af/ft8af/database/DatabaseOpr.java
Original file line number Diff line number Diff line change
Expand Up @@ -2565,6 +2565,9 @@ protected Void doInBackground(Void... voids) {
if (name.equalsIgnoreCase("flexMaxTunePower")) {//Flex max tune power
GeneralVariables.flexMaxTunePower = result.equals("") ? 10 : Integer.parseInt(result);
}
if (name.equalsIgnoreCase("flexLastIp")) {//Last Flex address, for CAT-chip reconnect
GeneralVariables.flexLastIp = result == null ? "" : result;
}
if (name.equalsIgnoreCase("saveSWL")) {//Save decoded messages
GeneralVariables.saveSWLMessage = result.equals("1");
}
Expand Down Expand Up @@ -2667,6 +2670,25 @@ protected Void doInBackground(Void... voids) {
if (name.equalsIgnoreCase("alcTargetHigh")) {
GeneralVariables.alcTargetHigh = result.equals("") ? 100 : Integer.parseInt(result);
}
// Meters HUD enabled-meters (SWR/ALC default on, rest default off)
if (name.equalsIgnoreCase("meterShowSwr")) {
GeneralVariables.meterShowSwr = result.equals("1");
}
if (name.equalsIgnoreCase("meterShowAlc")) {
GeneralVariables.meterShowAlc = result.equals("1");
}
if (name.equalsIgnoreCase("meterShowPower")) {
GeneralVariables.meterShowPower = result.equals("1");
}
if (name.equalsIgnoreCase("meterShowSMeter")) {
GeneralVariables.meterShowSMeter = result.equals("1");
}
if (name.equalsIgnoreCase("meterShowVoltage")) {
GeneralVariables.meterShowVoltage = result.equals("1");
}
if (name.equalsIgnoreCase("meterShowTemp")) {
GeneralVariables.meterShowTemp = result.equals("1");
}
if (name.equalsIgnoreCase("spectrumWidth")) {
GeneralVariables.setSpectrumWidth(result.equals("") ? 3500 : Integer.parseInt(result));
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,12 @@ public class MeterProtectionController {
public final MutableLiveData<Boolean> swrLockout = new MutableLiveData<>(false);
public final MutableLiveData<Integer> lastAlc = new MutableLiveData<>(0);
public final MutableLiveData<Integer> lastSwr = new MutableLiveData<>(0);
// True once the rig has reported at least one valid meter reading. Lets the
// meters HUD tell "no data yet / unsupported rig" apart from a legitimate 0
// reading (lastAlc/lastSwr start at 0, and 0 is a real value — ALC at no
// drive, SWR at 1.0:1). Never reset: once a rig has reported, the last values
// remain meaningful as the "last TX" readout.
public final MutableLiveData<Boolean> meterDataReceived = new MutableLiveData<>(false);
// Holds the SWR ratio string (e.g. "3.2:1") that triggered lockout, for the banner.
public final MutableLiveData<String> lockoutSwrRatio = new MutableLiveData<>("");

Expand Down Expand Up @@ -72,6 +78,10 @@ public void onMeterUpdate(int normalizedAlc, int normalizedSwr) {
// Publish for optional UI display
if (normalizedAlc >= 0) lastAlc.postValue(normalizedAlc);
if (normalizedSwr >= 0) lastSwr.postValue(normalizedSwr);
if ((normalizedAlc >= 0 || normalizedSwr >= 0)
&& !Boolean.TRUE.equals(meterDataReceived.getValue())) {
meterDataReceived.postValue(true);
}

// Diagnostics: when SWR protection is on, record each SWR reading we actually
// receive so the debug.log shows whether meter data even reaches here during TX, the
Expand Down
50 changes: 49 additions & 1 deletion ft8af/app/src/main/kotlin/radio/ks3ckc/ft8af/FT8AFApp.kt
Original file line number Diff line number Diff line change
Expand Up @@ -38,17 +38,23 @@ import com.k1af.ft8af.ModeProfile
import com.k1af.ft8af.R
import com.k1af.ft8af.database.OperationBand
import com.k1af.ft8af.ft8transmit.FT8TransmitSignal
import com.k1af.ft8af.flex.FlexRadio
import com.k1af.ft8af.rigs.CatConnectionState
import com.k1af.ft8af.rigs.InstructionSet
import com.k1af.ft8af.rigs.BaseRigOperation
import radio.ks3ckc.ft8af.theme.BgApp
import radio.ks3ckc.ft8af.ui.components.ActiveQsoPanel
import radio.ks3ckc.ft8af.ui.components.CatTapAction
import radio.ks3ckc.ft8af.ui.components.catTapAction
import radio.ks3ckc.ft8af.ui.components.shouldShowCatChip
import radio.ks3ckc.ft8af.ui.components.CqOptionsSheet
import radio.ks3ckc.ft8af.ui.components.canEnableFieldDay
import radio.ks3ckc.ft8af.ui.components.shouldPersistSection
import radio.ks3ckc.ft8af.ui.components.FT8AFTab
import radio.ks3ckc.ft8af.ui.components.FrequencyPickerSheet
import radio.ks3ckc.ft8af.ui.components.HoundSetupSheet
import radio.ks3ckc.ft8af.ui.components.MetersSheet
import radio.ks3ckc.ft8af.ui.components.MetersHandle
import radio.ks3ckc.ft8af.ui.components.formatMhz
import radio.ks3ckc.ft8af.ui.components.QsoCelebration
import radio.ks3ckc.ft8af.ui.components.SlotTimerBar
Expand Down Expand Up @@ -159,6 +165,9 @@ fun FT8AFApp(mainViewModel: MainViewModel) {
// Frequency picker sheet state
var showFrequencyPicker by rememberSaveable { mutableStateOf(false) }

// Meters HUD (ALC/SWR pull-down) state — opened by a top-edge swipe-down.
var showMeters by rememberSaveable { mutableStateOf(false) }

// A tapped Needed-DX notification asks us to jump to the Decode tab (DecodeScreen
// then scrolls to + highlights the alerted station).
val preselectCallsign by mainViewModel.mutablePreselectCallsign.observeAsState()
Expand Down Expand Up @@ -488,7 +497,29 @@ fun FT8AFApp(mainViewModel: MainViewModel) {
).show()
}
},
onReconnectCat = { mainViewModel.reconnectRig() },
onReconnectCat = {
when (
catTapAction(
connectorExists = mainViewModel.baseRig?.connector != null,
isFlexNetwork = GeneralVariables.instructionSet == InstructionSet.FLEX_NETWORK,
savedFlexIp = GeneralVariables.flexLastIp,
)
) {
CatTapAction.RECONNECT_EXISTING -> mainViewModel.reconnectRig()
CatTapAction.CONNECT_SAVED_FLEX -> {
val flexRadio = FlexRadio()
flexRadio.ip = GeneralVariables.flexLastIp
flexRadio.model = "FlexRadio"
mainViewModel.connectFlexRadioRig(GeneralVariables.getMainContext(), flexRadio)
}
CatTapAction.NEEDS_SETUP ->
Toast.makeText(
context,
context.getString(R.string.cat_needs_setup),
Toast.LENGTH_SHORT,
).show()
}
},
onOpenFrequencyPicker = { showFrequencyPicker = true },
onToggleExpand = { qsoPanelExpanded = !qsoPanelExpanded },
)
Expand Down Expand Up @@ -640,5 +671,22 @@ fun FT8AFApp(mainViewModel: MainViewModel) {
}
},
)

// Visible pull-tab at the top center opens the meters HUD from anywhere
// (tap or short drag down). Hidden while the HUD is open.
MetersHandle(
enabled = !showMeters,
onOpen = { showMeters = true },
modifier = Modifier.align(Alignment.TopCenter),
)

// Meters HUD (ALC/SWR) — sibling overlay so its scrim and panel sit above
// the tab bar and TxStrip, like the other sheets.
MetersSheet(
visible = showMeters,
mainViewModel = mainViewModel,
isTransmitting = isTransmitting,
onDismiss = { showMeters = false },
)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,29 @@ internal fun catChipVisuals(state: CatConnectionState): CatChipVisuals = when (s
internal fun shouldShowCatChip(controlMode: Int, state: CatConnectionState): Boolean =
controlMode != ControlMode.VOX || state != CatConnectionState.DISCONNECTED

/** What tapping the CAT chip should do, given the current connection state. */
enum class CatTapAction { RECONNECT_EXISTING, CONNECT_SAVED_FLEX, NEEDS_SETUP }

/**
* Decide what a tap on the CAT chip does. If a connector already exists, just
* reconnect it (the existing behavior — handy for Bluetooth/Flex retries).
* Otherwise, on a cold start, if the saved rig is a network Flex with a
* remembered address, connect straight to it — "my settings are set, just
* connect" — instead of doing nothing (the old reconnectRig() no-ops with no
* connector). With nothing saved to go on, the user needs to set the rig up.
*
* Pure so the routing is unit-tested without the rig/connect plumbing.
*/
internal fun catTapAction(
connectorExists: Boolean,
isFlexNetwork: Boolean,
savedFlexIp: String,
): CatTapAction = when {
connectorExists -> CatTapAction.RECONNECT_EXISTING
isFlexNetwork && savedFlexIp.isNotBlank() -> CatTapAction.CONNECT_SAVED_FLEX
else -> CatTapAction.NEEDS_SETUP
}

/**
* A small CAT (rig control) connection indicator for the TX strip. Tappable to
* re-trigger the connection — Bluetooth often only connects on the second try,
Expand Down
Loading
Loading