This commit is contained in:
Uladzimir Karpenka 2026-04-13 22:06:52 +03:00
parent 14d983c840
commit dde80e5696
19 changed files with 1599 additions and 1107 deletions

62
Source/NEXT_STEPS.md Normal file
View File

@ -0,0 +1,62 @@
# Next Steps
## Priority
1. Investigate Protocol 2 stop handling.
2. Complete Protocol 2 Alex support for boards with `Alex1`.
3. Continue UI cleanup for Protocol 2 specific hardware presentation.
## Immediate Issues To Debug
### Protocol 2 stop crash
- Re-test why the application crashes when the user presses `Stop` while working with a Protocol 2 device.
- Re-verify why the Protocol 2 stop command may still not be correctly sent to the transceiver.
- Check the shutdown path across:
- `src/DataEngine/cusdr_dataEngine.cpp`
- `src/DataEngine/cusdr_dataIO.cpp`
- `src/DataEngine/cusdr_protocol2_io.cpp`
- Already fixed one likely cause in `src/DataEngine/cusdr_protocol2_io.cpp`:
- duplicate `P2 stop` send,
- stop without `m_running`,
- timer/socket signal disconnect before deletion.
- Already fixed a second class of issues in `src/DataEngine/cusdr_dataEngine.cpp`:
- `DataIO` control calls now go through `QMetaObject::invokeMethod(..., Qt::BlockingQueuedConnection)`,
- so start/stop/init execute in the `DataIO` thread instead of the caller thread.
- Already fixed a concrete `Protocol 2` destructor bug in `src/DataEngine/cusdr_dataIO.cpp`:
- `m_dataIOSocketOn == true` with `m_dataIOSocket == 0` could dereference a null pointer during shutdown.
- Already moved `CSoundOut` deletion out of `DataIO::stop()` and into the destructor to reduce timer/thread teardown issues.
- Pay special attention to:
- socket lifetime,
- timer lifetime,
- `QObject` ownership and deletion thread,
- repeated stop calls,
- `networkDeviceStartStop(0)`,
- `Protocol2DataPath::stop()`,
- `sendHighPriorityPacket(false)`,
- whether the command socket is still valid when stop is triggered,
- whether `DataIO` / `Protocol2DataPath` should be destroyed via `deleteLater()` in their own thread instead of direct delete from `DataEngine`,
- whether the target `P2` board expects additional shutdown packets or repeated `run = 0` frames.
## Protocol 2 Functional Work
- Implement proper `Alex1/BPF2/RX2` handling for boards that support a second Alex register:
- Orion MkII
- SATURN / ANAN-G2
- Use Appendix D from `doc/openHPSDR Ethernet Protocol v4.3.docx` to map the second Alex register bits correctly.
- Consider extending Protocol 2 capability handling beyond board defaults where discovery returns more precise information.
- Consider support for XML/full hardware description discovery replies for board `254/255`.
## UI Work
- Review UI areas that still assume `Metis/Hermes` semantics internally.
- Make Protocol 2 model/capability presentation more explicit where useful:
- selected model,
- protocol version,
- ADC/DDC count,
- Alex availability.
## Reference
- Detailed log of completed work:
- `WORKLOG_PROTOCOL2_2026-04-13.md`

View File

@ -0,0 +1,187 @@
# cudaSDR Protocol 2 Worklog
Date: 2026-04-13
This file records the Protocol 2 related work completed in this repository so future sessions can quickly restore context.
## Implemented Earlier In This Session History
- Added separate openHPSDR Protocol 2 transport implementation:
- `src/DataEngine/cusdr_protocol2_io.h`
- `src/DataEngine/cusdr_protocol2_io.cpp`
- Added separate Protocol 2 discovery implementation:
- `src/DataEngine/cusdr_discoverer_P2.h`
- `src/DataEngine/cusdr_discoverer_P2.cpp`
- Wired Protocol 2 into the existing project flow without replacing Protocol 1.
- Updated `DataIO` so it delegates network start/stop and receive path to the separate Protocol 2 module when the selected device reports `protocolVersion >= 2`.
- Added Protocol 2 fallback search after Protocol 1 and fixed the `Search` button path so manual search also tries Protocol 2.
- Added initial RX-only Alex support for Protocol 2:
- General packet enables Alex.
- High Priority packet sends `Alex 0`.
- Filters switch automatically by band/frequency using existing Alex settings.
## Implemented Today
### 1. Device capability model for Protocol 2
- Extended `TNetworkDevicecard` in `src/cusdr_settings.h` with Protocol 2 specific fields:
- `adcCount`
- `ddcCount`
- `dspClockHz`
- `iqFormatFlags`
- `phaseWords`
- `hasAlex0`
- `hasAlex1`
### 2. Separate board profile helper
- Added:
- `src/DataEngine/cusdr_protocol2_profile.h`
- `src/DataEngine/cusdr_protocol2_profile.cpp`
- This helper maps Protocol 2 board ID to default hardware capabilities and human-readable model name.
- It also provides a summary string for UI device lists.
### 3. Discovery improvements for Protocol 2
- Updated `src/DataEngine/cusdr_discoverer_P2.cpp` to:
- use board profiles,
- fill the new capability fields in `TNetworkDevicecard`,
- parse extra discovery reply fields from the documentation:
- byte 20: number of implemented DDCs,
- byte 21: frequency vs phase word,
- byte 22: supported IQ/endian formats.
- Updated `src/DataEngine/cusdr_discoverer_P1.cpp` to initialize the new fields to safe defaults for Protocol 1 devices.
### 4. Protocol 2 initialization based on actual device profile
- Updated `src/DataEngine/cusdr_protocol2_io.cpp`:
- fixed General packet bit handling,
- enabled phase-word mode where required,
- selected IQ format only when the discovery flags say it is supported,
- used discovered/default ADC count in the DDC Specific packet,
- routed DDCs across ADCs,
- limited receiver count to hardware DDC count,
- used board DSP clock to compute phase words,
- preserved RX-only behavior.
### 5. Data engine behavior for Protocol 2 devices
- Updated `src/DataEngine/cusdr_dataEngine.cpp` so Protocol 2 devices:
- are treated as modern integrated hardware instead of legacy Mercury/Penelope stacks,
- automatically clamp receiver count to discovered DDC count,
- publish Hermes firmware field from the Protocol 2 discovery result for existing UI/status paths,
- enable Alex UI only if the selected Protocol 2 board profile says Alex is present.
### 6. UI modernization for Protocol 2 devices
- Updated `src/cusdr_networkWidget.cpp` so the device list shows a summary instead of only IP address.
- Updated `src/GL/cusdr_oglDisplayPanel.h` and `src/GL/cusdr_oglDisplayPanel.cpp` so the top status display uses the real Protocol 2 model name instead of always showing `Hermes`.
### 7. Documentation-driven corrections made today
- Re-read local documentation:
- `doc/openHPSDR Ethernet Protocol v4.3.docx`
- Corrected Protocol 2 board naming and assumptions from the document:
- board 1: `Hermes / ANAN-10/100`
- board 2: `Hermes / ANAN-10E/100B`
- board 5: `Orion MkII / ANAN-7/8000DLE`
- board 10: `Saturn / ANAN-G2`
- Corrected one important capability assumption:
- board 2 no longer auto-enables Alex RX support, because the document indicates no Alex receive filters for that board.
## Build Status
- Full build completed successfully:
- `make -j4`
- Output binary:
- `bin/cudaSDR`
## Additional Fix Applied Later Today
### Protocol 2 stop path hardening
- Investigated the stop path after a reported crash when pressing `Stop` with a Protocol 2 device.
- Confirmed that the shutdown flow was effectively hitting Protocol 2 stop twice:
- first through `DataEngine::stop() -> DataIO::networkDeviceStartStop(0)`,
- then again through `DataIO::stop() -> Protocol2DataPath::stop()`.
- Updated `src/DataEngine/cusdr_protocol2_io.cpp` so that:
- `Protocol2DataPath::stop()` only sends the final `run = 0` High Priority packet if the path was actually running,
- `networkDeviceStartStop(0)` returns immediately if Protocol 2 is already stopped,
- `closeSockets()` explicitly disconnects timer and socket signals before deletion.
- This removes the most obvious duplicate-stop race and reduces the chance of a timer/socket callback firing during teardown.
- Build was re-checked after this fix:
- `make -j4`
- success
### Still needs runtime verification
- This stop-path fix is compile-verified but still needs validation on real Protocol 2 hardware.
- If the transceiver still does not stop cleanly, the next thing to verify is whether the specific board expects more than a single `High Priority run = 0` packet before socket teardown.
## Additional Investigation And Fixes After Emulator Testing
### 1. Cross-thread DataIO control calls
- Emulator testing showed that Protocol 2 startup and shutdown were still unreliable.
- Investigation found that `DataIO` is moved to its own `QThread`, but `DataEngine` was still calling these methods directly from another thread:
- `sendInitFramesToNetworkDevice(...)`
- `networkDeviceStartStop(...)`
- `stop()`
- Updated `src/DataEngine/cusdr_dataEngine.cpp` to route those calls through:
- `QMetaObject::invokeMethod(..., Qt::BlockingQueuedConnection)`
- This was done so the Protocol 2 sockets and timers are manipulated from the `DataIO` thread they belong to.
### 2. Additional Protocol 2 stop crash findings
- User logs then showed:
- `QObject::killTimer: Timers cannot be stopped from another thread`
- `QObject::~QObject: Timers cannot be stopped from another thread`
- `DataEngine:: data IO thread not yet finished...`
- This indicated there were still object-lifetime issues during teardown, not only packet-order issues.
### 3. DataIO destructor bug in Protocol 2 mode
- Found a concrete bug in `src/DataEngine/cusdr_dataIO.cpp`:
- in Protocol 2 mode, `m_dataIOSocketOn` can be true while `m_dataIOSocket` is never created,
- but `DataIO::~DataIO()` still dereferenced `m_dataIOSocket`.
- Fixed destructor logic so the legacy socket is only closed/deleted when the pointer is valid.
- This was a direct segfault candidate in Protocol 2 shutdown.
### 4. Sound output teardown issue
- Found that `DataIO::stop()` was stopping and deleting `CSoundOut` while Protocol 2 shutdown was already in progress.
- Adjusted `src/DataEngine/cusdr_dataIO.cpp` so:
- `DataIO::stop()` no longer deletes the sound object,
- `CSoundOut` shutdown is deferred to `DataIO::~DataIO()`.
- This was done to reduce cross-thread/timer teardown problems during the live stop sequence.
### Current status after these fixes
- Build re-checked successfully after each change:
- `make -j4`
- Protocol 2 stop handling is more robust than before, but user testing still shows there may be at least one remaining shutdown/lifetime issue.
- Most likely next area:
- final deletion order of `DataIO` / `Protocol2DataPath` and their child `QObject`s after `m_dataIOThread->quit()`.
## Known Limitations / Remaining Work
- Protocol 2 implementation is still RX-only.
- Protocol 2 Alex support currently sends `Alex 0` only.
- Boards with `Alex1` support such as Orion MkII / SATURN are not yet fully mapped to the second Alex register according to Appendix D.
- Full XML or extended hardware-description discovery modes (`board 254/255`) are not implemented.
- Wideband Protocol 2 path is still not integrated.
- Some UI sections still conceptually assume old `Metis/Hermes` naming internally even though the visible device/model handling is now better.
## Good Starting Points For Next Session
- If continuing Protocol 2 work, read these files first:
- `src/DataEngine/cusdr_protocol2_profile.cpp`
- `src/DataEngine/cusdr_discoverer_P2.cpp`
- `src/DataEngine/cusdr_protocol2_io.cpp`
- `src/DataEngine/cusdr_dataEngine.cpp`
- `src/cusdr_networkWidget.cpp`
- `src/GL/cusdr_oglDisplayPanel.cpp`
- For documentation reference, use:
- `doc/openHPSDR Ethernet Protocol v4.3.docx`
- Highest value next task:
- implement proper `Alex1/BPF2/RX2` handling for Protocol 2 boards that support a second Alex register.

View File

@ -1,7 +1,7 @@
[General]
cudaSDR%20BETA=v0.3.2.13+
cudaSDR%20BETA%20=v0.3.2.13+
saved=\x43f\x43e\x43d\x435\x434\x435\x43b\x44c\x43d\x438\x43a 13 \x430\x43f\x440\x435\x43b\x44f 2026 20:08:53
saved=\x43f\x43e\x43d\x435\x434\x435\x43b\x44c\x43d\x438\x43a 13 \x430\x43f\x440\x435\x43b\x44f 2026 22:06:20
[ChirpWSPR]
chirpAmplitude=75
@ -106,7 +106,7 @@ checkfw=false
excalibur=false
hardware=1
interface=hermes
mercury=true
mercury=false
penelope=false
pennylane=false
@ -114,7 +114,7 @@ pennylane=false
audio_port=15000
hpsdr_local_ipAddress=192.168.122.1
listen_port=11000
metis_port=47257
metis_port=48349
server_ipAddress=192.168.122.1
server_port=52685
socketBufferSize=32
@ -202,7 +202,7 @@ attenuatorGen=off
audioVolume=73
averaging=on
averagingCnt=5
centerFrequency=7096940
centerFrequency=7055083
clickVFO=on
dBmPanScaleMax10cm=-10
dBmPanScaleMax10m=-30
@ -292,7 +292,7 @@ lastCenterFrequency23cm=902000000
lastCenterFrequency2m=144000000
lastCenterFrequency30m=10100000
lastCenterFrequency33cm=902000000
lastCenterFrequency40m=7096940
lastCenterFrequency40m=7055083
lastCenterFrequency5cm=902000000
lastCenterFrequency60m=5260000
lastCenterFrequency630m=472000
@ -314,7 +314,7 @@ lastVfoFrequency23cm=902000000
lastVfoFrequency2m=144000000
lastVfoFrequency30m=10100000
lastVfoFrequency33cm=902000000
lastVfoFrequency40m=7116128
lastVfoFrequency40m=7087913
lastVfoFrequency5cm=902000000
lastVfoFrequency60m=5260000
lastVfoFrequency630m=472000
@ -325,7 +325,7 @@ lastVfoFrequencyGen=22875900
mouseWheelFreqStep=100
panLocked=off
panMode=FILLEDLINE
vfoFrequency=7116128
vfoFrequency=7087913
waterfallMode=ENHANCED
waterfallOffsetHi=20
waterfallOffsetLo=-5

View File

@ -1,3 +1,3 @@
[General]
geometry=@ByteArray(\x1\xd9\xd0\xcb\0\x3\0\0\0\0\0\0\0\0\0\0\0\0\x5@\0\0\x2\xff\0\0\0\0\0\0\0\0\0\0\x5@\0\0\x2\xff\0\0\0\0\0\0\0\0\n\0\0\0\0\0\0\0\0\0\0\0\x5@\0\0\x2\xff)
windowState=@ByteArray(\0\0\0\xff\0\0\0\0\xfd\0\0\0\x1\0\0\0\x1\0\0\0\xf5\0\0\x2<\xfc\x2\0\0\0\x5\xfb\0\0\0\x12\0R\0\x61\0\x64\0i\0o\0\x43\0t\0r\0l\0\0\0\0\0\xff\xff\xff\xff\0\0\0/\0\xff\xff\xff\xfb\0\0\0\x14\0S\0\x65\0r\0v\0\x65\0r\0\x43\0t\0r\0l\0\0\0\0\0\xff\xff\xff\xff\0\0\0/\0\xff\xff\xff\xfb\0\0\0\x12\0H\0P\0S\0\x44\0R\0\x43\0t\0r\0l\0\0\0\0\0\xff\xff\xff\xff\0\0\0/\0\xff\xff\xff\xfb\0\0\0\x12\0\x43\0h\0i\0r\0p\0\x43\0t\0r\0l\0\0\0\0\0\xff\xff\xff\xff\0\0\0/\0\xff\xff\xff\xfb\0\0\0\x16\0\x44\0i\0s\0p\0l\0\x61\0y\0\x43\0t\0r\0l\0\0\0\0\0\xff\xff\xff\xff\0\0\0/\0\xff\xff\xff\0\0\x5\x41\0\0\x2<\0\0\0\x4\0\0\0\x4\0\0\0\b\0\0\0\b\xfc\0\0\0\x9e\0\0\0\x2\0\0\0\0\0\0\0\x2\0\0\0\0\0\0\0\x2\0\0\0\0\0\0\0\x2\0\0\0\0\0\0\0\x2\0\0\0\0\0\0\0\x2\0\0\0\0\0\0\0\x2\0\0\0\0\0\0\0\x2\0\0\0\0\0\0\0\x2\0\0\0\0\0\0\0\x2\0\0\0\0\0\0\0\x2\0\0\0\0\0\0\0\x2\0\0\0\0\0\0\0\x2\0\0\0\0\0\0\0\x2\0\0\0\0\0\0\0\x2\0\0\0\0\0\0\0\x2\0\0\0\0\0\0\0\x2\0\0\0\0\0\0\0\x2\0\0\0\0\0\0\0\x2\0\0\0\0\0\0\0\x2\0\0\0\0\0\0\0\x2\0\0\0\0\0\0\0\x2\0\0\0\0\0\0\0\x2\0\0\0\0\0\0\0\x2\0\0\0\0\0\0\0\x2\0\0\0\0\0\0\0\x2\0\0\0\0\0\0\0\x2\0\0\0\0\0\0\0\x2\0\0\0\0\0\0\0\x2\0\0\0\0\0\0\0\x2\0\0\0\0\0\0\0\x2\0\0\0\0\0\0\0\x2\0\0\0\0\0\0\0\x2\0\0\0\0\0\0\0\x2\0\0\0\0\0\0\0\x2\0\0\0\0\0\0\0\x2\0\0\0\0\0\0\0\x2\0\0\0\0\0\0\0\x2\0\0\0\0\0\0\0\x2\0\0\0\0\0\0\0\x2\0\0\0\0\0\0\0\x2\0\0\0\0\0\0\0\x2\0\0\0\0\0\0\0\x2\0\0\0\0\0\0\0\x2\0\0\0\0\0\0\0\x2\0\0\0\0\0\0\0\x2\0\0\0\0\0\0\0\x2\0\0\0\0\0\0\0\x2\0\0\0\0\0\0\0\x2\0\0\0\0\0\0\0\x2\0\0\0\0\0\0\0\x2\0\0\0\0\0\0\0\x2\0\0\0\0\0\0\0\x2\0\0\0\0\0\0\0\x2\0\0\0\0\0\0\0\x2\0\0\0\0\0\0\0\x2\0\0\0\0\0\0\0\x2\0\0\0\0\0\0\0\x2\0\0\0\0\0\0\0\x2\0\0\0\0\0\0\0\x2\0\0\0\0\0\0\0\x2\0\0\0\0\0\0\0\x2\0\0\0\0\0\0\0\x2\0\0\0\0\0\0\0\x2\0\0\0\0\0\0\0\x2\0\0\0\0\0\0\0\x2\0\0\0\0\0\0\0\x2\0\0\0\0\0\0\0\x2\0\0\0\0\0\0\0\x2\0\0\0\0\0\0\0\x2\0\0\0\0\0\0\0\x2\0\0\0\0\0\0\0\x2\0\0\0\0\0\0\0\x2\0\0\0\0\0\0\0\x2\0\0\0\0\0\0\0\x2\0\0\0\0\0\0\0\x2\0\0\0\0\0\0\0\x2\0\0\0\0\0\0\0\x2\0\0\0\0\0\0\0\x2\0\0\0\0\0\0\0\x2\0\0\0\0\0\0\0\x2\0\0\0\0\0\0\0\x2\0\0\0\0\0\0\0\x2\0\0\0\0\0\0\0\x2\0\0\0\0\0\0\0\x2\0\0\0\0\0\0\0\x2\0\0\0\0\0\0\0\x2\0\0\0\0\0\0\0\x2\0\0\0\0\0\0\0\x2\0\0\0\0\0\0\0\x2\0\0\0\0\0\0\0\x2\0\0\0\0\0\0\0\x2\0\0\0\0\0\0\0\x2\0\0\0\0\0\0\0\x2\0\0\0\0\0\0\0\x2\0\0\0\0\0\0\0\x2\0\0\0\0\0\0\0\x2\0\0\0\0\0\0\0\x2\0\0\0\0\0\0\0\x2\0\0\0\0\0\0\0\x2\0\0\0\0\0\0\0\x2\0\0\0\0\0\0\0\x2\0\0\0\0\0\0\0\x2\0\0\0\0\0\0\0\x2\0\0\0\0\0\0\0\x2\0\0\0\0\0\0\0\x2\0\0\0\0\0\0\0\x2\0\0\0\0\0\0\0\x2\0\0\0\0\0\0\0\x2\0\0\0\0\0\0\0\x2\0\0\0\0\0\0\0\x2\0\0\0\0\0\0\0\x2\0\0\0\0\0\0\0\x2\0\0\0\0\0\0\0\x2\0\0\0\0\0\0\0\x2\0\0\0\0\0\0\0\x2\0\0\0\0\0\0\0\x2\0\0\0\0\0\0\0\x2\0\0\0\0\0\0\0\x2\0\0\0\0\0\0\0\x2\0\0\0\0\0\0\0\x2\0\0\0\0\0\0\0\x2\0\0\0\0\0\0\0\x2\0\0\0\0\0\0\0\x2\0\0\0\0\0\0\0\x2\0\0\0\0\0\0\0\x2\0\0\0\0\0\0\0\x2\0\0\0\0\0\0\0\x2\0\0\0\0\0\0\0\x2\0\0\0\0\0\0\0\x2\0\0\0\0\0\0\0\x2\0\0\0\0\0\0\0\x2\0\0\0\0\0\0\0\x2\0\0\0\0\0\0\0\x2\0\0\0\0\0\0\0\x2\0\0\0\0\0\0\0\x2\0\0\0\0\0\0\0\x2\0\0\0\0\0\0\0\x2\0\0\0\0\0\0\0\x2\0\0\0\0\0\0\0\x2\0\0\0\0\0\0\0\x2\0\0\0\0\0\0\0\x2\0\0\0\0\0\0\0\x2\0\0\0\0\0\0\0\x2\0\0\0\0\0\0\0\x2\0\0\0\0\0\0\0\x2\0\0\0\0\0\0\0\x2\0\0\0\0\0\0\0\x2\0\0\0\0\0\0\0\x2\0\0\0\0\0\0\0\x2\0\0\0\0\0\0\0\x2\0\0\0\0\0\0\0\x2\0\0\0\0\0\0\0\x2\0\0\0\0\0\0\0\x2\0\0\0\0\0\0\0\x2\0\0\0\0\0\0\0\x2\0\0\0\0\0\0\0\x2\0\0\0\x1\0\0\0\x18\0\x44\0i\0s\0p\0l\0\x61\0y\0P\0\x61\0n\0\x65\0l\x1\0\0\0\0\xff\xff\xff\xff\0\0\0\0\0\0\0\0\0\0\0\x2\0\0\0\x1\0\0\0\x16\0M\0\x61\0i\0n\0\x42\0u\0t\0t\0o\0n\0s\x1\0\0\0\0\xff\xff\xff\xff\0\0\0\0\0\0\0\0)
geometry=@ByteArray(\x1\xd9\xd0\xcb\0\x3\0\0\0\0\0\0\0\0\0\0\0\0\x5@\0\0\x3\x1b\0\0\0\0\0\0\0\x1c\0\0\x5@\0\0\x3\x1b\0\0\0\0\0\0\0\0\n\0\0\0\0\0\0\0\0\x1c\0\0\x5@\0\0\x3\x1b)
windowState=@ByteArray(\0\0\0\xff\0\0\0\0\xfd\0\0\0\x1\0\0\0\x1\0\0\0\xf5\0\0\x2<\xfc\x2\0\0\0\x5\xfb\0\0\0\x12\0R\0\x61\0\x64\0i\0o\0\x43\0t\0r\0l\0\0\0\0\0\xff\xff\xff\xff\0\0\0/\0\xff\xff\xff\xfb\0\0\0\x14\0S\0\x65\0r\0v\0\x65\0r\0\x43\0t\0r\0l\0\0\0\0\0\xff\xff\xff\xff\0\0\0/\0\xff\xff\xff\xfb\0\0\0\x12\0H\0P\0S\0\x44\0R\0\x43\0t\0r\0l\0\0\0\0\0\xff\xff\xff\xff\0\0\0/\0\xff\xff\xff\xfb\0\0\0\x12\0\x43\0h\0i\0r\0p\0\x43\0t\0r\0l\0\0\0\0\0\xff\xff\xff\xff\0\0\0/\0\xff\xff\xff\xfb\0\0\0\x16\0\x44\0i\0s\0p\0l\0\x61\0y\0\x43\0t\0r\0l\0\0\0\0\0\xff\xff\xff\xff\0\0\0/\0\xff\xff\xff\0\0\x5\x41\0\0\x2<\0\0\0\x4\0\0\0\x4\0\0\0\b\0\0\0\b\xfc\0\0\0\xa5\0\0\0\x2\0\0\0\0\0\0\0\x2\0\0\0\0\0\0\0\x2\0\0\0\0\0\0\0\x2\0\0\0\0\0\0\0\x2\0\0\0\0\0\0\0\x2\0\0\0\0\0\0\0\x2\0\0\0\0\0\0\0\x2\0\0\0\0\0\0\0\x2\0\0\0\0\0\0\0\x2\0\0\0\0\0\0\0\x2\0\0\0\0\0\0\0\x2\0\0\0\0\0\0\0\x2\0\0\0\0\0\0\0\x2\0\0\0\0\0\0\0\x2\0\0\0\0\0\0\0\x2\0\0\0\0\0\0\0\x2\0\0\0\0\0\0\0\x2\0\0\0\0\0\0\0\x2\0\0\0\0\0\0\0\x2\0\0\0\0\0\0\0\x2\0\0\0\0\0\0\0\x2\0\0\0\0\0\0\0\x2\0\0\0\0\0\0\0\x2\0\0\0\0\0\0\0\x2\0\0\0\0\0\0\0\x2\0\0\0\0\0\0\0\x2\0\0\0\0\0\0\0\x2\0\0\0\0\0\0\0\x2\0\0\0\0\0\0\0\x2\0\0\0\0\0\0\0\x2\0\0\0\0\0\0\0\x2\0\0\0\0\0\0\0\x2\0\0\0\0\0\0\0\x2\0\0\0\0\0\0\0\x2\0\0\0\0\0\0\0\x2\0\0\0\0\0\0\0\x2\0\0\0\0\0\0\0\x2\0\0\0\0\0\0\0\x2\0\0\0\0\0\0\0\x2\0\0\0\0\0\0\0\x2\0\0\0\0\0\0\0\x2\0\0\0\0\0\0\0\x2\0\0\0\0\0\0\0\x2\0\0\0\0\0\0\0\x2\0\0\0\0\0\0\0\x2\0\0\0\0\0\0\0\x2\0\0\0\0\0\0\0\x2\0\0\0\0\0\0\0\x2\0\0\0\0\0\0\0\x2\0\0\0\0\0\0\0\x2\0\0\0\0\0\0\0\x2\0\0\0\0\0\0\0\x2\0\0\0\0\0\0\0\x2\0\0\0\0\0\0\0\x2\0\0\0\0\0\0\0\x2\0\0\0\0\0\0\0\x2\0\0\0\0\0\0\0\x2\0\0\0\0\0\0\0\x2\0\0\0\0\0\0\0\x2\0\0\0\0\0\0\0\x2\0\0\0\0\0\0\0\x2\0\0\0\0\0\0\0\x2\0\0\0\0\0\0\0\x2\0\0\0\0\0\0\0\x2\0\0\0\0\0\0\0\x2\0\0\0\0\0\0\0\x2\0\0\0\0\0\0\0\x2\0\0\0\0\0\0\0\x2\0\0\0\0\0\0\0\x2\0\0\0\0\0\0\0\x2\0\0\0\0\0\0\0\x2\0\0\0\0\0\0\0\x2\0\0\0\0\0\0\0\x2\0\0\0\0\0\0\0\x2\0\0\0\0\0\0\0\x2\0\0\0\0\0\0\0\x2\0\0\0\0\0\0\0\x2\0\0\0\0\0\0\0\x2\0\0\0\0\0\0\0\x2\0\0\0\0\0\0\0\x2\0\0\0\0\0\0\0\x2\0\0\0\0\0\0\0\x2\0\0\0\0\0\0\0\x2\0\0\0\0\0\0\0\x2\0\0\0\0\0\0\0\x2\0\0\0\0\0\0\0\x2\0\0\0\0\0\0\0\x2\0\0\0\0\0\0\0\x2\0\0\0\0\0\0\0\x2\0\0\0\0\0\0\0\x2\0\0\0\0\0\0\0\x2\0\0\0\0\0\0\0\x2\0\0\0\0\0\0\0\x2\0\0\0\0\0\0\0\x2\0\0\0\0\0\0\0\x2\0\0\0\0\0\0\0\x2\0\0\0\0\0\0\0\x2\0\0\0\0\0\0\0\x2\0\0\0\0\0\0\0\x2\0\0\0\0\0\0\0\x2\0\0\0\0\0\0\0\x2\0\0\0\0\0\0\0\x2\0\0\0\0\0\0\0\x2\0\0\0\0\0\0\0\x2\0\0\0\0\0\0\0\x2\0\0\0\0\0\0\0\x2\0\0\0\0\0\0\0\x2\0\0\0\0\0\0\0\x2\0\0\0\0\0\0\0\x2\0\0\0\0\0\0\0\x2\0\0\0\0\0\0\0\x2\0\0\0\0\0\0\0\x2\0\0\0\0\0\0\0\x2\0\0\0\0\0\0\0\x2\0\0\0\0\0\0\0\x2\0\0\0\0\0\0\0\x2\0\0\0\0\0\0\0\x2\0\0\0\0\0\0\0\x2\0\0\0\0\0\0\0\x2\0\0\0\0\0\0\0\x2\0\0\0\0\0\0\0\x2\0\0\0\0\0\0\0\x2\0\0\0\0\0\0\0\x2\0\0\0\0\0\0\0\x2\0\0\0\0\0\0\0\x2\0\0\0\0\0\0\0\x2\0\0\0\0\0\0\0\x2\0\0\0\0\0\0\0\x2\0\0\0\0\0\0\0\x2\0\0\0\0\0\0\0\x2\0\0\0\0\0\0\0\x2\0\0\0\0\0\0\0\x2\0\0\0\0\0\0\0\x2\0\0\0\0\0\0\0\x2\0\0\0\0\0\0\0\x2\0\0\0\0\0\0\0\x2\0\0\0\0\0\0\0\x2\0\0\0\0\0\0\0\x2\0\0\0\0\0\0\0\x2\0\0\0\0\0\0\0\x2\0\0\0\0\0\0\0\x2\0\0\0\0\0\0\0\x2\0\0\0\0\0\0\0\x2\0\0\0\0\0\0\0\x2\0\0\0\0\0\0\0\x2\0\0\0\0\0\0\0\x2\0\0\0\0\0\0\0\x2\0\0\0\0\0\0\0\x2\0\0\0\0\0\0\0\x2\0\0\0\0\0\0\0\x2\0\0\0\0\0\0\0\x2\0\0\0\0\0\0\0\x2\0\0\0\0\0\0\0\x2\0\0\0\0\0\0\0\x2\0\0\0\0\0\0\0\x2\0\0\0\0\0\0\0\x2\0\0\0\0\0\0\0\x2\0\0\0\0\0\0\0\x2\0\0\0\0\0\0\0\x2\0\0\0\0\0\0\0\x2\0\0\0\0\0\0\0\x2\0\0\0\0\0\0\0\x2\0\0\0\0\0\0\0\x2\0\0\0\x1\0\0\0\x18\0\x44\0i\0s\0p\0l\0\x61\0y\0P\0\x61\0n\0\x65\0l\x1\0\0\0\0\xff\xff\xff\xff\0\0\0\0\0\0\0\0\0\0\0\x2\0\0\0\x1\0\0\0\x16\0M\0\x61\0i\0n\0\x42\0u\0t\0t\0o\0n\0s\x1\0\0\0\0\xff\xff\xff\xff\0\0\0\0\0\0\0\0)

File diff suppressed because it is too large Load Diff

View File

@ -26,6 +26,7 @@ HEADERS += \
./src/DataEngine/cusdr_dataIO.h \
./src/DataEngine/cusdr_discoverer_P1.h \
./src/DataEngine/cusdr_discoverer_P2.h \
./src/DataEngine/cusdr_protocol2_profile.h \
./src/DataEngine/cusdr_protocol2_io.h \
./src/DataEngine/cusdr_receiver.h \
./src/DataEngine/soundout.h \
@ -103,6 +104,7 @@ HEADERS += \
./src/DataEngine/cusdr_dataIO.h \
./src/DataEngine/cusdr_discoverer_P1.h \
./src/DataEngine/cusdr_discoverer_P2.h \
./src/DataEngine/cusdr_protocol2_profile.h \
./src/DataEngine/cusdr_protocol2_io.h \
./src/DataEngine/cusdr_receiver.h \
./src/DataEngine/soundout.h \
@ -178,6 +180,7 @@ SOURCES += \
./src/DataEngine/cusdr_dataIO.cpp \
./src/DataEngine/cusdr_discoverer_P1.cpp \
./src/DataEngine/cusdr_discoverer_P2.cpp \
./src/DataEngine/cusdr_protocol2_profile.cpp \
./src/DataEngine/cusdr_protocol2_io.cpp \
./src/DataEngine/cusdr_receiver.cpp \
./src/DataEngine/soundout.cpp \
@ -248,6 +251,7 @@ SOURCES += \
./src/DataEngine/cusdr_dataIO.cpp \
./src/DataEngine/cusdr_discoverer_P1.cpp \
./src/DataEngine/cusdr_discoverer_P2.cpp \
./src/DataEngine/cusdr_protocol2_profile.cpp \
./src/DataEngine/cusdr_protocol2_io.cpp \
./src/DataEngine/cusdr_receiver.cpp \
./src/DataEngine/soundout.cpp \

View File

@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE QtCreatorProject>
<!-- Written by QtCreator 19.0.0, 2026-04-13T20:07:04. -->
<!-- Written by QtCreator 19.0.0, 2026-04-13T20:38:08. -->
<qtcreator>
<data>
<variable>EnvironmentId</variable>

View File

@ -39,6 +39,7 @@
// use WIDEBAND_PROCESSOR_DEBUG
#include "cusdr_dataEngine.h"
#include "cusdr_protocol2_profile.h"
/*!
@ -62,6 +63,45 @@ static int firstTimeRxInit;
static quint8 adc_rx1_4, adc_rx5_8, adc_rx9_16;
static quint8 new_adc_rx1_4, new_adc_rx5_8, new_adc_rx9_16;
namespace {
bool invokeDataIOInitFrame(DataIO *dataIO, int rx) {
if (!dataIO)
return false;
return QMetaObject::invokeMethod(
dataIO,
"sendInitFramesToNetworkDevice",
Qt::BlockingQueuedConnection,
Q_ARG(int, rx));
}
bool invokeDataIOStartStop(DataIO *dataIO, char value) {
if (!dataIO)
return false;
return QMetaObject::invokeMethod(
dataIO,
"networkDeviceStartStop",
Qt::BlockingQueuedConnection,
Q_ARG(char, value));
}
bool invokeDataIOStop(DataIO *dataIO) {
if (!dataIO)
return false;
return QMetaObject::invokeMethod(
dataIO,
"stop",
Qt::BlockingQueuedConnection);
}
}
DataEngine::DataEngine(QObject *parent)
: QObject(parent)
, set(Settings::instance())
@ -509,6 +549,15 @@ bool DataEngine::findHPSDRDevices() {
SleeperThread::msleep(100);
if (set->getCurrentMetisCard().protocolVersion >= 2) {
const TNetworkDevicecard currentDevice = set->getCurrentMetisCard();
set->setHPSDRHardware(1);
set->setMercuryPresence(false);
set->setPenelopePresence(false);
set->setPennyLanePresence(false);
set->setExcaliburPresence(false);
set->setAlexPresence(currentDevice.hasAlex0 || currentDevice.hasAlex1);
if (currentDevice.ddcCount > 0 && set->getNumberOfReceivers() > currentDevice.ddcCount)
set->setReceivers(this, currentDevice.ddcCount);
io.hermesFW = set->getCurrentMetisCard().firmwareVersion;
set->setHermesVersion(io.hermesFW);
return true;
@ -584,12 +633,12 @@ bool DataEngine::getFirmwareVersions() {
//setSampleRate(this, set->getSampleRate());
SleeperThread::msleep(100);
// pre-conditioning
for (int i = 0; i < io.receivers; i++)
m_dataIO->sendInitFramesToNetworkDevice(i);
// pre-conditioning
for (int i = 0; i < io.receivers; i++)
invokeDataIOInitFrame(m_dataIO, i);
if (m_serverMode == QSDR::SDRMode)
m_dataIO->networkDeviceStartStop(0x01); // 0x01 for starting Metis without wide band data
if (m_serverMode == QSDR::SDRMode)
invokeDataIOStartStop(m_dataIO, 0x01); // 0x01 for starting Metis without wide band data
m_networkDeviceRunning = true;
setSystemState(QSDR::NoError, m_hwInterface, m_serverMode, QSDR::DataEngineUp);
@ -992,12 +1041,12 @@ bool DataEngine::start() {
// pre-conditioning
for (int i = 0; i < io.receivers; i++)
m_dataIO->sendInitFramesToNetworkDevice(i);
invokeDataIOInitFrame(m_dataIO, i);
if (m_serverMode == QSDR::SDRMode && set->getWidebandData())
m_dataIO->networkDeviceStartStop(0x03); // 0x03 for starting the device with wide band data
invokeDataIOStartStop(m_dataIO, 0x03); // 0x03 for starting the device with wide band data
else
m_dataIO->networkDeviceStartStop(0x01); // 0x01 for starting the device without wide band data
invokeDataIOStartStop(m_dataIO, 0x01); // 0x01 for starting the device without wide band data
m_networkDeviceRunning = true;
@ -1021,10 +1070,10 @@ void DataEngine::stop() {
// turn time stamping off
setTimeStamp(this, false);
// stop the device
m_dataIO->networkDeviceStartStop(0);
m_networkDeviceRunning = false;
DATA_ENGINE_DEBUG << "HPSDR device stopped";
// stop the device
invokeDataIOStartStop(m_dataIO, 0);
m_networkDeviceRunning = false;
DATA_ENGINE_DEBUG << "HPSDR device stopped";
// stop the threads
//SleeperThread::msleep(100);
@ -1577,7 +1626,7 @@ void DataEngine::stopDataIO() {
if (m_dataIOThread->isRunning()) {
m_dataIO->stop();
invokeDataIOStop(m_dataIO);
m_dataIOThread->quit();
while (!m_dataIOThread->isFinished()) {

View File

@ -109,11 +109,17 @@ DataIO::DataIO(THPSDRParameter *ioData)
DataIO::~DataIO() {
if (m_dataIOSocketOn) {
if (m_dataIOSocketOn && m_dataIOSocket) {
m_dataIOSocket->close();
delete m_dataIOSocket;
m_dataIOSocket = 0;
}
if (m_pSoundCardOut) {
m_pSoundCardOut->Stop();
delete m_pSoundCardOut;
m_pSoundCardOut = 0;
}
}
void DataIO::stop() {
@ -125,11 +131,8 @@ void DataIO::stop() {
if (m_protocol2 && m_protocol2->isActive())
m_protocol2->stop();
if(m_pSoundCardOut) {
if (m_pSoundCardOut)
SleeperThread::msleep(100);
m_pSoundCardOut->Stop();
delete m_pSoundCardOut;
}
}
void DataIO::initDataReceiverSocket() {

View File

@ -228,6 +228,13 @@ int DiscovererP1::findHPSDRDevices() {
mcP1.boardName = str;
mcP1.protocolVersion = 1;
mcP1.firmwareVersion = m_deviceDatagram.at(9) & 0xFF;
mcP1.adcCount = 0;
mcP1.ddcCount = 0;
mcP1.dspClockHz = 0;
mcP1.iqFormatFlags = 0;
mcP1.phaseWords = false;
mcP1.hasAlex0 = false;
mcP1.hasAlex1 = false;
io->networkIOMutex.lock();
DISCOVERER_DEBUG << "Device board ID: " << no;
DISCOVERER_DEBUG << "Device is: " << qPrintable(str);

View File

@ -28,6 +28,7 @@
#define LOG_DISCOVERER
#include "cusdr_discoverer_P2.h"
#include "cusdr_protocol2_profile.h"
#include "Util/cusdr_buttons.h"
@ -44,25 +45,6 @@ DiscovererP2::~DiscovererP2() {
TNetworkDevicecard mcP2;
namespace {
static QString protocol2BoardName(int boardId) {
switch (boardId) {
case 0: return "ATLAS";
case 1: return "Hermes";
case 2: return "Hermes";
case 3: return "Angelia";
case 4: return "Orion";
case 5: return "Orion MkII";
case 6: return "Hermes-Lite";
case 10: return "Saturn";
default: return QString("Protocol2-%1").arg(boardId);
}
}
}
void DiscovererP2::initHPSDRDevice() {
m_searchTime.start();
@ -223,24 +205,40 @@ int DiscovererP2::findHPSDRDevices() {
io->networkIOMutex.unlock();
device = m_deviceDatagram[11] & 0xFF;
QString str = protocol2BoardName(device);
const Protocol2BoardProfile profile = protocol2BoardProfile(device);
QString str = profile.boardName;
mcP2.boardID = device;
mcP2.boardName = str;
mcP2.protocolVersion = m_deviceDatagram[12] & 0xFF;
mcP2.firmwareVersion = m_deviceDatagram[13] & 0xFF;
mcP2.adcCount = profile.adcCount;
mcP2.ddcCount = profile.defaultDdcCount;
mcP2.dspClockHz = profile.dspClockHz;
mcP2.iqFormatFlags = 0;
mcP2.phaseWords = profile.phaseWords;
mcP2.hasAlex0 = profile.hasAlex0;
mcP2.hasAlex1 = profile.hasAlex1;
if (m_deviceDatagram.size() >= 25) {
mcP2.ddcCount = m_deviceDatagram[20] & 0xFF;
mcP2.phaseWords = (m_deviceDatagram[21] & 0xFF) != 0;
mcP2.iqFormatFlags = m_deviceDatagram[22] & 0xFF;
}
io->networkIOMutex.lock();
DISCOVERER_DEBUG << "Protocol 2 device board ID: " << device;
DISCOVERER_DEBUG << "Protocol 2 device is: " << qPrintable(str);
DISCOVERER_DEBUG << "Protocol 2 capabilities: "
<< mcP2.adcCount << " ADC, "
<< mcP2.ddcCount << " DDC, phaseWords="
<< mcP2.phaseWords << ", iqFlags=0x"
<< QString::number(mcP2.iqFormatFlags, 16);
io->networkIOMutex.unlock();
m_deviceCards.append(mcP2);
str += " (";
str += mcP2.ip_address.toString();
str += ")";
set->addNetworkIOComboBoxEntry(str);
set->addNetworkIOComboBoxEntry(protocol2DeviceSummary(mcP2));
devicesFound++;
}
else {

View File

@ -6,6 +6,7 @@
#define LOG_DATAIO
#include "cusdr_protocol2_io.h"
#include "cusdr_protocol2_profile.h"
#ifdef LOG_DATAIO
# define PROTOCOL2_DEBUG qDebug().nospace() << "Protocol2::\t"
@ -25,6 +26,17 @@ static const int kProtocol2GeneralPacketSize = 60;
static const int kProtocol2HighPriorityPacketSize = 1444;
static const int kProtocol2StatusPacketMinSize = 31;
int protocol2ReceiverCount(Settings *set) {
const TNetworkDevicecard device = set->getCurrentMetisCard();
const int requestedReceivers = qMax(1, set->getNumberOfReceivers());
if (device.ddcCount > 0)
return qMin(requestedReceivers, device.ddcCount);
return requestedReceivers;
}
quint32 alexFilterWordForFrequency(Settings *set, long frequency, int currentBand) {
if (!set || !set->getAlexPresence())
@ -128,7 +140,7 @@ bool Protocol2DataPath::initSockets() {
const QHostAddress localAddress(set->getHPSDRDeviceLocalAddr());
const quint16 discoveryPort = set->getMetisPort();
const int receivers = qMax(1, set->getNumberOfReceivers());
const int receivers = protocol2ReceiverCount(set);
m_commandSocket = new QUdpSocket(this);
if (!m_commandSocket->bind(localAddress, discoveryPort, QUdpSocket::DontShareAddress)) {
@ -191,12 +203,13 @@ bool Protocol2DataPath::initSockets() {
void Protocol2DataPath::stop() {
const bool wasRunning = m_running;
m_running = false;
if (m_heartbeatTimer)
m_heartbeatTimer->stop();
if (m_commandSocket)
if (wasRunning && m_commandSocket)
sendHighPriorityPacket(false);
closeSockets();
@ -212,6 +225,9 @@ void Protocol2DataPath::networkDeviceStartStop(char value) {
return;
if (value == 0) {
if (!m_running)
return;
m_running = false;
if (m_heartbeatTimer)
m_heartbeatTimer->stop();
@ -288,11 +304,13 @@ void Protocol2DataPath::sendHeartbeat() {
void Protocol2DataPath::closeSockets() {
if (m_heartbeatTimer) {
disconnect(m_heartbeatTimer, 0, this, 0);
delete m_heartbeatTimer;
m_heartbeatTimer = 0;
}
foreach (QUdpSocket *socket, m_ddcSockets) {
disconnect(socket, 0, this, 0);
socket->close();
delete socket;
}
@ -300,12 +318,14 @@ void Protocol2DataPath::closeSockets() {
m_ddcSampleBuffers.clear();
if (m_statusSocket) {
disconnect(m_statusSocket, 0, this, 0);
m_statusSocket->close();
delete m_statusSocket;
m_statusSocket = 0;
}
if (m_commandSocket) {
disconnect(m_commandSocket, 0, this, 0);
m_commandSocket->close();
delete m_commandSocket;
m_commandSocket = 0;
@ -343,7 +363,21 @@ void Protocol2DataPath::sendGeneralPacket() {
datagram[26] = 0x10;
datagram[27] = 20;
datagram[28] = 32;
datagram[59] = set->getAlexPresence() ? 0x01 : 0x00;
const TNetworkDevicecard device = set->getCurrentMetisCard();
if (device.phaseWords)
datagram[37] = (char)((quint8)datagram.at(37) | 0x08);
if ((device.iqFormatFlags & 0x05) == 0x05)
datagram[39] = 0x05;
if (device.boardID == 0)
datagram[56] = qBound(0, protocol2ReceiverCount(set) - 1, 3);
if (device.hasAlex0)
datagram[59] = (char)((quint8)datagram.at(59) | 0x01);
if (device.hasAlex1)
datagram[59] = (char)((quint8)datagram.at(59) | 0x02);
if (m_commandSocket->writeDatagram(datagram, set->getCurrentMetisCard().ip_address, DEVICE_PORT) >= 0)
++m_generalSequence;
@ -353,7 +387,8 @@ void Protocol2DataPath::sendGeneralPacket() {
void Protocol2DataPath::sendDdcSpecificPacket() {
const int receivers = qMax(1, set->getNumberOfReceivers());
const TNetworkDevicecard device = set->getCurrentMetisCard();
const int receivers = protocol2ReceiverCount(set);
QByteArray datagram(17 + receivers * 6, 0x00);
datagram[0] = (m_ddcSequence >> 24) & 0xFF;
@ -361,15 +396,22 @@ void Protocol2DataPath::sendDdcSpecificPacket() {
datagram[2] = (m_ddcSequence >> 8) & 0xFF;
datagram[3] = m_ddcSequence & 0xFF;
datagram[4] = 0x01;
datagram[5] = io->ccTx.dither ? 0x01 : 0x00;
datagram[6] = io->ccTx.random ? 0x01 : 0x00;
const int adcCount = qMax(1, device.adcCount);
datagram[4] = adcCount;
for (int adc = 0; adc < adcCount && adc < 8; ++adc) {
if (io->ccTx.dither)
datagram[5] = datagram.at(5) | (1 << adc);
if (io->ccTx.random)
datagram[6] = datagram.at(6) | (1 << adc);
}
for (int rx = 0; rx < receivers; ++rx) {
datagram[7 + rx / 8] = datagram.at(7 + rx / 8) | (1 << (rx % 8));
const int offset = 17 + rx * 6;
datagram[offset + 0] = 0x00;
datagram[offset + 0] = (adcCount > 1) ? (rx % adcCount) : 0x00;
datagram[offset + 1] = (io->samplerate >> 8) & 0xFF;
datagram[offset + 2] = io->samplerate & 0xFF;
datagram[offset + 3] = 0x00;
@ -387,7 +429,7 @@ void Protocol2DataPath::sendHighPriorityPacket(bool run) {
QByteArray datagram(kProtocol2HighPriorityPacketSize, 0x00);
const QList<long> frequencies = set->getCtrFrequencies();
const int receivers = qMax(1, set->getNumberOfReceivers());
const int receivers = protocol2ReceiverCount(set);
const int currentReceiver = set->getCurrentReceiver();
const int currentBand = (int)set->getCurrentHamBand(currentReceiver);
const long currentVfoFrequency = set->getVfoFrequency(currentReceiver);
@ -504,7 +546,13 @@ int Protocol2DataPath::samplesPerLegacyHalfFrame() const {
}
quint32 Protocol2DataPath::encodeFrequency(long frequency) const {
return (quint32)frequency;
const TNetworkDevicecard device = set->getCurrentMetisCard();
if (!device.phaseWords)
return (quint32)frequency;
const quint64 dspClock = (device.dspClockHz > 0) ? (quint64)device.dspClockHz : 122880000ULL;
return (quint32)(((quint64)(quint32)frequency << 32) / dspClock);
}
qint32 Protocol2DataPath::read24BitSample(const char *data) const {

View File

@ -0,0 +1,140 @@
/**
* @file cusdr_protocol2_profile.cpp
* @brief openHPSDR Protocol 2 board capability helpers
*/
#include "cusdr_protocol2_profile.h"
namespace {
static Protocol2BoardProfile makeDefaultProfile(int boardId) {
Protocol2BoardProfile profile;
profile.boardId = boardId;
profile.boardName = "Protocol2 SDR";
profile.adcCount = 1;
profile.defaultDdcCount = 2;
profile.dspClockHz = 122880000;
profile.phaseWords = true;
profile.hasAlex0 = true;
profile.hasAlex1 = false;
return profile;
}
}
Protocol2BoardProfile protocol2BoardProfile(int boardId) {
Protocol2BoardProfile profile = makeDefaultProfile(boardId);
switch (boardId) {
case 0:
profile.boardName = "ATLAS";
profile.adcCount = 4;
profile.defaultDdcCount = 4;
profile.dspClockHz = 122880000;
profile.phaseWords = false;
profile.hasAlex0 = true;
profile.hasAlex1 = false;
break;
case 1:
profile.boardName = "Hermes / ANAN-10/100";
profile.adcCount = 1;
profile.defaultDdcCount = 2;
profile.dspClockHz = 122880000;
profile.phaseWords = true;
profile.hasAlex0 = true;
profile.hasAlex1 = false;
break;
case 2:
profile.boardName = "Hermes / ANAN-10E/100B";
profile.adcCount = 1;
profile.defaultDdcCount = 2;
profile.dspClockHz = 122880000;
profile.phaseWords = true;
profile.hasAlex0 = false;
profile.hasAlex1 = false;
break;
case 3:
profile.boardName = "Angelia / ANAN-100D";
profile.adcCount = 2;
profile.defaultDdcCount = 4;
profile.dspClockHz = 122880000;
profile.phaseWords = true;
profile.hasAlex0 = true;
profile.hasAlex1 = false;
break;
case 4:
profile.boardName = "Orion / ANAN-200D";
profile.adcCount = 2;
profile.defaultDdcCount = 4;
profile.dspClockHz = 122880000;
profile.phaseWords = true;
profile.hasAlex0 = true;
profile.hasAlex1 = false;
break;
case 5:
profile.boardName = "Orion MkII / ANAN-7/8000DLE";
profile.adcCount = 2;
profile.defaultDdcCount = 4;
profile.dspClockHz = 122880000;
profile.phaseWords = true;
profile.hasAlex0 = true;
profile.hasAlex1 = true;
break;
case 6:
profile.boardName = "Hermes Lite";
profile.adcCount = 1;
profile.defaultDdcCount = 2;
profile.dspClockHz = 76800000;
profile.phaseWords = true;
profile.hasAlex0 = false;
profile.hasAlex1 = false;
break;
case 10:
profile.boardName = "Saturn / ANAN-G2";
profile.adcCount = 2;
profile.defaultDdcCount = 4;
profile.dspClockHz = 122880000;
profile.phaseWords = true;
profile.hasAlex0 = true;
profile.hasAlex1 = true;
break;
default:
break;
}
return profile;
}
QString protocol2DeviceSummary(const TNetworkDevicecard &device) {
QString summary = device.boardName;
if (device.protocolVersion > 0)
summary += QString(" P%1").arg(device.protocolVersion);
if (device.firmwareVersion > 0)
summary += QString(" FW %1.%2").arg(device.firmwareVersion / 10).arg(device.firmwareVersion % 10);
if (device.adcCount > 0 || device.ddcCount > 0)
summary += QString(" %1ADC/%2DDC").arg(device.adcCount).arg(device.ddcCount);
if (device.phaseWords)
summary += " phase";
if (device.hasAlex0 || device.hasAlex1)
summary += device.hasAlex1 ? " Alex0/1" : " Alex";
summary += QString(" (%1)").arg(device.ip_address.toString());
return summary;
}

View File

@ -0,0 +1,26 @@
/**
* @file cusdr_protocol2_profile.h
* @brief openHPSDR Protocol 2 board capability helpers
*/
#ifndef _CUSDR_PROTOCOL2_PROFILE_H
#define _CUSDR_PROTOCOL2_PROFILE_H
#include "cusdr_settings.h"
struct Protocol2BoardProfile {
int boardId;
const char* boardName;
int adcCount;
int defaultDdcCount;
int dspClockHz;
bool phaseWords;
bool hasAlex0;
bool hasAlex1;
};
Protocol2BoardProfile protocol2BoardProfile(int boardId);
QString protocol2DeviceSummary(const TNetworkDevicecard &device);
#endif // _CUSDR_PROTOCOL2_PROFILE_H

View File

@ -118,6 +118,7 @@ OGLDisplayPanel::OGLDisplayPanel(QWidget *parent)
setupConnections();
setupTextstrings();
setCurrentNetworkDevice(set->getCurrentMetisCard());
set10mhzSource(this, set->get10MHzSource());
set122_88mhzSource(this, set->get122_8MHzSource());
@ -281,6 +282,12 @@ void OGLDisplayPanel::setupConnections() {
this,
SLOT(setHermesVersion(int)));
CHECKED_CONNECT(
set,
SIGNAL(hpsdrNetworkDeviceChanged(TNetworkDevicecard)),
this,
SLOT(setCurrentNetworkDevice(TNetworkDevicecard)));
CHECKED_CONNECT(
set,
SIGNAL(alexPresenceChanged(bool)),
@ -2745,6 +2752,20 @@ void OGLDisplayPanel::setMetisVersion(int value) {
}
void OGLDisplayPanel::setCurrentNetworkDevice(TNetworkDevicecard card) {
if (card.protocolVersion >= 2 && !card.boardName.isEmpty()) {
m_hermesString = card.boardName + " ";
m_hermesStringWidth = m_oglTextSmall->fontMetrics().width(m_hermesString);
}
else {
m_hermesString = QString("Hermes ");
m_hermesStringWidth = m_oglTextSmall->fontMetrics().width(m_hermesString);
}
update();
}
void OGLDisplayPanel::setExcaliburVersion(QObject *sender, int value) {
Q_UNUSED (sender)

View File

@ -324,6 +324,7 @@ private slots:
void setPenelopeVersion(int value);
void setPennylaneVersion(int value);
void setMetisVersion(int value);
void setCurrentNetworkDevice(TNetworkDevicecard card);
void setExcaliburVersion(QObject *sender, int value);
void setAlexVersion(QObject *sender, int value);

View File

@ -36,6 +36,7 @@
#include <QBoxLayout>
#include "cusdr_networkWidget.h"
#include "DataEngine/cusdr_protocol2_profile.h"
#define btn_height 15
@ -502,7 +503,10 @@ void NetworkWidget::setNetworkDeviceList(QList<TNetworkDevicecard> list) {
networkDeviceIPAdresses->clear();
foreach (TNetworkDevicecard device, list) {
networkDeviceIPAdresses->addItem(device.ip_address.toString());
if (device.protocolVersion >= 2)
networkDeviceIPAdresses->addItem(protocol2DeviceSummary(device));
else
networkDeviceIPAdresses->addItem(QString("%1 (%2)").arg(device.boardName).arg(device.ip_address.toString()));
networkDeviceIPAdresses->update();
}
}

View File

@ -485,6 +485,13 @@ typedef struct _networkDeviceCard {
QString boardName;
int protocolVersion;
int firmwareVersion;
int adcCount;
int ddcCount;
int dspClockHz;
int iqFormatFlags;
bool phaseWords;
bool hasAlex0;
bool hasAlex1;
} TNetworkDevicecard;

29
cudaSDR.log Normal file
View File

@ -0,0 +1,29 @@
пн апр. 13 21:24:14 2026: ************************************************************************
пн апр. 13 21:24:14 2026: Settings:: start at: пн апр. 13 21:24:14 2026
пн апр. 13 21:24:14 2026: cudaSDR BETA
пн апр. 13 21:24:15 2026: Settings:: Alex config: 4098
пн апр. 13 21:24:15 2026: Settings:: reading done.
пн апр. 13 21:24:15 2026: Init:: OpenGL found.
пн апр. 13 21:24:15 2026: Init:: Framebuffer Objects found.
пн апр. 13 21:24:15 2026: Init:: main window setup ...
пн апр. 13 21:24:16 2026: MainWindow:: main window init done
пн апр. 13 21:24:16 2026: MainWindow:: server ip from ini-file: "192.168.122.1"
пн апр. 13 21:24:16 2026: MainWindow:: HPSDR device local ip from ini-file: "192.168.122.1"
пн апр. 13 21:24:16 2026: MainWindow:: network interface "bridge0" :
пн апр. 13 21:24:16 2026: MainWindow:: -> broadcast address: 172.16.2.255
пн апр. 13 21:24:16 2026: MainWindow:: -> ip address: 172.16.2.99
пн апр. 13 21:24:16 2026: MainWindow:: network interface "virbr0" :
пн апр. 13 21:24:16 2026: MainWindow:: -> broadcast address: 192.168.122.255
пн апр. 13 21:24:16 2026: MainWindow:: -> ip address: 192.168.122.1
пн апр. 13 21:24:16 2026: MainWindow:: found 2 valid ip addresses.
пн апр. 13 21:24:16 2026: MainWindow:: HPSDR network device interface set to: "virbr0"("192.168.122.1")
пн апр. 13 21:24:16 2026: MainWindow:: HPSDR network device interface set to: "virbr0"("192.168.122.1")
пн апр. 13 21:24:16 2026: MainWindow:: using ip address 192.168.122.1 for the server.
пн апр. 13 21:24:16 2026: MainWindow:: using ip address 192.168.122.1 for connecting to a HPSDR device.
пн апр. 13 21:24:16 2026: Init:: main window setup done.
пн апр. 13 21:24:16 2026: Init:: current path: /home/vladimir/Документы/source/cudaSDR
пн апр. 13 21:24:16 2026: Init:: fftw_wisdom does not exist - planning FFT...
пн апр. 13 21:24:22 2026: planning FFT size 64 ...
пн апр. 13 21:24:23 2026: planning FFT size 128 ...
пн апр. 13 21:24:23 2026: planning FFT size 256 ...
пн апр. 13 21:24:24 2026: planning FFT size 512 ...