MCPcopy Create free account
hub / github.com/aethersdr/AetherSDR / decodeWaterfallTile

Method decodeWaterfallTile

src/core/PanadapterStream.cpp:967–1057  ·  view source on GitHub ↗

Source from the content-addressed store, hash-verified

965// ~96-106, signal peaks ~110-115. Colour-mapped in SpectrumWidget.
966
967void PanadapterStream::decodeWaterfallTile(const uchar* raw, int totalBytes, bool hasTrailer, quint32 streamId)
968{
969 static constexpr int TILE_SUBHEADER_BYTES = 36;
970 if (totalBytes < VITA49_HEADER_BYTES + TILE_SUBHEADER_BYTES) return;
971
972 const uchar* sub = raw + VITA49_HEADER_BYTES;
973
974 // Extract frequency range from tile sub-header.
975 // FrameLowFreq and BinBandwidth are int64 "VitaFrequency" (Hz × 2^20).
976 const qint64 frameLowRaw = qFromBigEndian<qint64>(sub + 0);
977 const qint64 binBwRaw = qFromBigEndian<qint64>(sub + 8);
978 const quint16 tileWidth = qFromBigEndian<quint16>(sub + 20);
979 const quint16 tileHeight = qFromBigEndian<quint16>(sub + 22);
980 const quint32 timecode = qFromBigEndian<quint32>(sub + 24);
981 const quint32 autoBlack = qFromBigEndian<quint32>(sub + 28);
982 const quint16 totalBinsInFrame = qFromBigEndian<quint16>(sub + 32);
983 const quint16 firstBinIndex = qFromBigEndian<quint16>(sub + 34);
984
985 if (tileWidth == 0 || tileHeight == 0) return;
986
987 // FrameLowFreq and BinBandwidth arrive as either VitaFrequency (Hz × 2^20)
988 // or plain Hz; disambiguate on the raw integer magnitude so there is no
989 // upper frequency ceiling. The previous "divide then reject results above
990 // 1000 MHz" heuristic blacked out the waterfall for every transverter above
991 // 1 GHz (#3449, #1843, #1928, #2835). See VitaTileFrequency.h.
992 const auto tileFreq = AetherSDR::Vita::decodeTileFrequencyMhz(frameLowRaw, binBwRaw);
993 const double lowFreqMhz = tileFreq.lowMhz;
994 const double binBwMhz = tileFreq.binBwMhz;
995 const double highFreqMhz = lowFreqMhz + binBwMhz * tileWidth;
996
997 const int payloadOffset = VITA49_HEADER_BYTES + TILE_SUBHEADER_BYTES;
998 const int payloadBytes = totalBytes - payloadOffset - (hasTrailer ? 4 : 0);
999 if (payloadBytes < tileWidth * 2) return; // need at least one row of bins
1000
1001 static bool loggedOnce = false;
1002 if (!loggedOnce) {
1003 qCDebug(lcVita49) << "WaterfallTile: width=" << tileWidth << "height=" << tileHeight
1004 << "totalBinsInFrame=" << totalBinsInFrame
1005 << "firstBinIndex=" << firstBinIndex
1006 << "timecode=" << timecode
1007 << "lowFreqMhz=" << lowFreqMhz
1008 << "binBwMhz=" << binBwMhz
1009 << "highFreqMhz=" << highFreqMhz
1010 << "fullFrameMhz=" << (lowFreqMhz + binBwMhz * totalBinsInFrame)
1011 << "autoBlack=" << autoBlack;
1012 loggedOnce = true;
1013 }
1014
1015 // ── Waterfall frame assembly ─────────────────────────────────────────
1016 // Start a new frame if timecode changed OR if totalBinsInFrame changed.
1017 // Without the totalBins check, a spoofed packet that reuses a timecode
1018 // with an inflated totalBinsInFrame leaves wfFrame.buf undersized
1019 // relative to the bounds calculation below, and the inner write loop
1020 // then writes past the buffer's end with attacker-chosen bytes.
1021 // See GHSA-7gvg-x594-pprq.
1022 auto& wfFrame = m_wfFrames[streamId];
1023 if (timecode != wfFrame.timecode
1024 || totalBinsInFrame != wfFrame.totalBins) {

Callers

nothing calls this directly

Calls 6

decodeTileFrequencyMhzFunction · 0.85
recordFrameRestartMethod · 0.80
isCompleteMethod · 0.45
resetMethod · 0.45
sizeMethod · 0.45
enabledMethod · 0.45

Tested by

no test coverage detected