#include "loratap.h" #include #include #include #include namespace roro::lora { static void le16(std::vector& o, uint16_t v) { o.insert(o.end(), {uint8_t(v), uint8_t(v >> 8)}); } static void le32(std::vector& o, uint32_t v) { o.insert(o.end(), {uint8_t(v), uint8_t(v >> 8), uint8_t(v >> 16), uint8_t(v >> 24)}); } // LoRaTap's dBm encoding: -139 dBm plus the byte, clamped to what a byte holds. static uint8_t dbmByte(float dbm) { long v = std::lround(dbm + 139); return static_cast(std::clamp(v, 0L, 255L)); } std::string capturePath(int64_t utcSeconds) { time_t t = static_cast(utcSeconds); struct tm u; gmtime_r(&t, &u); char buf[48]; std::snprintf(buf, sizeof buf, "/captures/lora/%04d%02d%02d-%02d%02d%02d.pcap", u.tm_year + 1900, u.tm_mon + 1, u.tm_mday, u.tm_hour, u.tm_min, u.tm_sec); return buf; } void appendPcapHeader(std::vector& out) { le32(out, 0xA1B2C3D4); le16(out, 2); le16(out, 4); le32(out, 0); // time zone le32(out, 0); // timestamp accuracy le32(out, 65535); // snap length le32(out, 270); // LINKTYPE_LORATAP } void appendRecord(std::vector& out, uint32_t seconds, uint32_t micros, const RxInfo& rx, const uint8_t* data, size_t len) { uint32_t captured = static_cast(kLoraTapSize + len); le32(out, seconds); le32(out, micros); le32(out, captured); le32(out, captured); uint32_t f = rx.frequencyHz; // The spec puts packet RSSI in quarter dB below 0 dB SNR (an SX127x formula), but Wireshark // reads it as -139 dBm plus the byte either way, and the SX1262 already gives dBm. uint8_t packetRssi = dbmByte(rx.rssi); long snr = std::clamp(std::lround(rx.snr * 4), -128L, 127L); out.insert(out.end(), { 0, 0, 0, uint8_t(kLoraTapSize), // version 0, padding, length uint8_t(f >> 24), uint8_t(f >> 16), uint8_t(f >> 8), uint8_t(f), uint8_t(std::lround(rx.bandwidthKHz / 125)), rx.spreadingFactor, packetRssi, dbmByte(rx.rssi), dbmByte(rx.noiseFloor), static_cast(static_cast(snr)), rx.syncWord, }); if (len) out.insert(out.end(), data, data + len); } } // namespace roro::lora