support protocol2

This commit is contained in:
2026-07-26 14:57:44 +03:00
parent 6ca18411cf
commit ec4af751cd
27 changed files with 7099 additions and 6108 deletions
-82
View File
@@ -1,82 +0,0 @@
# Next Steps
## Priority
1. Investigate Protocol 2 stop handling.
2. Validate Protocol 2 wideband behavior on real hardware.
3. Complete Protocol 2 Alex support for boards with `Alex1`.
## 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
### Protocol 2 wideband runtime verification
- Re-test Protocol 2 wideband on real hardware after the 2026-04-14 integration.
- Verify that pressing the UI `Wideband` button now really causes the transceiver to:
- start sending wideband UDP data,
- stop sending wideband UDP data.
- Verify that this works while the device is already running, without requiring a full stop/start cycle.
- Verify the incoming packet format and pacing match what the implementation now assumes:
- UDP base port `1027`,
- sequence number in bytes `0..3`,
- 16-bit raw ADC samples starting at byte `4`,
- one full spectrum block formed from `Settings::getWidebandBuffers()` packets.
- Pay special attention to boards that may not follow the default `32 packets per frame` behavior.
- If a board reports or requires fixed wideband constraints, use the protocol document/device profile to specialize the current defaults.
- 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.
- Confirm the `Wideband` button behavior is now correct from the user point of view:
- button state,
- dock visibility,
- `widebandData` setting,
- real hardware stream enable/disable.
- 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`
-179
View File
@@ -1,179 +0,0 @@
# Protocol 2 Receiver/DDC Notes from Thetis
Date: 2026-04-14
This note captures findings from two sources:
- local protocol documentation: `doc/openHPSDR Ethernet Protocol v4.3.docx`
- local Thetis source tree: `/home/vladimir/Документы/source/Thetis`
It is intended as a working note for future Protocol 2 receiver work, especially for the second receiver and Orion MkII / ANAN-7000DLE / ANAN-8000DLE behavior.
## High-level conclusion
Protocol 2 does not impose a global "only 2 receivers" limit.
The protocol separates:
- number of ADCs
- number of DDCs
This strongly suggests that DDCs are logical/digital receive channels built on top of available ADC inputs, not a one-to-one count of independent analog hardware chains.
## Protocol document findings
From `openHPSDR Ethernet Protocol v4.3.docx`:
- The protocol tries not to restrict the number of configurable resources.
- Current hardware implementation applies only a broad upper limit of `80 DDCs` and `8 ADCs`.
- Discovery reply includes `Number of DDCs implemented`, so the hardware reports its own DDC capability.
Board capability appendix findings already extracted:
- `Board 1 = Hermes (ANAN-10, 100)`
- `Number of ADCs = 1`
- `Number of DDCs supported = 2`
- `Board 2 = Hermes (ANAN-10E)`
- `Number of ADCs = 1`
- `Number of DDCs supported = 2`
- `Board 3 = Angelia (ANAN-100D)`
- `Number of ADCs = 2`
- `Number of DDCs supported = 7`
- `Board 4 = Orion (ANAN-200D)`
- `Number of ADCs = 2`
- `Number of DDCs supported = 7`
Implication:
- low-end boards really are 2-DDC devices
- higher-end 2-ADC boards support more logical DDC channels than ADC count
## Thetis findings
Relevant files:
- [network.h](/home/vladimir/Документы/source/Thetis/Project Files/Source/ChannelMaster/network.h:414)
- [console.cs](/home/vladimir/Документы/source/Thetis/Project Files/Source/Console/console.cs:8186)
- [networkproto1.c](/home/vladimir/Документы/source/Thetis/Project Files/Source/ChannelMaster/networkproto1.c:497)
### Model grouping
Thetis groups these as the same high-end hardware family for receiver/DDC logic:
- `ANAN-100D`
- `ANAN-200D`
- `ORIONMKII`
- `ANAN7000D`
- `ANAN8000D`
- `ANAN-G2`
- `ANVELINAPRO3`
See:
- [console.cs](/home/vladimir/Документы/source/Thetis/Project Files/Source/Console/console.cs:8211)
### Protocol 1 interpretation in Thetis
For that high-end family, Thetis explicitly sets:
- `P1_rxcount = 5`
- `nddc = 5`
See:
- [console.cs](/home/vladimir/Документы/source/Thetis/Project Files/Source/Console/console.cs:8219)
This means Thetis does not treat `ANAN-7000DLE/8000DLE` as a 2-DDC class device.
`networkproto1.c` also contains special handling for `nddc == 5`, again separating these boards from 2-DDC hardware:
- [networkproto1.c](/home/vladimir/Документы/source/Thetis/Project Files/Source/ChannelMaster/networkproto1.c:504)
- [networkproto1.c](/home/vladimir/Документы/source/Thetis/Project Files/Source/ChannelMaster/networkproto1.c:528)
### Protocol 2 interpretation in Thetis
For Protocol 2, Thetis groups:
- `Angelia`
- `Orion`
- `OrionMKII`
- `Saturn`
into one receiver/DDC mapping block:
- [console.cs](/home/vladimir/Документы/source/Thetis/Project Files/Source/Console/console.cs:8556)
In the normal non-diversity, non-sync mapping, Thetis uses:
- `rx1 = 2`
- `rx2 = 3`
See:
- [console.cs](/home/vladimir/Документы/source/Thetis/Project Files/Source/Console/console.cs:8562)
This is important:
- for high-end Protocol 2 boards, Thetis does not map main receivers onto `DDC0` and `DDC1`
- it maps them onto `DDC2` and `DDC3`
### Frequency map evidence
Thetis has explicit frequency mapping entries up to at least `RX7`:
- [network.c](/home/vladimir/Документы/source/Thetis/Project Files/Source/ChannelMaster/network.c:947)
- [network.c](/home/vladimir/Документы/source/Thetis/Project Files/Source/ChannelMaster/network.c:977)
And sequence/error tracking exists up to `DDC6`:
- [netInterface.c](/home/vladimir/Документы/source/Thetis/Project Files/Source/ChannelMaster/netInterface.c:173)
- [netInterface.c](/home/vladimir/Документы/source/Thetis/Project Files/Source/ChannelMaster/netInterface.c:185)
This again supports the idea that these are logical digital channels, not simply "one DDC per physical receiver front-end".
## Working interpretation for Orion MkII / ANAN-7000DLE / ANAN-8000DLE
Most likely model:
- there are `2` hardware ADC inputs
- there are multiple logical DDC channels built on top of those ADC inputs
- wideband is a separate stream, not "DDC1"
- `DDC0/DDC1` are likely reserved or preferred for sync/diversity/PureSignal related modes on high-end boards
- normal user RX channels on high-end Protocol 2 boards are likely expected on `DDC2` and `DDC3`
This is consistent with:
- protocol appendix: `2 ADC` and more-than-2 supported DDC on high-end hardware
- Thetis P2 receiver mapping
- Thetis P1 special-case handling for `nddc == 5`
## Important caution
One user assertion remains plausible and should not be discarded:
- Orion MkII may have only `2` hardware receive chains in the analog sense
That does not contradict the protocol or Thetis findings.
It would simply mean:
- `2` hardware ADC/analog paths
- multiple digital DDC slices derived from those ADCs
So the likely correction is not "two hardware DDC total", but rather:
- "two hardware ADC/front-end paths, several logical DDC channels"
## Practical implication for cudaSDR Protocol 2 work
For future receiver work, do not assume:
- high-end Protocol 2 boards use the same DDC mapping as Hermes / HermesII
- receiver 1 and receiver 2 should be attached to `DDC0` and `DDC1`
Instead, verify whether our Protocol 2 implementation should follow Thetis-style mapping for high-end boards:
- RX1 -> `DDC2`
- RX2 -> `DDC3`
And keep wideband separate from DDC numbering.
-262
View File
@@ -1,262 +0,0 @@
# 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.
## Additional Fixes On 2026-04-14
### 1. Protocol 2 control-timing corrections
- Re-read the local protocol document and verified the command timing requirements again:
- `High Priority` / C&C must be sent at least once per second,
- `100 ms` is the recommended periodic rate,
- packets should also be sent when relevant data changes.
- Updated `src/DataEngine/cusdr_protocol2_io.cpp` so Protocol 2 now reacts immediately to settings changes instead of relying only on the `100 ms` heartbeat:
- center/VFO frequency changes,
- current receiver changes,
- band changes,
- Alex config/state changes,
- sample-rate changes,
- DDC dither/random changes,
- number-of-receivers changes.
- `General`, `DDC Specific`, and `High Priority` packets are now refreshed according to the type of change, and DDC sockets are rebuilt if the active RX count changes.
### 2. Protocol 2 wideband support integrated
- Re-read the `Wideband` sections of `doc/openHPSDR Ethernet Protocol v4.3.docx`.
- Implemented Protocol 2 wideband receive support in:
- `src/DataEngine/cusdr_protocol2_io.h`
- `src/DataEngine/cusdr_protocol2_io.cpp`
- Added Protocol 2 wideband socket handling:
- bind UDP wideband receive socket on base port `1027`,
- receive raw ADC wideband packets,
- track per-port sequence numbers,
- reassemble a full wideband block into `io->wb_queue` for the existing wideband DSP/display pipeline.
- Updated Protocol 2 `General packet` wideband fields from the document:
- bytes `21..22`: wideband base port `1027`,
- byte `23`: enable bit for ADC0 wideband,
- bytes `24..25`: `512` samples per packet,
- byte `26`: `16`-bit samples,
- byte `27`: `20 ms` update rate,
- byte `28`: packets per frame from `Settings::getWidebandBuffers()`.
- Added live handling for `widebandDataChanged(...)` so Protocol 2 can enable/disable wideband by resending the `General packet` while running.
- When wideband is disabled, the partial Protocol 2 wideband accumulator is cleared and the wideband spectrum display is reset.
### 3. UI wideband button now controls the actual stream
- Investigated the existing UI and found the `Wideband` button only showed/hid the dock widget.
- It did **not** previously toggle the actual hardware wideband data stream.
- Updated `src/cusdr_mainWidget.cpp` so the `Wideband` button now:
- sets `widebandData`,
- sets `widebandStatus`,
- shows/hides the dock.
- Updated dock visibility handling so manual close/open remains synchronized with both UI state and data-stream state.
- Also removed the old behavior where the button stayed disabled merely because `widebandData` was off in the saved settings.
### 4. Protocol 1 live wideband toggle aligned with UI
- Updated `src/DataEngine/cusdr_dataEngine.h` and `src/DataEngine/cusdr_dataEngine.cpp` so `widebandDataChanged(...)` now reissues the legacy start command while the data engine is already running:
- `0x03` with wideband,
- `0x01` without wideband.
- Protocol 2 keeps its own separate live wideband handling inside `Protocol2DataPath`, so the legacy restart command is skipped for Protocol 2 devices.
### 5. Build re-check
- Rebuilt after the wideband work:
- `make -j4`
- success
### Current status after wideband integration
- Protocol 2 now has:
- periodic and change-driven command refresh,
- integrated wideband receive path,
- UI-triggered wideband enable/disable behavior.
- This is compile-verified.
- Runtime validation on real Protocol 2 hardware is still required for:
- actual wideband packet cadence,
- live on/off switching while running,
- board-specific wideband constraints such as fixed packets-per-frame behavior on some hardware.
-108
View File
@@ -1,108 +0,0 @@
# Protocol 2 Worklog - 2026-04-14
## Что подтверждено
- Устройство корректно находится по Protocol 2.
- Для `Orion MkII / boardID 5` рабочий RX действительно идёт через `DDC2`, то есть поток приходит с `UDP 1037`.
- Реальное железо шлёт обратный трафик не на отдельные локальные порты `1025/1037/...`, а на локальный ephemeral-порт сессии.
- Поэтому схема с отдельными `QUdpSocket` для status/DDC не работала на реальном трансивере, хотя по tcpdump трафик был виден.
- После перехода на приём через общий сокет входящие IQ начали реально доходить:
- `dgrams > 0`
- `samples > 0`
- `iqFrames > 0`
- `iq_queue` и упаковка в legacy-кадр уже не являются первичным блокером: спектр/водопад появляются хотя бы кратковременно.
## Что уже исправлено в коде
### Protocol 2 mapping и пакеты
- Добавлен high-end mapping `RX1 -> DDC2`, `RX2 -> DDC3` для `boardID 3/4/5/10`.
- `High Priority` пишет RX частоты в реальные DDC-слоты.
- Добавлена запись `DUC frequency` в `HP bytes 329..332`.
- `DDC Specific` отправляется полным пакетом `1444` байта.
- Sample rate в `DDC Specific` передаётся в `ksps`, а не в `Hz`.
- В `General Packet` исправлены:
- `DUC Specific port = 1026`
- `Mic data port = 1026`
- `Hardware watchdog enable`
- `PA enable`
- Добавлен минимальный `DUC/TX Specific` пакет.
### Сокеты и входящий трафик
- Исходно была реализована схема с отдельными UDP-сокетами под `1025`, `1037` и т.д.
- По tcpdump выяснилось, что реальный Orion MkII шлёт status/DDC/mic обратно на локальный порт командного сокета.
- После этого реализован приём как в `pihpsdr`:
- один основной сокет с локальным session-port
- demux по `source port`
- `1025 -> status`
- `1026 -> mic`
- `1035+N -> DDC IQ`
- `1027 -> wideband`
### Диагностика
- Добавлены счётчики:
- `dgrams`
- `samples`
- `iqFrames`
- `hpTx`
- `hpRx`
- Добавлен периодический лог состояния RX.
## Что показывают последние логи
- `hpTx` продолжает расти.
- `hpRx` продолжает расти.
- Значит:
- общий UDP сокет работает
- `QSocket/readyRead()` не является основным подозреваемым
- status-трафик живой
- Но `dgrams` по DDC останавливаются на фиксированном числе.
- Это означает, что после короткого времени именно радио перестаёт слать `DDC IQ`, при том что control/status канал остаётся жив.
## Что уже проверено как гипотезы
- Только `HP` в heartbeat.
- `HP` каждые `50 ms`.
- Только `HP + DDC Specific`.
- Цикл, близкий к `pihpsdr`:
- `HP` каждые `100 ms`
- `TX Specific` / `RX Specific` по очереди
- `General` раз в `800 ms`
Поведение меняется, но DDC поток всё равно обрывается.
## Что нужно сделать следующим
Нужен лог с разбивкой по входящим `source port`, чтобы зафиксировать точную картину после остановки RX потока.
Ожидаемый лог теперь имеет вид:
```text
Protocol2:: rx flow: ... ports[1025=..., 1026=..., 1037=...]
```
### Критичный вопрос
Что происходит после остановки `dgrams`:
- если растут только `1025/1026`, а `1037` стоит:
- радио отключает именно DDC поток, при том что session/status живы;
- если `1037` растёт, а `dgrams` нет:
- ошибка в нашем demux/parse;
- если появляется другой неожиданный порт:
- нужно смотреть новую маршрутизацию/режим потока.
## Главный вывод на текущий момент
Проблема уже не выглядит как:
- ошибка DSP,
- пустой `iq_queue`,
- неверные номера портов в `General`,
- мёртвый `QSocket`.
Основной текущий подозреваемый:
- содержимое или cadence одного из Protocol 2 control-пакетов всё ещё не совпадает с тем, что ожидает реальный `Orion MkII`, и из-за этого радио через короткое время прекращает выдачу `DDC IQ`, хотя status-канал продолжает жить.
+1690 -1693
View File
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
+2 -2
View File
@@ -1,3 +1,3 @@
[General] [General]
geometry=@ByteArray(\x1\xd9\xd0\xcb\0\x3\0\0\0\0\x1\xf9\0\0\0J\0\0\a9\0\0\x3\x65\0\0\x1\xf9\0\0\0\x66\0\0\a9\0\0\x3\x65\0\0\0\0\0\0\0\0\n\0\0\0\x1\xf9\0\0\0\x66\0\0\a9\0\0\x3\x65) geometry=@ByteArray(\x1\xd9\xd0\xcb\0\x3\0\0\0\0\x5\x36\0\0\0\x37\0\0\t\xb2\0\0\x2\xaa\0\0\x5\x36\0\0\0S\0\0\t\xb2\0\0\x2\xaa\0\0\0\0\0\0\0\0\n\0\0\0\x5\x36\0\0\0S\0\0\t\xb2\0\0\x2\xaa)
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\xe1\0\0\0\x2\0\0\0\0\0\0\0\x2\0\0\0\0\0\0\0\x2\0\0\0\0\0\0\0\x2\0\0\0\0\0\0\0\x2\0\0\0\0\0\0\0\x2\0\0\0\0\0\0\0\x2\0\0\0\0\0\0\0\x2\0\0\0\0\0\0\0\x2\0\0\0\0\0\0\0\x2\0\0\0\0\0\0\0\x2\0\0\0\0\0\0\0\x2\0\0\0\0\0\0\0\x2\0\0\0\0\0\0\0\x2\0\0\0\0\0\0\0\x2\0\0\0\0\0\0\0\x2\0\0\0\0\0\0\0\x2\0\0\0\0\0\0\0\x2\0\0\0\0\0\0\0\x2\0\0\0\0\0\0\0\x2\0\0\0\0\0\0\0\x2\0\0\0\0\0\0\0\x2\0\0\0\0\0\0\0\x2\0\0\0\0\0\0\0\x2\0\0\0\0\0\0\0\x2\0\0\0\0\0\0\0\x2\0\0\0\0\0\0\0\x2\0\0\0\0\0\0\0\x2\0\0\0\0\0\0\0\x2\0\0\0\0\0\0\0\x2\0\0\0\0\0\0\0\x2\0\0\0\0\0\0\0\x2\0\0\0\0\0\0\0\x2\0\0\0\0\0\0\0\x2\0\0\0\0\0\0\0\x2\0\0\0\0\0\0\0\x2\0\0\0\0\0\0\0\x2\0\0\0\0\0\0\0\x2\0\0\0\0\0\0\0\x2\0\0\0\0\0\0\0\x2\0\0\0\0\0\0\0\x2\0\0\0\0\0\0\0\x2\0\0\0\0\0\0\0\x2\0\0\0\0\0\0\0\x2\0\0\0\0\0\0\0\x2\0\0\0\0\0\0\0\x2\0\0\0\0\0\0\0\x2\0\0\0\0\0\0\0\x2\0\0\0\0\0\0\0\x2\0\0\0\0\0\0\0\x2\0\0\0\0\0\0\0\x2\0\0\0\0\0\0\0\x2\0\0\0\0\0\0\0\x2\0\0\0\0\0\0\0\x2\0\0\0\0\0\0\0\x2\0\0\0\0\0\0\0\x2\0\0\0\0\0\0\0\x2\0\0\0\0\0\0\0\x2\0\0\0\0\0\0\0\x2\0\0\0\0\0\0\0\x2\0\0\0\0\0\0\0\x2\0\0\0\0\0\0\0\x2\0\0\0\0\0\0\0\x2\0\0\0\0\0\0\0\x2\0\0\0\0\0\0\0\x2\0\0\0\0\0\0\0\x2\0\0\0\0\0\0\0\x2\0\0\0\0\0\0\0\x2\0\0\0\0\0\0\0\x2\0\0\0\0\0\0\0\x2\0\0\0\0\0\0\0\x2\0\0\0\0\0\0\0\x2\0\0\0\0\0\0\0\x2\0\0\0\0\0\0\0\x2\0\0\0\0\0\0\0\x2\0\0\0\0\0\0\0\x2\0\0\0\0\0\0\0\x2\0\0\0\0\0\0\0\x2\0\0\0\0\0\0\0\x2\0\0\0\0\0\0\0\x2\0\0\0\0\0\0\0\x2\0\0\0\0\0\0\0\x2\0\0\0\0\0\0\0\x2\0\0\0\0\0\0\0\x2\0\0\0\0\0\0\0\x2\0\0\0\0\0\0\0\x2\0\0\0\0\0\0\0\x2\0\0\0\0\0\0\0\x2\0\0\0\0\0\0\0\x2\0\0\0\0\0\0\0\x2\0\0\0\0\0\0\0\x2\0\0\0\0\0\0\0\x2\0\0\0\0\0\0\0\x2\0\0\0\0\0\0\0\x2\0\0\0\0\0\0\0\x2\0\0\0\0\0\0\0\x2\0\0\0\0\0\0\0\x2\0\0\0\0\0\0\0\x2\0\0\0\0\0\0\0\x2\0\0\0\0\0\0\0\x2\0\0\0\0\0\0\0\x2\0\0\0\0\0\0\0\x2\0\0\0\0\0\0\0\x2\0\0\0\0\0\0\0\x2\0\0\0\0\0\0\0\x2\0\0\0\0\0\0\0\x2\0\0\0\0\0\0\0\x2\0\0\0\0\0\0\0\x2\0\0\0\0\0\0\0\x2\0\0\0\0\0\0\0\x2\0\0\0\0\0\0\0\x2\0\0\0\0\0\0\0\x2\0\0\0\0\0\0\0\x2\0\0\0\0\0\0\0\x2\0\0\0\0\0\0\0\x2\0\0\0\0\0\0\0\x2\0\0\0\0\0\0\0\x2\0\0\0\0\0\0\0\x2\0\0\0\0\0\0\0\x2\0\0\0\0\0\0\0\x2\0\0\0\0\0\0\0\x2\0\0\0\0\0\0\0\x2\0\0\0\0\0\0\0\x2\0\0\0\0\0\0\0\x2\0\0\0\0\0\0\0\x2\0\0\0\0\0\0\0\x2\0\0\0\0\0\0\0\x2\0\0\0\0\0\0\0\x2\0\0\0\0\0\0\0\x2\0\0\0\0\0\0\0\x2\0\0\0\0\0\0\0\x2\0\0\0\0\0\0\0\x2\0\0\0\0\0\0\0\x2\0\0\0\0\0\0\0\x2\0\0\0\0\0\0\0\x2\0\0\0\0\0\0\0\x2\0\0\0\0\0\0\0\x2\0\0\0\0\0\0\0\x2\0\0\0\0\0\0\0\x2\0\0\0\0\0\0\0\x2\0\0\0\0\0\0\0\x2\0\0\0\0\0\0\0\x2\0\0\0\0\0\0\0\x2\0\0\0\0\0\0\0\x2\0\0\0\0\0\0\0\x2\0\0\0\0\0\0\0\x2\0\0\0\0\0\0\0\x2\0\0\0\0\0\0\0\x2\0\0\0\0\0\0\0\x2\0\0\0\0\0\0\0\x2\0\0\0\0\0\0\0\x2\0\0\0\0\0\0\0\x2\0\0\0\0\0\0\0\x2\0\0\0\0\0\0\0\x2\0\0\0\0\0\0\0\x2\0\0\0\0\0\0\0\x2\0\0\0\0\0\0\0\x2\0\0\0\0\0\0\0\x2\0\0\0\0\0\0\0\x2\0\0\0\0\0\0\0\x2\0\0\0\0\0\0\0\x2\0\0\0\0\0\0\0\x2\0\0\0\0\0\0\0\x2\0\0\0\0\0\0\0\x2\0\0\0\0\0\0\0\x2\0\0\0\0\0\0\0\x2\0\0\0\0\0\0\0\x2\0\0\0\0\0\0\0\x2\0\0\0\0\0\0\0\x2\0\0\0\0\0\0\0\x2\0\0\0\0\0\0\0\x2\0\0\0\0\0\0\0\x2\0\0\0\0\0\0\0\x2\0\0\0\0\0\0\0\x2\0\0\0\0\0\0\0\x2\0\0\0\0\0\0\0\x2\0\0\0\0\0\0\0\x2\0\0\0\0\0\0\0\x2\0\0\0\0\0\0\0\x2\0\0\0\0\0\0\0\x2\0\0\0\0\0\0\0\x2\0\0\0\0\0\0\0\x2\0\0\0\0\0\0\0\x2\0\0\0\0\0\0\0\x2\0\0\0\0\0\0\0\x2\0\0\0\0\0\0\0\x2\0\0\0\0\0\0\0\x2\0\0\0\0\0\0\0\x2\0\0\0\0\0\0\0\x2\0\0\0\0\0\0\0\x2\0\0\0\0\0\0\0\x2\0\0\0\0\0\0\0\x2\0\0\0\0\0\0\0\x2\0\0\0\0\0\0\0\x2\0\0\0\0\0\0\0\x2\0\0\0\0\0\0\0\x2\0\0\0\0\0\0\0\x2\0\0\0\0\0\0\0\x2\0\0\0\0\0\0\0\x2\0\0\0\0\0\0\0\x2\0\0\0\0\0\0\0\x2\0\0\0\0\0\0\0\x2\0\0\0\0\0\0\0\x2\0\0\0\0\0\0\0\x2\0\0\0\0\0\0\0\x2\0\0\0\0\0\0\0\x2\0\0\0\0\0\0\0\x2\0\0\0\0\0\0\0\x2\0\0\0\0\0\0\0\x2\0\0\0\0\0\0\0\x2\0\0\0\0\0\0\0\x2\0\0\0\0\0\0\0\x2\0\0\0\0\0\0\0\x2\0\0\0\0\0\0\0\x2\0\0\0\0\0\0\0\x2\0\0\0\0\0\0\0\x2\0\0\0\0\0\0\0\x2\0\0\0\0\0\0\0\x2\0\0\0\0\0\0\0\x2\0\0\0\0\0\0\0\x2\0\0\0\0\0\0\0\x2\0\0\0\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) 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\x3,\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\x4}\0\0\x1\x94\0\0\0\x4\0\0\0\x4\0\0\0\b\0\0\0\b\xfc\0\0\0\n\0\0\0\x2\0\0\0\0\0\0\0\x2\0\0\0\0\0\0\0\x2\0\0\0\0\0\0\0\x2\0\0\0\0\0\0\0\x2\0\0\0\0\0\0\0\x2\0\0\0\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)"
+4120
View File
File diff suppressed because it is too large Load Diff
+13 -5
View File
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE QtCreatorProject> <!DOCTYPE QtCreatorProject>
<!-- Written by QtCreator 19.0.0, 2026-04-13T20:38:08. --> <!-- Written by QtCreator 19.0.0, 2026-04-14T22:57:19. -->
<qtcreator> <qtcreator>
<data> <data>
<variable>EnvironmentId</variable> <variable>EnvironmentId</variable>
@@ -161,14 +161,18 @@
<valuelist type="QVariantList" key="Analyzer.Valgrind.SuppressionFiles"/> <valuelist type="QVariantList" key="Analyzer.Valgrind.SuppressionFiles"/>
<valuelist type="QVariantList" key="CustomOutputParsers"/> <valuelist type="QVariantList" key="CustomOutputParsers"/>
<value type="int" key="PE.EnvironmentAspect.Base">2</value> <value type="int" key="PE.EnvironmentAspect.Base">2</value>
<valuelist type="QVariantList" key="PE.EnvironmentAspect.Changes"/> <valuelist type="QVariantList" key="PE.EnvironmentAspect.Changes">
<value type="QString">QT_QPA_PLATFORM=xcb</value>
<value type="QString">QT_OPENGL=desktop</value>
</valuelist>
<value type="bool" key="PE.EnvironmentAspect.PrintOnRun">false</value> <value type="bool" key="PE.EnvironmentAspect.PrintOnRun">false</value>
<value type="QString" key="PerfRecordArgsId">-e cpu-cycles --call-graph dwarf,4096 -F 250</value> <value type="QString" key="PerfRecordArgsId">-e cpu-cycles --call-graph dwarf,4096 -F 250</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName"></value> <value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName"></value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">Qt4ProjectManager.Qt4RunConfiguration:</value> <value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">Qt4ProjectManager.Qt4RunConfiguration:</value>
<value type="QString" key="ProjectExplorer.RunConfiguration.BuildKey">/home/vladimir/Документы/source/cudaSDR/Source/cudaSDR.pro</value> <value type="QString" key="ProjectExplorer.RunConfiguration.BuildKey">/home/vladimir/Документы/source/cudaSDR/Source/cudaSDR.pro</value>
<value type="bool" key="ProjectExplorer.RunConfiguration.Customized">false</value> <value type="bool" key="ProjectExplorer.RunConfiguration.Customized">true</value>
<value type="QString" key="ProjectExplorer.RunConfiguration.UniqueId"></value> <value type="QString" key="ProjectExplorer.RunConfiguration.UniqueId"></value>
<value type="bool" key="RunConfiguration.Arguments.multi">true</value>
<value type="bool" key="RunConfiguration.UseCppDebuggerAuto">true</value> <value type="bool" key="RunConfiguration.UseCppDebuggerAuto">true</value>
<value type="bool" key="RunConfiguration.UseLibrarySearchPath">true</value> <value type="bool" key="RunConfiguration.UseLibrarySearchPath">true</value>
<value type="bool" key="RunConfiguration.UseQmlDebuggerAuto">true</value> <value type="bool" key="RunConfiguration.UseQmlDebuggerAuto">true</value>
@@ -273,14 +277,18 @@
<valuelist type="QVariantList" key="Analyzer.Valgrind.SuppressionFiles"/> <valuelist type="QVariantList" key="Analyzer.Valgrind.SuppressionFiles"/>
<valuelist type="QVariantList" key="CustomOutputParsers"/> <valuelist type="QVariantList" key="CustomOutputParsers"/>
<value type="int" key="PE.EnvironmentAspect.Base">2</value> <value type="int" key="PE.EnvironmentAspect.Base">2</value>
<valuelist type="QVariantList" key="PE.EnvironmentAspect.Changes"/> <valuelist type="QVariantList" key="PE.EnvironmentAspect.Changes">
<value type="QString">QT_QPA_PLATFORM=xcb</value>
<value type="QString">QT_OPENGL=desktop</value>
</valuelist>
<value type="bool" key="PE.EnvironmentAspect.PrintOnRun">false</value> <value type="bool" key="PE.EnvironmentAspect.PrintOnRun">false</value>
<value type="QString" key="PerfRecordArgsId">-e cpu-cycles --call-graph dwarf,4096 -F 250</value> <value type="QString" key="PerfRecordArgsId">-e cpu-cycles --call-graph dwarf,4096 -F 250</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName"></value> <value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName"></value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">Qt4ProjectManager.Qt4RunConfiguration:</value> <value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">Qt4ProjectManager.Qt4RunConfiguration:</value>
<value type="QString" key="ProjectExplorer.RunConfiguration.BuildKey">/home/vladimir/Документы/source/cudaSDR/Source/cudaSDR.pro</value> <value type="QString" key="ProjectExplorer.RunConfiguration.BuildKey">/home/vladimir/Документы/source/cudaSDR/Source/cudaSDR.pro</value>
<value type="bool" key="ProjectExplorer.RunConfiguration.Customized">false</value> <value type="bool" key="ProjectExplorer.RunConfiguration.Customized">true</value>
<value type="QString" key="ProjectExplorer.RunConfiguration.UniqueId"></value> <value type="QString" key="ProjectExplorer.RunConfiguration.UniqueId"></value>
<value type="bool" key="RunConfiguration.Arguments.multi">true</value>
<value type="bool" key="RunConfiguration.UseCppDebuggerAuto">true</value> <value type="bool" key="RunConfiguration.UseCppDebuggerAuto">true</value>
<value type="bool" key="RunConfiguration.UseLibrarySearchPath">true</value> <value type="bool" key="RunConfiguration.UseLibrarySearchPath">true</value>
<value type="bool" key="RunConfiguration.UseQmlDebuggerAuto">true</value> <value type="bool" key="RunConfiguration.UseQmlDebuggerAuto">true</value>
+50 -9
View File
@@ -448,7 +448,7 @@ bool DataEngine::startDataEngineWithoutConnection() {
if (!m_dataProcessor) createDataProcessor(); if (!m_dataProcessor) createDataProcessor();
// data receiver thread // data receiver thread
if (!startDataIO(QThread::NormalPriority)) { if (!startDataIO(QThread::HighestPriority)) {
setSystemState(QSDR::DataReceiverThreadError, m_hwInterface, m_serverMode, QSDR::DataEngineDown); setSystemState(QSDR::DataReceiverThreadError, m_hwInterface, m_serverMode, QSDR::DataEngineDown);
return false; return false;
@@ -562,8 +562,28 @@ bool DataEngine::findHPSDRDevices() {
set->setPennyLanePresence(false); set->setPennyLanePresence(false);
set->setExcaliburPresence(false); set->setExcaliburPresence(false);
set->setAlexPresence(currentDevice.hasAlex0 || currentDevice.hasAlex1); set->setAlexPresence(currentDevice.hasAlex0 || currentDevice.hasAlex1);
if (!currentDevice.widebandData && set->getWidebandData())
set->setWidebandData(this, false);
if (currentDevice.ddcCount > 0 && set->getNumberOfReceivers() > currentDevice.ddcCount) if (currentDevice.ddcCount > 0 && set->getNumberOfReceivers() > currentDevice.ddcCount)
set->setReceivers(this, currentDevice.ddcCount); set->setReceivers(this, currentDevice.ddcCount);
if (currentDevice.maxDdcSampleRateKsps > 0 || currentDevice.minDdcSampleRateKsps > 0) {
const int supportedRates[] = {48000, 96000, 192000, 384000, 768000, 1536000};
const int minRate = currentDevice.minDdcSampleRateKsps > 0 ? currentDevice.minDdcSampleRateKsps * 1000 : 48000;
const int maxRate = currentDevice.maxDdcSampleRateKsps > 0 ? currentDevice.maxDdcSampleRateKsps * 1000 : 1536000;
int fallbackRate = 0;
for (unsigned int i = 0; i < sizeof(supportedRates) / sizeof(supportedRates[0]); ++i) {
if (supportedRates[i] >= minRate && supportedRates[i] <= maxRate) {
if (fallbackRate == 0)
fallbackRate = supportedRates[i];
if (supportedRates[i] <= set->getSampleRate())
fallbackRate = supportedRates[i];
}
}
if (fallbackRate == 0)
fallbackRate = 48000;
if (set->getSampleRate() < minRate || set->getSampleRate() > maxRate)
set->setSampleRate(this, fallbackRate);
}
io.hermesFW = set->getCurrentMetisCard().firmwareVersion; io.hermesFW = set->getCurrentMetisCard().firmwareVersion;
set->setHermesVersion(io.hermesFW); set->setHermesVersion(io.hermesFW);
return true; return true;
@@ -630,7 +650,7 @@ bool DataEngine::getFirmwareVersions() {
} }
// data IO thread // data IO thread
if (!startDataIO(QThread::NormalPriority)) {// ::NormalPriority)) { if (!startDataIO(QThread::HighestPriority)) {// ::NormalPriority)) {
DATA_ENGINE_DEBUG << "data IO thread could not be started."; DATA_ENGINE_DEBUG << "data IO thread could not be started.";
return false; return false;
@@ -640,8 +660,11 @@ bool DataEngine::getFirmwareVersions() {
SleeperThread::msleep(100); SleeperThread::msleep(100);
// pre-conditioning // pre-conditioning
for (int i = 0; i < io.receivers; i++) const bool protocol2Active = (set->getCurrentMetisCard().protocolVersion >= 2);
invokeDataIOInitFrame(m_dataIO, i); if (!protocol2Active) {
for (int i = 0; i < io.receivers; i++)
invokeDataIOInitFrame(m_dataIO, i);
}
if (m_serverMode == QSDR::SDRMode) if (m_serverMode == QSDR::SDRMode)
invokeDataIOStartStop(m_dataIO, 0x01); // 0x01 for starting Metis without wide band data invokeDataIOStartStop(m_dataIO, 0x01); // 0x01 for starting Metis without wide band data
@@ -1027,7 +1050,7 @@ bool DataEngine::start() {
} }
// data IO thread // data IO thread
if (!startDataIO(QThread::NormalPriority)) {// ::NormalPriority::HighPriority)) { if (!startDataIO(QThread::HighestPriority)) {// ::NormalPriority::HighPriority)) {
DATA_ENGINE_DEBUG << "data IO thread could not be started."; DATA_ENGINE_DEBUG << "data IO thread could not be started.";
return false; return false;
@@ -1046,8 +1069,11 @@ bool DataEngine::start() {
SleeperThread::msleep(100); SleeperThread::msleep(100);
// pre-conditioning // pre-conditioning
for (int i = 0; i < io.receivers; i++) const bool protocol2Active = (set->getCurrentMetisCard().protocolVersion >= 2);
invokeDataIOInitFrame(m_dataIO, i); if (!protocol2Active) {
for (int i = 0; i < io.receivers; i++)
invokeDataIOInitFrame(m_dataIO, i);
}
if (m_serverMode == QSDR::SDRMode && set->getWidebandData()) if (m_serverMode == QSDR::SDRMode && set->getWidebandData())
invokeDataIOStartStop(m_dataIO, 0x03); // 0x03 for starting the device with wide band data invokeDataIOStartStop(m_dataIO, 0x03); // 0x03 for starting the device with wide band data
@@ -2265,6 +2291,18 @@ void DataEngine::setSampleRate(QObject *sender, int value) {
io.outputMultiplier = 8; io.outputMultiplier = 8;
break; break;
case 768000:
io.samplerate = value;
io.speed = 3;
io.outputMultiplier = 16;
break;
case 1536000:
io.samplerate = value;
io.speed = 3;
io.outputMultiplier = 32;
break;
default: default:
DATA_ENGINE_DEBUG << "invalid sample rate !\n"; DATA_ENGINE_DEBUG << "invalid sample rate !\n";
stop(); stop();
@@ -3668,6 +3706,9 @@ void DataProcessor::encodeCCBytes() {
void DataProcessor::writeData() { void DataProcessor::writeData() {
if (set->getCurrentMetisCard().protocolVersion >= 2)
return;
if (m_setNetworkDeviceHeader) { if (m_setNetworkDeviceHeader) {
//RRK updated for 4byte int and network order //RRK updated for 4byte int and network order
@@ -3864,8 +3905,8 @@ void WideBandDataProcessor::processWideBandInputBuffer(const QByteArray &buffer)
for (int i = 0; i < length; i += 2) { for (int i = 0; i < length; i += 2) {
s = (int)((qint8 ) buffer.at(i+1)) << 8; s = (int)((qint8 ) buffer.at(i)) << 8;
s += (int)((quint8) buffer.at(i)); s += (int)((quint8) buffer.at(i+1));
sample = (float)(s * norm); sample = (float)(s * norm);
cpxWBIn[i/2].re = sample * io->wbWindow.at(i/2); cpxWBIn[i/2].re = sample * io->wbWindow.at(i/2);
+46 -6
View File
@@ -24,8 +24,6 @@
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/ */
#define LOG_DATAIO
#include "cusdr_dataIO.h" #include "cusdr_dataIO.h"
#include "cusdr_protocol2_io.h" #include "cusdr_protocol2_io.h"
#include "soundout.h" #include "soundout.h"
@@ -155,7 +153,21 @@ void DataIO::initDataReceiverSocket() {
} }
else { else {
if (io->samplerate == 384000) { if (io->samplerate == 1536000) {
newBufferSize = 512*1024;//512 * 1032;
io->networkIOMutex.lock();
DATAIO_DEBUG << "socket buffer size set to 512 kB.";
io->networkIOMutex.unlock();
}
else if (io->samplerate == 768000) {
newBufferSize = 256*1024;//256 * 1032;
io->networkIOMutex.lock();
DATAIO_DEBUG << "socket buffer size set to 256 kB.";
io->networkIOMutex.unlock();
}
else if (io->samplerate == 384000) {
newBufferSize = 128*1024;//128 * 1032; newBufferSize = 128*1024;//128 * 1032;
io->networkIOMutex.lock(); io->networkIOMutex.lock();
@@ -185,9 +197,27 @@ void DataIO::initDataReceiverSocket() {
} }
} }
if (m_dataIOSocket->bind(QHostAddress(set->getHPSDRDeviceLocalAddr()), const QHostAddress localAddress(set->getHPSDRDeviceLocalAddr());
set->getMetisPort(), const quint16 requestedPort = set->getMetisPort();
QUdpSocket::DontShareAddress)) bool socketBound = m_dataIOSocket->bind(localAddress,
requestedPort,
QUdpSocket::DontShareAddress);
if (!socketBound && requestedPort != 0) {
io->networkIOMutex.lock();
DATAIO_DEBUG << "data receiver socket binding failed on port " << requestedPort
<< ", trying an ephemeral local port.";
io->networkIOMutex.unlock();
m_dataIOSocket->close();
socketBound = m_dataIOSocket->bind(localAddress,
0,
QUdpSocket::DontShareAddress);
if (socketBound)
set->setMetisPort(this, m_dataIOSocket->localPort());
}
if (socketBound)
//QUdpSocket::ReuseAddressHint | QUdpSocket::ShareAddress)) //QUdpSocket::ReuseAddressHint | QUdpSocket::ShareAddress))
{ {
@@ -684,6 +714,16 @@ void DataIO::setSampleRate(QObject *sender, int value) {
DATAIO_DEBUG << "socket buffer size set to 128 kB."; DATAIO_DEBUG << "socket buffer size set to 128 kB.";
break; break;
case 768000:
bufferSize = 256*1024;//256 * 1032
DATAIO_DEBUG << "socket buffer size set to 256 kB.";
break;
case 1536000:
bufferSize = 512*1024;//512 * 1032
DATAIO_DEBUG << "socket buffer size set to 512 kB.";
break;
default: default:
DATAIO_DEBUG << "invalid sample rate !\n"; DATAIO_DEBUG << "invalid sample rate !\n";
break; break;
@@ -231,8 +231,21 @@ int DiscovererP1::findHPSDRDevices() {
mcP1.adcCount = 0; mcP1.adcCount = 0;
mcP1.ddcCount = 0; mcP1.ddcCount = 0;
mcP1.dspClockHz = 0; mcP1.dspClockHz = 0;
mcP1.individualDdcSampleRates = false;
mcP1.minDdcSampleRateKsps = 48;
mcP1.maxDdcSampleRateKsps = 384;
mcP1.widebandData = false;
mcP1.widebandSamplesPerPacket = 0;
mcP1.widebandBitsPerSample = 0;
mcP1.ducCount = 1;
mcP1.ducSampleRateKsps = 48;
mcP1.ducBitsPerSample = 24;
mcP1.ducSamplesPerPacket = 0;
mcP1.iqFormatFlags = 0; mcP1.iqFormatFlags = 0;
mcP1.phaseWords = false; mcP1.phaseWords = false;
mcP1.ducPhaseWords = false;
mcP1.ddcAttenuator = false;
mcP1.ducRxAttenuator = false;
mcP1.hasAlex0 = false; mcP1.hasAlex0 = false;
mcP1.hasAlex1 = false; mcP1.hasAlex1 = false;
io->networkIOMutex.lock(); io->networkIOMutex.lock();
+46 -3
View File
@@ -193,7 +193,7 @@ int DiscovererP2::findHPSDRDevices() {
m_deviceDatagram[3] == (char)0) { m_deviceDatagram[3] == (char)0) {
status = m_deviceDatagram[4] & 0xFF; status = m_deviceDatagram[4] & 0xFF;
if (status == 2 || status == 3) { if (status == 2 || status == 3 || status == 0xFE || status == 0xFF) {
sprintf(mcP2.mac_address, "%02X:%02X:%02X:%02X:%02X:%02X", sprintf(mcP2.mac_address, "%02X:%02X:%02X:%02X:%02X:%02X",
m_deviceDatagram[5] & 0xFF, m_deviceDatagram[6] & 0xFF, m_deviceDatagram[7] & 0xFF, m_deviceDatagram[5] & 0xFF, m_deviceDatagram[6] & 0xFF, m_deviceDatagram[7] & 0xFF,
@@ -215,8 +215,21 @@ int DiscovererP2::findHPSDRDevices() {
mcP2.adcCount = profile.adcCount; mcP2.adcCount = profile.adcCount;
mcP2.ddcCount = profile.defaultDdcCount; mcP2.ddcCount = profile.defaultDdcCount;
mcP2.dspClockHz = profile.dspClockHz; mcP2.dspClockHz = profile.dspClockHz;
mcP2.individualDdcSampleRates = profile.individualDdcSampleRates;
mcP2.minDdcSampleRateKsps = profile.minDdcSampleRateKsps;
mcP2.maxDdcSampleRateKsps = profile.maxDdcSampleRateKsps;
mcP2.widebandData = profile.widebandData;
mcP2.widebandSamplesPerPacket = profile.widebandSamplesPerPacket;
mcP2.widebandBitsPerSample = profile.widebandBitsPerSample;
mcP2.ducCount = profile.ducCount;
mcP2.ducSampleRateKsps = profile.ducSampleRateKsps;
mcP2.ducBitsPerSample = profile.ducBitsPerSample;
mcP2.ducSamplesPerPacket = profile.ducSamplesPerPacket;
mcP2.iqFormatFlags = 0; mcP2.iqFormatFlags = 0;
mcP2.phaseWords = profile.phaseWords; mcP2.phaseWords = profile.phaseWords;
mcP2.ducPhaseWords = profile.ducPhaseWords;
mcP2.ddcAttenuator = profile.ddcAttenuator;
mcP2.ducRxAttenuator = profile.ducRxAttenuator;
mcP2.hasAlex0 = profile.hasAlex0; mcP2.hasAlex0 = profile.hasAlex0;
mcP2.hasAlex1 = profile.hasAlex1; mcP2.hasAlex1 = profile.hasAlex1;
@@ -226,13 +239,43 @@ int DiscovererP2::findHPSDRDevices() {
mcP2.iqFormatFlags = m_deviceDatagram[22] & 0xFF; mcP2.iqFormatFlags = m_deviceDatagram[22] & 0xFF;
} }
if (m_deviceDatagram.size() >= 97 && (m_deviceDatagram[4] & 0xFF) >= 0xFE) {
mcP2.ddcCount = m_deviceDatagram[39] & 0xFF;
mcP2.phaseWords = (m_deviceDatagram[40] & 0x01) != 0;
mcP2.ddcAttenuator = (m_deviceDatagram[41] & 0x01) != 0;
mcP2.widebandData = (m_deviceDatagram[42] & 0x01) != 0;
mcP2.widebandSamplesPerPacket =
((m_deviceDatagram[43] & 0xFF) << 8) | (m_deviceDatagram[44] & 0xFF);
mcP2.widebandBitsPerSample = m_deviceDatagram[46] & 0xFF;
mcP2.individualDdcSampleRates = (m_deviceDatagram[51] & 0x01) != 0;
mcP2.minDdcSampleRateKsps =
((m_deviceDatagram[52] & 0xFF) << 8) | (m_deviceDatagram[53] & 0xFF);
mcP2.maxDdcSampleRateKsps =
((m_deviceDatagram[54] & 0xFF) << 8) | (m_deviceDatagram[55] & 0xFF);
mcP2.ducCount = m_deviceDatagram[70] & 0xFF;
mcP2.ducPhaseWords = (m_deviceDatagram[78] & 0x01) != 0;
mcP2.ducSampleRateKsps = m_deviceDatagram[79] & 0xFF;
mcP2.ducBitsPerSample = m_deviceDatagram[80] & 0xFF;
mcP2.ducSamplesPerPacket =
((m_deviceDatagram[81] & 0xFF) << 8) | (m_deviceDatagram[82] & 0xFF);
mcP2.ducRxAttenuator = (m_deviceDatagram[92] & 0x01) != 0;
}
io->networkIOMutex.lock(); io->networkIOMutex.lock();
DISCOVERER_DEBUG << "Protocol 2 device board ID: " << device; DISCOVERER_DEBUG << "Protocol 2 device board ID: " << device;
DISCOVERER_DEBUG << "Protocol 2 device is: " << qPrintable(str); DISCOVERER_DEBUG << "Protocol 2 device is: " << qPrintable(str);
DISCOVERER_DEBUG << "Protocol 2 capabilities: " DISCOVERER_DEBUG << "Protocol 2 capabilities: "
<< mcP2.adcCount << " ADC, " << mcP2.adcCount << " ADC, "
<< mcP2.ddcCount << " DDC, phaseWords=" << mcP2.ddcCount << " DDC, DDC rate "
<< mcP2.phaseWords << ", iqFlags=0x" << mcP2.minDdcSampleRateKsps << "-"
<< mcP2.maxDdcSampleRateKsps
<< " ksps, phaseWords="
<< mcP2.phaseWords
<< ", DUC " << mcP2.ducSampleRateKsps
<< " ksps/" << mcP2.ducBitsPerSample
<< " bit, wideband="
<< mcP2.widebandData
<< ", iqFlags=0x"
<< QString::number(mcP2.iqFormatFlags, 16); << QString::number(mcP2.iqFormatFlags, 16);
io->networkIOMutex.unlock(); io->networkIOMutex.unlock();
File diff suppressed because it is too large Load Diff
+27 -1
View File
@@ -10,7 +10,9 @@
#include <QHash> #include <QHash>
#include <QList> #include <QList>
#include <QMutex>
#include <QSet> #include <QSet>
#include <QThread>
#include <QTimer> #include <QTimer>
#include <QUdpSocket> #include <QUdpSocket>
@@ -19,6 +21,8 @@ struct Protocol2IQSample {
qint32 q; qint32 q;
}; };
class Protocol2ReceiveThread;
class Protocol2DataPath : public QObject { class Protocol2DataPath : public QObject {
Q_OBJECT Q_OBJECT
@@ -53,12 +57,18 @@ private slots:
void handleWidebandDataChanged(QObject *sender, bool value); void handleWidebandDataChanged(QObject *sender, bool value);
private: private:
friend class Protocol2ReceiveThread;
QList<int> activeDdcIndexes() const; QList<int> activeDdcIndexes() const;
void closeSockets(); void closeSockets();
void processReceivedDatagram(const QByteArray &datagram, quint16 senderPort);
void processIgnoredDatagram(const char *data, int length, quint16 senderPort);
bool sendNativeDatagram(const QByteArray &datagram, const QHostAddress &address, quint16 port);
void sendGeneralPacket(); void sendGeneralPacket();
void sendDdcSpecificPacket(); void sendDdcSpecificPacket();
void sendDucSpecificPacket(); void sendDucSpecificPacket();
void sendHighPriorityPacket(bool run); void sendHighPriorityPacket(bool run);
void softRestartDdcStream();
void refreshDdcConfiguration(bool rebuildSockets); void refreshDdcConfiguration(bool rebuildSockets);
void refreshHighPriorityState(); void refreshHighPriorityState();
void refreshWidebandConfiguration(); void refreshWidebandConfiguration();
@@ -74,10 +84,15 @@ private:
Settings* set; Settings* set;
THPSDRParameter* io; THPSDRParameter* io;
QUdpSocket* m_commandSocket; QUdpSocket* m_commandSocket;
Protocol2ReceiveThread* m_receiveThread;
int m_commandSocketFd;
quint16 m_commandPort;
int m_commandReceiveBufferSize;
QUdpSocket* m_statusSocket; QUdpSocket* m_statusSocket;
QUdpSocket* m_widebandSocket; QUdpSocket* m_widebandSocket;
QList<QUdpSocket*> m_ddcSockets; QList<QUdpSocket*> m_ddcSockets;
QList<QList<Protocol2IQSample> > m_ddcSampleBuffers; QList<QByteArray> m_ddcSampleBuffers;
QList<QByteArray> m_legacySampleBuffers;
QTimer* m_heartbeatTimer; QTimer* m_heartbeatTimer;
QByteArray m_partialLegacyFrame; QByteArray m_partialLegacyFrame;
QByteArray m_statusDatagram; QByteArray m_statusDatagram;
@@ -95,17 +110,28 @@ private:
int m_heartbeatTick; int m_heartbeatTick;
quint64 m_ddcDatagramsReceived; quint64 m_ddcDatagramsReceived;
quint64 m_ddcSamplesReceived; quint64 m_ddcSamplesReceived;
quint32 m_lastDdcSequence;
quint64 m_ddcSequenceGaps;
quint64 m_ddcRecoveryAttempts;
quint64 m_legacyFramesEnqueued; quint64 m_legacyFramesEnqueued;
quint64 m_legacyFramesQueued; quint64 m_legacyFramesQueued;
quint64 m_legacyFramesDropped; quint64 m_legacyFramesDropped;
quint64 m_statusPacketsReceived; quint64 m_statusPacketsReceived;
quint64 m_highPriorityPacketsSent; quint64 m_highPriorityPacketsSent;
quint64 m_micPacketsIgnored;
quint64 m_widebandPacketsIgnored;
quint64 m_widebandPacketsLogged;
bool m_dropWidebandReceivePackets;
QHash<quint16, quint64> m_commandPortCounters; QHash<quint16, quint64> m_commandPortCounters;
QSet<quint16> m_loggedFirstSourcePorts; QSet<quint16> m_loggedFirstSourcePorts;
QMutex m_receiveMutex;
bool m_logConfigPackets; bool m_logConfigPackets;
uchar m_lastStatusByte4; uchar m_lastStatusByte4;
uchar m_lastStatusByte5; uchar m_lastStatusByte5;
bool m_loggedFirstDdcDatagram; bool m_loggedFirstDdcDatagram;
quint64 m_prevDdcDatagramsReceived; // to detect stream stop
int m_lastDdcGrowthTick;
int m_lastDdcRecoveryTick;
}; };
#endif // _CUSDR_PROTOCOL2_IO_H #endif // _CUSDR_PROTOCOL2_IO_H
@@ -15,7 +15,20 @@ static Protocol2BoardProfile makeDefaultProfile(int boardId) {
profile.adcCount = 1; profile.adcCount = 1;
profile.defaultDdcCount = 2; profile.defaultDdcCount = 2;
profile.dspClockHz = 122880000; profile.dspClockHz = 122880000;
profile.individualDdcSampleRates = true;
profile.minDdcSampleRateKsps = 48;
profile.maxDdcSampleRateKsps = 1536;
profile.widebandData = true;
profile.widebandSamplesPerPacket = 512;
profile.widebandBitsPerSample = 16;
profile.ducCount = 1;
profile.ducSampleRateKsps = 192;
profile.ducBitsPerSample = 24;
profile.ducSamplesPerPacket = 240;
profile.phaseWords = true; profile.phaseWords = true;
profile.ducPhaseWords = true;
profile.ddcAttenuator = true;
profile.ducRxAttenuator = true;
profile.hasAlex0 = true; profile.hasAlex0 = true;
profile.hasAlex1 = false; profile.hasAlex1 = false;
return profile; return profile;
@@ -35,6 +48,7 @@ Protocol2BoardProfile protocol2BoardProfile(int boardId) {
profile.defaultDdcCount = 4; profile.defaultDdcCount = 4;
profile.dspClockHz = 122880000; profile.dspClockHz = 122880000;
profile.phaseWords = false; profile.phaseWords = false;
profile.ducPhaseWords = false;
profile.hasAlex0 = true; profile.hasAlex0 = true;
profile.hasAlex1 = false; profile.hasAlex1 = false;
break; break;
@@ -62,9 +76,10 @@ Protocol2BoardProfile protocol2BoardProfile(int boardId) {
case 3: case 3:
profile.boardName = "Angelia / ANAN-100D"; profile.boardName = "Angelia / ANAN-100D";
profile.adcCount = 2; profile.adcCount = 2;
profile.defaultDdcCount = 4; profile.defaultDdcCount = 7;
profile.dspClockHz = 122880000; profile.dspClockHz = 122880000;
profile.phaseWords = true; profile.phaseWords = true;
profile.ducSamplesPerPacket = 238;
profile.hasAlex0 = true; profile.hasAlex0 = true;
profile.hasAlex1 = false; profile.hasAlex1 = false;
break; break;
@@ -75,6 +90,7 @@ Protocol2BoardProfile protocol2BoardProfile(int boardId) {
profile.defaultDdcCount = 4; profile.defaultDdcCount = 4;
profile.dspClockHz = 122880000; profile.dspClockHz = 122880000;
profile.phaseWords = true; profile.phaseWords = true;
profile.ducSamplesPerPacket = 240;
profile.hasAlex0 = true; profile.hasAlex0 = true;
profile.hasAlex1 = false; profile.hasAlex1 = false;
break; break;
@@ -99,6 +115,16 @@ Protocol2BoardProfile protocol2BoardProfile(int boardId) {
profile.hasAlex1 = false; profile.hasAlex1 = false;
break; break;
case 100:
profile.boardName = "Hermes Lite 2 / C25";
profile.adcCount = 1;
profile.defaultDdcCount = 2;
profile.dspClockHz = 76800000;
profile.phaseWords = true;
profile.hasAlex0 = false;
profile.hasAlex1 = false;
break;
case 10: case 10:
profile.boardName = "Saturn / ANAN-G2"; profile.boardName = "Saturn / ANAN-G2";
profile.adcCount = 2; profile.adcCount = 2;
@@ -129,6 +155,9 @@ QString protocol2DeviceSummary(const TNetworkDevicecard &device) {
if (device.adcCount > 0 || device.ddcCount > 0) if (device.adcCount > 0 || device.ddcCount > 0)
summary += QString(" %1ADC/%2DDC").arg(device.adcCount).arg(device.ddcCount); summary += QString(" %1ADC/%2DDC").arg(device.adcCount).arg(device.ddcCount);
if (device.maxDdcSampleRateKsps > 0)
summary += QString(" %1ksps").arg(device.maxDdcSampleRateKsps);
if (device.phaseWords) if (device.phaseWords)
summary += " phase"; summary += " phase";
@@ -15,7 +15,20 @@ struct Protocol2BoardProfile {
int adcCount; int adcCount;
int defaultDdcCount; int defaultDdcCount;
int dspClockHz; int dspClockHz;
bool individualDdcSampleRates;
int minDdcSampleRateKsps;
int maxDdcSampleRateKsps;
bool widebandData;
int widebandSamplesPerPacket;
int widebandBitsPerSample;
int ducCount;
int ducSampleRateKsps;
int ducBitsPerSample;
int ducSamplesPerPacket;
bool phaseWords; bool phaseWords;
bool ducPhaseWords;
bool ddcAttenuator;
bool ducRxAttenuator;
bool hasAlex0; bool hasAlex0;
bool hasAlex1; bool hasAlex1;
}; };
+9 -1
View File
@@ -373,8 +373,16 @@ void Receiver::setSampleRate(QObject *sender, int value) {
m_samplerate = value; m_samplerate = value;
break; break;
case 768000:
m_samplerate = value;
break;
case 1536000:
m_samplerate = value;
break;
default: default:
RECEIVER_DEBUG << "invalid sample rate (possible values are: 48, 96, 192, or 384 kHz)!\n"; RECEIVER_DEBUG << "invalid sample rate (possible values are: 48, 96, 192, 384, 768, or 1536 kHz)!\n";
break; break;
} }
+11
View File
@@ -2683,7 +2683,18 @@ void QGLDistancePanel::sampleRateChanged(QObject *sender, int value) {
Q_UNUSED(sender) Q_UNUSED(sender)
if (m_sampleRate == value)
return;
m_sampleRate = value; m_sampleRate = value;
m_freqScalePanadapterUpdate = true;
m_freqScalePanadapterRenew = true;
m_panGridUpdate = true;
m_panGridRenew = true;
m_spectrumUpdate = true;
m_spectrumVertexColorUpdate = true;
m_spectrumColorsChanged = true;
m_panadapterBins.clear();
update(); update();
} }
+19
View File
@@ -4315,13 +4315,32 @@ void QGLReceiverPanel::sampleRateChanged(QObject *sender, int value) {
Q_UNUSED(sender) Q_UNUSED(sender)
if (m_sampleRate == value)
return;
m_sampleRate = value; m_sampleRate = value;
if (m_vfoFrequency > m_centerFrequency + m_sampleRate/2)
m_vfoFrequency = m_centerFrequency + m_sampleRate/2;
else if (m_vfoFrequency < m_centerFrequency - m_sampleRate/2)
m_vfoFrequency = m_centerFrequency - m_sampleRate/2;
m_deltaFrequency = m_centerFrequency - m_vfoFrequency;
m_deltaF = (qreal)(1.0*m_deltaFrequency/m_sampleRate); m_deltaF = (qreal)(1.0*m_deltaFrequency/m_sampleRate);
m_freqScalePanadapterUpdate = true; m_freqScalePanadapterUpdate = true;
m_freqScalePanadapterRenew = true;
m_panGridUpdate = true; m_panGridUpdate = true;
m_panGridRenew = true;
m_waterfallUpdate = true;
m_waterfallDisplayUpdate = true;
m_secScaleWaterfallUpdate = true;
m_peakHoldBufferResize = true; m_peakHoldBufferResize = true;
m_filterChanged = true; m_filterChanged = true;
m_spectrumVertexColorUpdate = true;
m_spectrumColorsChanged = true;
m_panadapterBins.clear();
m_panPeakHoldBins.clear();
m_waterfallPixel.clear();
update(); update();
} }
+11
View File
@@ -2027,7 +2027,18 @@ void QGLWidebandPanel::sampleRateChanged(QObject *sender, int value) {
Q_UNUSED(sender) Q_UNUSED(sender)
if (m_sampleRate == value)
return;
m_sampleRate = value; m_sampleRate = value;
m_freqScaleUpdate = true;
m_freqScaleRenew = true;
m_panGridUpdate = true;
m_panGridRenew = true;
m_spectrumUpdate = true;
m_spectrumVertexColorUpdate = true;
m_spectrumColorsChanged = true;
m_widebandPanSpectrumBins.clear();
update(); update();
} }
+9 -1
View File
@@ -324,8 +324,16 @@ void QDSPEngine::setSampleRate(QObject *sender, int value) {
m_samplerate = value; m_samplerate = value;
break; break;
case 768000:
m_samplerate = value;
break;
case 1536000:
m_samplerate = value;
break;
default: default:
DSP_ENGINE_DEBUG << "invalid sample rate (possible values are: 48, 96, 192, or 384 kHz)!\n"; DSP_ENGINE_DEBUG << "invalid sample rate (possible values are: 48, 96, 192, 384, 768, or 1536 kHz)!\n";
break; break;
} }
+156 -7
View File
@@ -131,6 +131,18 @@ void HPSDRWidget::setupConnections() {
QSDR::_HWInterfaceMode, QSDR::_HWInterfaceMode,
QSDR::_ServerMode, QSDR::_ServerMode,
QSDR::_DataEngineState))); QSDR::_DataEngineState)));
CHECKED_CONNECT(
set,
SIGNAL(sampleRateChanged(QObject *, int)),
this,
SLOT(sampleRateValueChanged(QObject *, int)));
CHECKED_CONNECT(
set,
SIGNAL(hpsdrNetworkDeviceChanged(TNetworkDevicecard)),
this,
SLOT(setCurrentNetworkDevice(TNetworkDevicecard)));
} }
QGroupBox* HPSDRWidget::hpsdrHardwareBtnGroup() { QGroupBox* HPSDRWidget::hpsdrHardwareBtnGroup() {
@@ -460,6 +472,20 @@ QGroupBox *HPSDRWidget::sampleRateExclusiveGroup() {
samplerateBtnList.append(samplerate384Btn); samplerateBtnList.append(samplerate384Btn);
CHECKED_CONNECT(samplerate384Btn, SIGNAL(released()), this, SLOT(sampleRateChanged())); CHECKED_CONNECT(samplerate384Btn, SIGNAL(released()), this, SLOT(sampleRateChanged()));
samplerate768Btn = new AeroButton("768 kHz", this);
samplerate768Btn->setRoundness(0);
samplerate768Btn->setFixedSize (56, btn_height);
samplerate768Btn->setStyleSheet(set->getMiniButtonStyle());
samplerateBtnList.append(samplerate768Btn);
CHECKED_CONNECT(samplerate768Btn, SIGNAL(released()), this, SLOT(sampleRateChanged()));
samplerate1536Btn = new AeroButton("1536 kHz", this);
samplerate1536Btn->setRoundness(0);
samplerate1536Btn->setFixedSize (64, btn_height);
samplerate1536Btn->setStyleSheet(set->getMiniButtonStyle());
samplerateBtnList.append(samplerate1536Btn);
CHECKED_CONNECT(samplerate1536Btn, SIGNAL(released()), this, SLOT(sampleRateChanged()));
switch(set->getSampleRate()) { switch(set->getSampleRate()) {
case 48000: case 48000:
@@ -467,6 +493,8 @@ QGroupBox *HPSDRWidget::sampleRateExclusiveGroup() {
samplerate96Btn->setBtnState(AeroButton::OFF); samplerate96Btn->setBtnState(AeroButton::OFF);
samplerate192Btn->setBtnState(AeroButton::OFF); samplerate192Btn->setBtnState(AeroButton::OFF);
samplerate384Btn->setBtnState(AeroButton::OFF); samplerate384Btn->setBtnState(AeroButton::OFF);
samplerate768Btn->setBtnState(AeroButton::OFF);
samplerate1536Btn->setBtnState(AeroButton::OFF);
break; break;
case 96000: case 96000:
@@ -474,6 +502,8 @@ QGroupBox *HPSDRWidget::sampleRateExclusiveGroup() {
samplerate96Btn->setBtnState(AeroButton::ON); samplerate96Btn->setBtnState(AeroButton::ON);
samplerate192Btn->setBtnState(AeroButton::OFF); samplerate192Btn->setBtnState(AeroButton::OFF);
samplerate384Btn->setBtnState(AeroButton::OFF); samplerate384Btn->setBtnState(AeroButton::OFF);
samplerate768Btn->setBtnState(AeroButton::OFF);
samplerate1536Btn->setBtnState(AeroButton::OFF);
break; break;
case 192000: case 192000:
@@ -481,6 +511,8 @@ QGroupBox *HPSDRWidget::sampleRateExclusiveGroup() {
samplerate96Btn->setBtnState(AeroButton::OFF); samplerate96Btn->setBtnState(AeroButton::OFF);
samplerate192Btn->setBtnState(AeroButton::ON); samplerate192Btn->setBtnState(AeroButton::ON);
samplerate384Btn->setBtnState(AeroButton::OFF); samplerate384Btn->setBtnState(AeroButton::OFF);
samplerate768Btn->setBtnState(AeroButton::OFF);
samplerate1536Btn->setBtnState(AeroButton::OFF);
break; break;
case 384000: case 384000:
@@ -488,6 +520,26 @@ QGroupBox *HPSDRWidget::sampleRateExclusiveGroup() {
samplerate96Btn->setBtnState(AeroButton::OFF); samplerate96Btn->setBtnState(AeroButton::OFF);
samplerate192Btn->setBtnState(AeroButton::OFF); samplerate192Btn->setBtnState(AeroButton::OFF);
samplerate384Btn->setBtnState(AeroButton::ON); samplerate384Btn->setBtnState(AeroButton::ON);
samplerate768Btn->setBtnState(AeroButton::OFF);
samplerate1536Btn->setBtnState(AeroButton::OFF);
break;
case 768000:
samplerate48Btn->setBtnState(AeroButton::OFF);
samplerate96Btn->setBtnState(AeroButton::OFF);
samplerate192Btn->setBtnState(AeroButton::OFF);
samplerate384Btn->setBtnState(AeroButton::OFF);
samplerate768Btn->setBtnState(AeroButton::ON);
samplerate1536Btn->setBtnState(AeroButton::OFF);
break;
case 1536000:
samplerate48Btn->setBtnState(AeroButton::OFF);
samplerate96Btn->setBtnState(AeroButton::OFF);
samplerate192Btn->setBtnState(AeroButton::OFF);
samplerate384Btn->setBtnState(AeroButton::OFF);
samplerate768Btn->setBtnState(AeroButton::OFF);
samplerate1536Btn->setBtnState(AeroButton::ON);
break; break;
} }
@@ -498,6 +550,9 @@ QGroupBox *HPSDRWidget::sampleRateExclusiveGroup() {
hbox->addWidget(samplerate96Btn); hbox->addWidget(samplerate96Btn);
hbox->addWidget(samplerate192Btn); hbox->addWidget(samplerate192Btn);
hbox->addWidget(samplerate384Btn); hbox->addWidget(samplerate384Btn);
hbox->addWidget(samplerate768Btn);
hbox->addWidget(samplerate1536Btn);
updateSampleRateButtons();
QVBoxLayout *vbox = new QVBoxLayout(); QVBoxLayout *vbox = new QVBoxLayout();
vbox->setSpacing(4); vbox->setSpacing(4);
@@ -945,15 +1000,12 @@ void HPSDRWidget::sampleRateChanged() {
AeroButton *button = qobject_cast<AeroButton *>(sender()); AeroButton *button = qobject_cast<AeroButton *>(sender());
int btnHit = samplerateBtnList.indexOf(button); int btnHit = samplerateBtnList.indexOf(button);
foreach(AeroButton *btn, samplerateBtnList) { const QList<int> rates = QList<int>() << 48000 << 96000 << 192000 << 384000 << 768000 << 1536000;
if (btnHit < 0 || btnHit >= rates.size() || !sampleRateSupported(rates.at(btnHit))) {
btn->setBtnState(AeroButton::OFF); updateSampleRateButtons();
btn->update(); return;
} }
button->setBtnState(AeroButton::ON);
button->update();
switch (btnHit) { switch (btnHit) {
case 0: case 0:
@@ -975,6 +1027,102 @@ void HPSDRWidget::sampleRateChanged() {
set->setSampleRate(this, 384000); set->setSampleRate(this, 384000);
HPSDR_WIDGET_DEBUG << "set sample rate to 384 kHz."; HPSDR_WIDGET_DEBUG << "set sample rate to 384 kHz.";
break; break;
case 4:
set->setSampleRate(this, 768000);
HPSDR_WIDGET_DEBUG << "set sample rate to 768 kHz.";
break;
case 5:
set->setSampleRate(this, 1536000);
HPSDR_WIDGET_DEBUG << "set sample rate to 1536 kHz.";
break;
}
updateSampleRateButtons();
}
void HPSDRWidget::sampleRateValueChanged(QObject *sender, int value) {
Q_UNUSED(sender)
Q_UNUSED(value)
updateSampleRateButtons();
}
void HPSDRWidget::setCurrentNetworkDevice(TNetworkDevicecard card) {
Q_UNUSED(card)
updateSampleRateButtons();
}
bool HPSDRWidget::protocol2SampleRatesEnabled() const {
const TNetworkDevicecard device = set->getCurrentMetisCard();
return device.protocolVersion >= 2 && device.individualDdcSampleRates;
}
bool HPSDRWidget::sampleRateSupported(int rate) const {
const TNetworkDevicecard device = set->getCurrentMetisCard();
if (device.protocolVersion < 2)
return rate <= 384000;
if (!device.individualDdcSampleRates)
return rate == 48000;
const int minRate = qMax(48000, device.minDdcSampleRateKsps > 0 ? device.minDdcSampleRateKsps * 1000 : 48000);
const int maxRate = device.maxDdcSampleRateKsps > 0 ? device.maxDdcSampleRateKsps * 1000 : 1536000;
return rate >= minRate && rate <= maxRate;
}
void HPSDRWidget::updateSampleRateButtons() {
if (samplerateBtnList.isEmpty())
return;
const QList<int> rates = QList<int>() << 48000 << 96000 << 192000 << 384000 << 768000 << 1536000;
for (int i = 0; i < samplerateBtnList.size() && i < rates.size(); ++i)
samplerateBtnList.at(i)->setEnabled(sampleRateSupported(rates.at(i)));
if (!sampleRateSupported(set->getSampleRate())) {
int fallbackRate = 0;
for (int i = 0; i < rates.size(); ++i) {
if (sampleRateSupported(rates.at(i))) {
if (fallbackRate == 0)
fallbackRate = rates.at(i);
if (rates.at(i) <= set->getSampleRate())
fallbackRate = rates.at(i);
}
}
if (fallbackRate == 0)
fallbackRate = 48000;
set->setSampleRate(this, fallbackRate);
return;
}
const int currentRate = set->getSampleRate();
for (int i = 0; i < samplerateBtnList.size(); ++i) {
AeroButton *btn = samplerateBtnList.at(i);
btn->setBtnState(AeroButton::OFF);
btn->update();
}
int selected = 0;
switch (currentRate) {
case 48000: selected = 0; break;
case 96000: selected = 1; break;
case 192000: selected = 2; break;
case 384000: selected = 3; break;
case 768000: selected = 4; break;
case 1536000: selected = 5; break;
default: selected = 0; break;
}
if (selected >= 0 && selected < samplerateBtnList.size()) {
samplerateBtnList.at(selected)->setBtnState(AeroButton::ON);
samplerateBtnList.at(selected)->update();
} }
} }
@@ -1020,4 +1168,5 @@ void HPSDRWidget::enableButtons() {
mercury2Btn->setEnabled(true); mercury2Btn->setEnabled(true);
m_receiverComboBox->setEnabled(true); m_receiverComboBox->setEnabled(true);
updateSampleRateButtons();
} }
+7
View File
@@ -97,6 +97,8 @@ private:
AeroButton *samplerate96Btn; AeroButton *samplerate96Btn;
AeroButton *samplerate192Btn; AeroButton *samplerate192Btn;
AeroButton *samplerate384Btn; AeroButton *samplerate384Btn;
AeroButton *samplerate768Btn;
AeroButton *samplerate1536Btn;
QList<AeroButton *> hardwareBtnList; QList<AeroButton *> hardwareBtnList;
QList<AeroButton *> source10MhzBtnList; QList<AeroButton *> source10MhzBtnList;
@@ -120,6 +122,9 @@ private:
void setupConnections(); void setupConnections();
void createSource10MhzExclusiveGroup(); void createSource10MhzExclusiveGroup();
void createSource122_88MhzExclusiveGroup(); void createSource122_88MhzExclusiveGroup();
void updateSampleRateButtons();
bool protocol2SampleRatesEnabled() const;
bool sampleRateSupported(int rate) const;
private slots: private slots:
void systemStateChanged( void systemStateChanged(
@@ -136,6 +141,8 @@ private slots:
void enableButtons(); void enableButtons();
void firmwareCheckChanged(); void firmwareCheckChanged();
void sampleRateChanged(); void sampleRateChanged();
void sampleRateValueChanged(QObject *sender, int value);
void setCurrentNetworkDevice(TNetworkDevicecard card);
signals: signals:
void messageEvent(QString message); void messageEvent(QString message);
+45 -3
View File
@@ -65,6 +65,33 @@ Settings::Settings(QObject *parent)
qRegisterMetaType<QList<TNetworkDevicecard> >(); qRegisterMetaType<QList<TNetworkDevicecard> >();
qRegisterMetaType<qVectorFloat>("qVectorFloat"); qRegisterMetaType<qVectorFloat>("qVectorFloat");
m_currentHPSDRDevice.ip_address = QHostAddress();
m_currentHPSDRDevice.mac_address[0] = '\0';
m_currentHPSDRDevice.boardID = -1;
m_currentHPSDRDevice.boardName = "";
m_currentHPSDRDevice.protocolVersion = 0;
m_currentHPSDRDevice.firmwareVersion = 0;
m_currentHPSDRDevice.adcCount = 0;
m_currentHPSDRDevice.ddcCount = 0;
m_currentHPSDRDevice.dspClockHz = 0;
m_currentHPSDRDevice.individualDdcSampleRates = false;
m_currentHPSDRDevice.minDdcSampleRateKsps = 48;
m_currentHPSDRDevice.maxDdcSampleRateKsps = 384;
m_currentHPSDRDevice.widebandData = false;
m_currentHPSDRDevice.widebandSamplesPerPacket = 0;
m_currentHPSDRDevice.widebandBitsPerSample = 0;
m_currentHPSDRDevice.ducCount = 0;
m_currentHPSDRDevice.ducSampleRateKsps = 0;
m_currentHPSDRDevice.ducBitsPerSample = 0;
m_currentHPSDRDevice.ducSamplesPerPacket = 0;
m_currentHPSDRDevice.iqFormatFlags = 0;
m_currentHPSDRDevice.phaseWords = false;
m_currentHPSDRDevice.ducPhaseWords = false;
m_currentHPSDRDevice.ddcAttenuator = false;
m_currentHPSDRDevice.ducRxAttenuator = false;
m_currentHPSDRDevice.hasAlex0 = false;
m_currentHPSDRDevice.hasAlex1 = false;
startTime = QDateTime::currentDateTime(); startTime = QDateTime::currentDateTime();
qDebug() << "************************************************************************"; qDebug() << "************************************************************************";
@@ -289,7 +316,8 @@ int Settings::loadSettings() {
//checkHPSDRDevices(); //checkHPSDRDevices();
value = settings->value("server/sample_rate", 48000).toInt(); value = settings->value("server/sample_rate", 48000).toInt();
if ((value != 48000) & (value != 96000) & (value != 192000) & (value != 384000)) value = 48000; if ((value != 48000) && (value != 96000) && (value != 192000) &&
(value != 384000) && (value != 768000) && (value != 1536000)) value = 48000;
setSampleRate(this, value); setSampleRate(this, value);
str = settings->value("server/dither", "off").toString(); str = settings->value("server/dither", "off").toString();
@@ -3356,15 +3384,29 @@ void Settings::setSampleRate(QObject *sender, int value) {
m_chirpDownSampleRate = 32; m_chirpDownSampleRate = 32;
break; break;
case 768000:
m_sampleRate = value;
m_mercurySpeed = 3;
m_outputSampleIncrement = 16;
m_chirpDownSampleRate = 64;
break;
case 1536000:
m_sampleRate = value;
m_mercurySpeed = 3;
m_outputSampleIncrement = 32;
m_chirpDownSampleRate = 128;
break;
default: default:
SETTINGS_DEBUG << "Invalid sample rate (must be 48, 96,192, or 384 kHz)!\n"; SETTINGS_DEBUG << "Invalid sample rate (must be 48, 96, 192, 384, 768, or 1536 kHz)!\n";
break; break;
} }
for (int i = 0; i < MAX_RECEIVERS; i++) for (int i = 0; i < MAX_RECEIVERS; i++)
m_receiverDataList[i].sampleRate = m_sampleRate; m_receiverDataList[i].sampleRate = m_sampleRate;
emit sampleRateChanged(sender, value); emit sampleRateChanged(sender, m_sampleRate);
} }
void Settings::setMercuryAttenuator(QObject *sender, int value) { void Settings::setMercuryAttenuator(QObject *sender, int value) {
+13
View File
@@ -488,8 +488,21 @@ typedef struct _networkDeviceCard {
int adcCount; int adcCount;
int ddcCount; int ddcCount;
int dspClockHz; int dspClockHz;
bool individualDdcSampleRates;
int minDdcSampleRateKsps;
int maxDdcSampleRateKsps;
bool widebandData;
int widebandSamplesPerPacket;
int widebandBitsPerSample;
int ducCount;
int ducSampleRateKsps;
int ducBitsPerSample;
int ducSamplesPerPacket;
int iqFormatFlags; int iqFormatFlags;
bool phaseWords; bool phaseWords;
bool ducPhaseWords;
bool ddcAttenuator;
bool ducRxAttenuator;
bool hasAlex0; bool hasAlex0;
bool hasAlex1; bool hasAlex1;
+25
View File
@@ -27,3 +27,28 @@
пн апр. 13 21:24:23 2026: planning FFT size 128 ... пн апр. 13 21:24:23 2026: planning FFT size 128 ...
пн апр. 13 21:24:23 2026: planning FFT size 256 ... пн апр. 13 21:24:23 2026: planning FFT size 256 ...
пн апр. 13 21:24:24 2026: planning FFT size 512 ... пн апр. 13 21:24:24 2026: planning FFT size 512 ...
ср апр. 29 16:23:06 2026: ************************************************************************
ср апр. 29 16:23:06 2026: Settings:: start at: ср апр. 29 16:23:06 2026
ср апр. 29 16:23:06 2026: cudaSDR BETA
ср апр. 29 16:23:07 2026: Settings:: Alex config: 4098
ср апр. 29 16:23:07 2026: Settings:: reading done.
ср апр. 29 16:23:07 2026: Init:: OpenGL found.
ср апр. 29 16:23:07 2026: Init:: Framebuffer Objects found.
ср апр. 29 16:23:07 2026: Init:: main window setup ...
ср апр. 29 16:23:08 2026: MainWindow:: main window init done
ср апр. 29 16:23:08 2026: MainWindow:: server ip from ini-file: "192.168.122.1"
ср апр. 29 16:23:08 2026: MainWindow:: HPSDR device local ip from ini-file: "172.16.2.99"
ср апр. 29 16:23:08 2026: MainWindow:: network interface "bridge0" :
ср апр. 29 16:23:08 2026: MainWindow:: -> broadcast address: 172.16.2.255
ср апр. 29 16:23:08 2026: MainWindow:: -> ip address: 172.16.2.99
ср апр. 29 16:23:08 2026: MainWindow:: network interface "virbr0" :
ср апр. 29 16:23:08 2026: MainWindow:: -> broadcast address: 192.168.122.255
ср апр. 29 16:23:08 2026: MainWindow:: -> ip address: 192.168.122.1
ср апр. 29 16:23:08 2026: MainWindow:: found 2 valid ip addresses.
ср апр. 29 16:23:08 2026: MainWindow:: HPSDR network device interface set to: "bridge0"("172.16.2.99")
ср апр. 29 16:23:08 2026: MainWindow:: HPSDR network device interface set to: "bridge0"("172.16.2.99")
ср апр. 29 16:23:08 2026: MainWindow:: using ip address 192.168.122.1 for the server.
ср апр. 29 16:23:08 2026: MainWindow:: using ip address 172.16.2.99 for connecting to a HPSDR device.
ср апр. 29 16:23:08 2026: Init:: main window setup done.
ср апр. 29 16:23:08 2026: Init:: current path: /home/vladimir/Документы/source/cudaSDR
ср апр. 29 16:23:08 2026: Init:: fftw_wisdom does not exist - planning FFT...