Fixed POCSAG decoding

master
Hannes Matuschek 11 years ago
parent 0ad4ec8634
commit 00c21e6b6e

@ -1,12 +1,12 @@
IF(SDR_WITH_PORTAUDIO)
# add_executable(sdr_wavplay sdr_wavplay.cc)
# target_link_libraries(sdr_wavplay ${LIBS} libsdr)
add_executable(sdr_wavplay sdr_wavplay.cc)
target_link_libraries(sdr_wavplay ${LIBS} libsdr)
# add_executable(sdr_fm sdr_fm.cc)
# target_link_libraries(sdr_fm ${LIBS} libsdr)
add_executable(sdr_fm sdr_fm.cc)
target_link_libraries(sdr_fm ${LIBS} libsdr)
# add_executable(sdr_rec sdr_rec.cc)
# target_link_libraries(sdr_rec ${LIBS} libsdr)
add_executable(sdr_rec sdr_rec.cc)
target_link_libraries(sdr_rec ${LIBS} libsdr)
add_executable(sdr_afsk1200 sdr_afsk1200.cc)
target_link_libraries(sdr_afsk1200 ${LIBS} libsdr)

@ -1,7 +1,7 @@
#include "wavfile.hh"
#include "autocast.hh"
#include "interpolate.hh"
#include "afsk.hh"
#include "fsk.hh"
#include "ax25.hh"
#include "utils.hh"

@ -1,7 +1,7 @@
#include "autocast.hh"
#include "portaudio.hh"
#include "wavfile.hh"
#include "afsk.hh"
#include "fsk.hh"
#include "utils.hh"
#include "pocsag.hh"
@ -36,21 +36,17 @@ int main(int argc, char *argv[])
WavSource src(argv[1]);
PortSink sink;
AutoCast<int16_t> cast;
ASKDetector<int16_t> detector;
ASKDetector<int16_t> detector(false);
BitStream bits(1200, BitStream::NORMAL);
POCSAG pocsag;
POCSAGDump pocsag(std::cout);
//BitDump dump;
// Playback
//src.connect(&sink);
// Cast to int16
src.connect(&cast);
// ASK detector
cast.connect(&detector);
// bit decoder
detector.connect(&bits);
// Baudot decoder
// dump to std::cerr
// POCSAG decoder and print
bits.connect(&pocsag);
// on idle -> read next buffer from input file

@ -1,81 +0,0 @@
#include "baseband.hh"
#include "autocast.hh"
#include "demod.hh"
#include "portaudio.hh"
#include "wavfile.hh"
#include "gui/gui.hh"
#include "psk31.hh"
#include <iostream>
#include <cmath>
#include <csignal>
#include <QApplication>
#include <QMainWindow>
#include <QThread>
using namespace sdr;
static void __sigint_handler(int signo) {
// On SIGINT -> stop queue properly
Queue::get().stop();
}
int main(int argc, char *argv[]) {
if (2 != argc) {
std::cerr << "Usage: sdr_psk31 FILENAME" << std::endl;
return -1;
}
sdr::Logger::get().addHandler(
new sdr::StreamLogHandler(std::cerr, sdr::LOG_DEBUG));
// Register handler:
signal(SIGINT, __sigint_handler);
QApplication app(argc, argv);
QMainWindow *win = new QMainWindow();
gui::Spectrum *spec = new gui::Spectrum(2, 1024, 5);
gui::SpectrumView *spec_view = new gui::SpectrumView(spec);
spec_view->setMindB(-60);
win->setCentralWidget(spec_view);
win->setMinimumSize(640, 240);
win->show();
PortAudio::init();
Queue &queue = Queue::get();
WavSource src(argv[1]);
AutoCast< std::complex<int16_t> > cast;
IQBaseBand<int16_t> baseband(0, 2144., 200.0, 63, 1);
baseband.setCenterFrequency(2144.0);
BPSK31<int16_t> demod;
PortSink sink;
Varicode decode;
DebugDump<uint8_t> dump;
src.connect(&cast, true);
cast.connect(&baseband, true);
baseband.connect(spec);
baseband.connect(&sink);
baseband.connect(&demod);
demod.connect(&decode, true);
decode.connect(&dump, true);
queue.addIdle(&src, &WavSource::next);
//queue.addIdle(&src, &IQSigGen<double>::next);
queue.start();
app.exec();
queue.stop();
queue.wait();
PortAudio::terminate();
return 0;
}

@ -1,82 +0,0 @@
#include "sdr.hh"
#include "rtlsource.hh"
#include "baseband.hh"
#include "autocast.hh"
#include "gui/gui.hh"
#include "logger.hh"
#include <signal.h>
#include <QApplication>
#include <QMainWindow>
#include <QThread>
using namespace sdr;
static void __sigint_handler(int signo) {
// On SIGINT -> stop queue properly
Queue::get().stop();
}
int main(int argc, char *argv[])
{
if (argc < 2) {
std::cerr << "USAGE: sdr_rds FREQUENCY" << std::endl;
return -1;
}
double freq = atof(argv[1]);
PortAudio::init();
Queue &queue = Queue::get();
// Register handler:
signal(SIGINT, __sigint_handler);
QApplication app(argc, argv);
QMainWindow *win = new QMainWindow();
gui::Spectrum *spec = new gui::Spectrum(2, 1024, 5);
gui::SpectrumView *spec_view = new gui::SpectrumView(spec);
spec_view->setMindB(-200);
win->setCentralWidget(spec_view);
win->setMinimumSize(640, 240);
win->show();
sdr::Logger::get().addHandler(new sdr::StreamLogHandler(std::cerr, sdr::LOG_DEBUG));
// Assemble processing chain
//RTLSource src(freq, 1e6);
WavSource src(argv[1]);
AutoCast< std::complex<int16_t> > cast;
IQBaseBand<int16_t> baseband(0, 200e3, 16, 5);
FMDemod<int16_t, int16_t> demod;
BaseBand<int16_t> mono(0, 15e3, 16, 6);
BaseBand<int16_t> pilot(19e3, 5e2, 16, 84);
BaseBand<int16_t> rds(57e3, 3e3, 16, 84);
PortSink sink;
src.connect(&cast, true);
cast.connect(&baseband, true);
//src.connect(&baseband, true);
baseband.connect(&demod, true);
demod.connect(&mono);
demod.connect(&pilot);
demod.connect(&rds);
mono.connect(&sink);
mono.connect(spec);
//queue.addStart(&src, &RTLSource::start);
//queue.addStop(&src, &RTLSource::stop);
queue.addIdle(&src, &WavSource::next);
queue.start();
app.exec();
queue.stop();
queue.wait();
PortAudio::terminate();
return 0;
}

@ -1,7 +1,7 @@
#include "autocast.hh"
#include "portaudio.hh"
#include "wavfile.hh"
#include "afsk.hh"
#include "fsk.hh"
#include "baudot.hh"
#include "utils.hh"

@ -1,64 +0,0 @@
#include "sdr.hh"
#include "rtlsource.hh"
#include "baseband.hh"
#include "utils.hh"
#include "gui/gui.hh"
#include <signal.h>
#include "portaudio.hh"
#include <QApplication>
#include <QMainWindow>
#include <QThread>
using namespace sdr;
static void __sigint_handler(int signo) {
// On SIGINT -> stop queue properly
Queue::get().stop();
}
int main(int argc, char *argv[])
{
Queue &queue = Queue::get();
// Register handler:
signal(SIGINT, __sigint_handler);
PortAudio::init();
QApplication app(argc, argv);
QMainWindow *win = new QMainWindow();
gui::Spectrum *spec = new gui::Spectrum(2, 1024, 5);
gui::WaterFallView *spec_view = new gui::WaterFallView(spec);
win->setCentralWidget(spec_view);
win->setMinimumSize(640, 240);
win->show();
// Assemble processing chain
PortSource< int16_t > src(44100.0, 2048);
AGC<int16_t> agc;
//IQBaseBand<int16_t> baseband(0, 500e3, 8, 5);
//AMDemod<int16_t> demod;
PortSink sink;
src.connect(&agc, true);
agc.connect(&sink, true);
//baseband.connect(&demod);
//demod.connect(&sink, true);
src.connect(spec);
queue.addIdle(&src, &PortSource< int16_t >::next);
queue.start();
app.exec();
queue.stop();
queue.wait();
PortAudio::terminate();
return 0;
}

@ -2,12 +2,12 @@
set(LIBSDR_SOURCES
buffer.cc node.cc queue.cc traits.cc
portaudio.cc utils.cc wavfile.cc
exception.cc logger.cc psk31.cc options.cc afsk.cc ax25.cc baudot.cc pocsag.cc bch31_21.cc)
exception.cc logger.cc psk31.cc options.cc fsk.cc ax25.cc baudot.cc pocsag.cc bch31_21.cc)
set(LIBSDR_HEADERS sdr.hh math.hh
buffer.hh node.hh queue.hh buffernode.hh filternode.hh traits.hh autocast.hh
siggen.hh portaudio.hh utils.hh wavfile.hh demod.hh firfilter.hh
fftplan.hh fftplan_native.hh exception.hh baseband.hh freqshift.hh subsample.hh
combine.hh logger.hh psk31.hh interpolate.hh operators.hh options.hh afsk.hh ax25.hh
combine.hh logger.hh psk31.hh interpolate.hh operators.hh options.hh fsk.hh ax25.hh
baudot.hh pocsag.hh bch31_21.hh)
if(SDR_WITH_PORTAUDIO)

@ -1,4 +1,4 @@
#include "afsk.hh"
#include "fsk.hh"
#include "logger.hh"
#include "traits.hh"
#include "interpolate.hh"
@ -6,6 +6,9 @@
using namespace sdr;
/* ******************************************************************************************** *
* Implementation of FSKDetector
* ******************************************************************************************** */
FSKDetector::FSKDetector(float baud, float Fmark, float Fspace)
: Sink<int16_t>(), Source(), _baud(baud), _corrLen(0), _Fmark(Fmark), _Fspace(Fspace)
{
@ -92,6 +95,10 @@ FSKDetector::process(const Buffer<int16_t> &buffer, bool allow_overwrite) {
}
/* ******************************************************************************************** *
* Implementation of BitStream
* ******************************************************************************************** */
BitStream::BitStream(float baud, Mode mode)
: Sink<uint8_t>(), Source(), _baud(baud), _mode(mode), _corrLen(0)
{
@ -106,7 +113,7 @@ BitStream::config(const Config &src_cfg) {
// Check if buffer type matches
if (Config::typeId<uint8_t>() != src_cfg.type()) {
ConfigError err;
err << "Can not configure FSKBitStreamBase: Invalid type " << src_cfg.type()
err << "Can not configure BitStream: Invalid type " << src_cfg.type()
<< ", expected " << Config::typeId<int16_t>();
throw err;
}
@ -136,11 +143,11 @@ BitStream::config(const Config &src_cfg) {
_buffer = Buffer<uint8_t>(1+src_cfg.bufferSize()/_corrLen);
LogMessage msg(LOG_DEBUG);
msg << "Config FSKBitStreamBase node: " << std::endl
<< " input sample rate: " << src_cfg.sampleRate() << " Hz" << std::endl
<< " baud rate: " << _baud << std::endl
<< " samples per bit: " << 1./_omega << std::endl
<< " phase incr/symbol: " << _omega;
msg << "Config BitStream node: " << std::endl
<< " symbol rate: " << src_cfg.sampleRate() << " Hz" << std::endl
<< " baud rate: " << _baud << std::endl
<< " symbols/bit: " << 1./_omega << std::endl
<< " bit mode: " << ( (NORMAL == _mode) ? "normal" : "transition" );
Logger::get().log(msg);
// Forward config.
@ -193,3 +200,37 @@ BitStream::process(const Buffer<uint8_t> &buffer, bool allow_overwrite)
if (o>0) { this->send(_buffer.head(o)); }
}
/* ******************************************************************************************** *
* Implementation of BitDump
* ******************************************************************************************** */
BitDump::BitDump(std::ostream &stream)
: Sink<uint8_t>(), _stream(stream)
{
// pass...
}
void
BitDump::config(const Config &src_cfg) {
// Check if config is complete
if (!src_cfg.hasType()) { return; }
// Check if buffer type matches
if (Config::typeId<uint8_t>() != src_cfg.type()) {
ConfigError err;
err << "Can not configure BitDump: Invalid type " << src_cfg.type()
<< ", expected " << Config::typeId<int16_t>();
throw err;
}
}
void
BitDump::process(const Buffer<uint8_t> &buffer, bool allow_overwrite) {
for (size_t i=0; i<buffer.size(); i++) {
_stream << int(buffer[i]) << " ";
}
_stream << std::endl;
}

@ -1,5 +1,5 @@
#ifndef __SDR_AFSK_HH__
#define __SDR_AFSK_HH__
#ifndef __SDR_FSK_HH__
#define __SDR_FSK_HH__
#include "node.hh"
#include "traits.hh"
@ -8,11 +8,20 @@
namespace sdr {
/** Implements the basic FSK/AFSK symbol detection.
* This node contains two FIR filters for the detection of the mark and space frequencies. The node
* returns a sequence of symbols (i.e. sub-bits) which need to be processed to obtain a sequenc of
* transmitted bits (i.e. by the @c BitStream node).
*
* @ingroup demods */
class FSKDetector: public Sink<int16_t>, public Source
{
public:
/** Constructor.
* @param baud Specifies the baud-rate of the signal.
* @param Fmark Specifies the mark frequency in Hz.
* @param Fspace Specifies the space frequency in Hz. */
FSKDetector(float baud, float Fmark, float Fspace);
void config(const Config &src_cfg);
@ -23,11 +32,16 @@ protected:
uint8_t _process(int16_t sample);
protected:
/** Baudrate of the transmission. Needed to compute the filter length of the FIR mark/space
* filters. */
float _baud;
/** The filter lenght. */
size_t _corrLen;
/** The current FIR filter LUT index. */
size_t _lutIdx;
/** Mark "tone" frequency. */
float _Fmark;
/** Space "tone" frequency. */
float _Fspace;
/** Mark frequency FIR filter LUT. */
Buffer< std::complex<float> > _markLUT;
@ -42,12 +56,23 @@ protected:
};
/** Rather trivial node to detect mark/space symbols by the amplitude.
* For low baud rates (i.e. <= 1200 baud) a FSK signal can be "demodulated" using a
* simple FM demodulator. The result will be a series of decaying exponentials. Hence the
* mark/space symbols can be determined by the means of the input amplitude (positive/negative).
*
* This node implements such a simple symbol detection by the means of the amplitude. The node
* returns a sequence of symbols (sub-bits) that need to be processed to obtain the sequence of
* received bits (i.e. @c BitStream).
*
* @ingroup demods */
template <class Scalar>
class ASKDetector: public Sink<Scalar>, public Source
{
public:
ASKDetector()
: Sink<Scalar>(), Source()
/** Constructor. */
ASKDetector(bool invert=false)
: Sink<Scalar>(), Source(), _invert(invert)
{
// pass...
}
@ -69,8 +94,9 @@ public:
LogMessage msg(LOG_DEBUG);
msg << "Config ASKDetector node: " << std::endl
<< " detection threshold: " << 0 << std::endl
<< " sample/symbol rate: " << src_cfg.sampleRate() << " Hz";
<< " threshold: " << 0 << std::endl
<< " invert: " << ( _invert ? "yes" : "no" ) << std::endl
<< " symbol rate: " << src_cfg.sampleRate() << " Hz";
Logger::get().log(msg);
// Forward config.
@ -79,17 +105,22 @@ public:
void process(const Buffer<Scalar> &buffer, bool allow_overwrite) {
for (size_t i=0; i<buffer.size(); i++) {
_buffer[i] = (buffer[i]>0);
_buffer[i] = ((buffer[i]>0)^_invert);
}
this->send(_buffer.head(buffer.size()), false);
}
protected:
/** If true the symbol logic is inverted. */
bool _invert;
/** The output buffer. */
Buffer<uint8_t> _buffer;
};
/** Decodes a bitstream with the desired baud rate. */
/** Decodes a bitstream with the desired baud rate.
* This node implements a simple PLL to syncronize the bit sampling with the transitions
* of the input symbol sequence. */
class BitStream: public Sink<uint8_t>, public Source
{
public:
@ -100,45 +131,62 @@ public:
} Mode;
public:
/** Constructor.
* @param baud Specifies the baud-rate of the input signal.
* @param mode Specifies the bit detection mode. */
BitStream(float baud, Mode mode = TRANSITION);
void config(const Config &src_cfg);
void process(const Buffer<uint8_t> &buffer, bool allow_overwrite);
protected:
/** The baud rate. */
float _baud;
/** The bit detection mode. */
Mode _mode;
/** The approximative bit length in samples. */
size_t _corrLen;
/** Last received symbols. */
Buffer<int8_t> _symbols;
/** Insertion index for the next symbol. */
size_t _symIdx;
/** Sum over all received symbol (encoded as -1 & 1). */
int32_t _symSum;
/** Last sum over all received symbol (encoded as -1 & 1). */
int32_t _lastSymSum;
/** Current bit "phase". */
float _phase;
/** Phase velocity. */
float _omega;
/** Minimum phase velocity. */
float _omegaMin;
/** Maximum phase velocity. */
float _omegaMax;
/** PLL gain. */
float _pllGain;
/** The last decoded bits (needed for transition mode). */
uint8_t _lastBits;
/** Output buffer. */
Buffer<uint8_t> _buffer;
};
/** Trivial node to dump a bit-stream to a std::ostream.
* @ingroup sinks */
class BitDump : public Sink<uint8_t>
{
public:
void config(const Config &src_cfg) {}
void process(const Buffer<uint8_t> &buffer, bool allow_overwrite) {
for (size_t i=0; i<buffer.size(); i++) {
std::cerr << int(buffer[i]) << " ";
}
std::cerr << std::endl;
}
/** Constructor.
* @param stream Specifies the output stream. */
BitDump(std::ostream &stream);
void config(const Config &src_cfg);
void process(const Buffer<uint8_t> &buffer, bool allow_overwrite);
protected:
/** The output stream. */
std::ostream &_stream;
};
}
#endif // __SDR_AFSK_HH__
#endif // __SDR_FSK_HH__

@ -1,5 +1,6 @@
#include "pocsag.hh"
#include "bch31_21.hh"
#include "logger.hh"
using namespace sdr;
@ -8,6 +9,9 @@ inline bool is_address(uint32_t word) {
}
/* ********************************************************************************************* *
* Implementation of POCSAG
* ********************************************************************************************* */
POCSAG::POCSAG()
: Sink<uint8_t>()
{
@ -25,6 +29,10 @@ POCSAG::config(const Config &src_cfg) {
throw err;
}
LogMessage msg(LOG_DEBUG);
msg << "Config POCSAG node.";
Logger::get().log(msg);
_state = WAIT;
_bits = 0;
}
@ -43,7 +51,7 @@ POCSAG::process(const Buffer<uint8_t> &buffer, bool allow_overwrite)
uint32_t word = (_bits & 0xffffffff);
if ( (0 == pocsag_repair(word)) && (0x7cd215d8 == word)) {
// init messages
_reset_all_messages();
_reset_message();
_state = RECEIVE; _bitcount = 0; _slot = 0;
}
} else if (RECEIVE == _state) {
@ -77,7 +85,8 @@ POCSAG::process(const Buffer<uint8_t> &buffer, bool allow_overwrite)
_state = RECEIVE; _slot = 0; _bitcount = 0;
} else {
// Otherwise -> end of transmission, wait for next sync
_finish_all_messages(); _state = WAIT;
_finish_message(); _state = WAIT;
// Process received messages
this->handleMessages();
}
}
@ -89,66 +98,157 @@ POCSAG::process(const Buffer<uint8_t> &buffer, bool allow_overwrite)
void
POCSAG::_process_word(uint32_t word)
{
// Check if slot is skipped
if (0x7A89C197 == word) { // Skip slot
_finish_message(_slot); return;
}
/*std::cerr << "POCSAG: RX " << std::hex << word
<< std::dec << " @ " << int(_slot) << std::endl; */
// Check if word is an address word
if (is_address(word)) {
_finish_message(_slot);
if (0x7A89C197 == word) {
// Skip
_finish_message();
} else if (is_address(word)) {
// If address word
_finish_message();
// Assemble address
uint32_t addr = (((word>>13) & 0x03ffff)<<3) | (_slot & 0x03);
uint32_t addr = ((((word>>13) & 0x03ffff)<<3) + _slot );
uint8_t func = ((word>>11) & 0x03);
_messages[_slot] = Message(addr, func);
// init new message
_message = Message(addr, func);
} else {
if (_messages[_slot].isEmpty()) {
std::cerr << "Oops: Payload w/o address in slot " << int(_slot)
<< " word: " << std::hex << word << std::dec << std::endl;
// on data word
if (_message.isEmpty()) {
LogMessage msg(LOG_DEBUG);
msg << "POCSAG: Payload w/o address in slot " << int(_slot)
<< " word: " << std::hex << word;
Logger::get().log(msg);
} else {
_message.addPayload(word);
}
_messages[_slot].addPayload(word);
}
}
void
POCSAG::_reset_message(uint8_t slot) {
_messages[slot] = Message();
POCSAG::_reset_message() {
_message = Message();
}
void
POCSAG::_reset_all_messages() {
for (uint8_t i=0; i<8; i++) {
_reset_message(i);
}
}
void
POCSAG::_finish_message(uint8_t slot) {
if (_messages[slot].isEmpty()) { return; }
_queue.push_back(_messages[slot]);
_reset_message(slot);
}
void
POCSAG::_finish_all_messages() {
for (uint8_t i=0; i<8; i++) {
_finish_message(i);
}
POCSAG::_finish_message() {
if (_message.isEmpty()) { return; }
_queue.push_back(_message);
_reset_message();
}
void
POCSAG::handleMessages() {
// pass...
}
/* ********************************************************************************************* *
* Implementation of POCSAGDump
* ********************************************************************************************* */
POCSAGDump::POCSAGDump(std::ostream &stream)
: POCSAG(), _stream(stream)
{
// pass...
}
void
POCSAGDump::handleMessages() {
// You may re-implement this virutal method to process the queued messages in _queue.
while (_queue.size()) {
Message msg = _queue.back(); _queue.pop_back();
std::cerr << "POCSAG: @" << msg.address()
<< ", F=" << int(msg.function())
<< ", bits=" << msg.bits() << std::endl;
if (msg.estimateText() >= msg.estimateNumeric()) {
std::cerr << " txt: " << msg.asText() << "" << std::endl;
} else {
std::cerr << " num: " << msg.asNumeric() << std::endl;
}
//std::cerr << " hex: " << msg.asHex() << std::endl;
}
}
/* ********************************************************************************************* *
* Implementation of POCSAG::Message
* ********************************************************************************************* */
std::string
ascii2text(uint8_t byte) {
switch ( byte ) {
case 0: return "<NUL>";
case 1: return "<SOH>";
case 2: return "<STX>";
case 3: return "<ETX>";
case 4: return "<EOT>";
case 5: return "<ENQ>";
case 6: return "<ACK>";
case 7: return "<BEL>";
case 8: return "<BS>";
case 9: return "<HT>";
case 10: return "<LF>";
case 11: return "<VT>";
case 12: return "<FF>";
case 13: return "<CR>";
case 14: return "<SO>";
case 15: return "<SI>";
case 16: return "<DLE>";
case 17: return "<DC1>";
case 18: return "<DC2>";
case 19: return "<DC3>";
case 20: return "<DC4>";
case 21: return "<NAK>";
case 22: return "<SYN>";
case 23: return "<ETB>";
case 24: return "<CAN>";
case 25: return "<EM>";
case 26: return "<SUB>";
case 27: return "<ESC>";
case 28: return "<FS>";
case 29: return "<GS>";
case 30: return "<RS>";
case 31: return "<US>";
default: break;
}
std::string txt; txt.resize(1, char(byte));
return txt;
}
char
bcd2text(uint8_t bcd) {
static const char *conv_table = "084 2.6]195-3U7[";
return conv_table[bcd&0xf];
}
int
pocsag_text_weight(char c) {
if (c < 32 || c == 127) {
return -5; // Non printable characters are uncommon
}
if ( (c > 32 && c < 48)
|| (c > 57 && c < 65)
|| (c > 90 && c < 97)
|| (c > 122 && c < 127) ) {
return -2; // Penalize special characters
}
return 1;
}
int
pocsag_numeric_weight(char cp, size_t pos) {
if(cp == 'U')
return -10;
if(cp == '[' || cp == ']')
return -5;
if(cp == ' ' || cp == '.' || cp == '-')
return -2;
if(pos < 10) // Penalize long messages
return 5;
return 0;
}
POCSAG::Message::Message()
: _address(0), _function(0), _empty(true), _bits(0)
@ -181,15 +281,92 @@ POCSAG::Message::operator =(const Message &other) {
void
POCSAG::Message::addPayload(uint32_t word) {
// Add 20 LSB bits from data to payload vector
// Add data bits from data to payload vector
uint32_t mask = 0x40000000;
for (size_t i=0; i<20; i++) {
// on new byte
for (int i=19; i>=0; i--) {
// on new byte -> add empty byte to payload
if (0 == (_bits % 8)) { _payload.push_back(0x00); }
// add bit to last byte of payload
_payload.back() = ((_payload.back()<<1)|(word&mask));
_payload.back() = ((_payload.back()<<1) | ((word & mask)>>(i+11)));
// Increment bit counter and update mask
_bits++; mask = (mask>>1);
}
}
std::string
POCSAG::Message::asText() const
{
uint8_t byte = 0;
// Decode message
std::stringstream buffer;
for (size_t i=0; i<_bits; i++) {
size_t byteIdx = i/8;
size_t bitIdx = (7-(i%8));
// add bit to byte (reverse order)
byte = ((byte>>1) | (((_payload[byteIdx]>>bitIdx) & 0x01) << 6));
if (6 == (i%7)) {
buffer << ascii2text(byte&0x7f);
}
//std::cerr << "byte " << byteIdx << " bit " << bitIdx << " ascii bit " << (i%7) << std::endl;
}
return buffer.str();
}
std::string
POCSAG::Message::asNumeric() const
{
// Get number of complete half-bytes in payload
size_t N = _bits/4;
// Decode message
std::stringstream buffer;
for (size_t i=0; i<(N/2); i++) {
buffer << bcd2text((_payload[i]>>4) & 0xf);
buffer << bcd2text((_payload[i]>>0) & 0xf);
}
if (N%2) {
buffer << bcd2text((_payload[N/2]>>0) & 0xf);
}
return buffer.str();
}
std::string
POCSAG::Message::asHex() const {
std::stringstream buffer;
buffer << std::hex;
for (size_t i=0; i<_payload.size(); i++) {
buffer << int(_payload[i]);
}
return buffer.str();
}
int
POCSAG::Message::estimateText() const {
int weight = 0;
uint8_t byte = 0;
for (size_t i=0; i<_bits; i++) {
size_t byteIdx = i/8;
size_t bitIdx = (7-i%8);
// add bit to byte
byte = ((byte>>1) | (((_payload[byteIdx]>>bitIdx) & 0x01) << 6));
if (6 == i%7) {
weight += pocsag_text_weight(byte&0x7f);
}
}
return weight;
}
int
POCSAG::Message::estimateNumeric() const {
int weight = 0;
// Get number of complete half-bytes in payload
size_t N = _bits/4;
for (size_t i=0; i<(N/2); i++) {
weight += pocsag_numeric_weight(bcd2text((_payload[i]>>4) & 0xf), i);
weight += pocsag_numeric_weight(bcd2text((_payload[i]>>0) & 0xf), i);
}
if (N%2) {
weight += pocsag_numeric_weight(bcd2text((_payload[N/2]>>0) & 0xf), N/2);
}
return weight;
}

@ -13,93 +13,126 @@ namespace sdr {
*
* 1) at least 576 bits of alternating value (1 0 1 0 ...)
* 2) a 32-bit sync word (0x7CD215D8)
* 3) 16 data words (each 32 bit)
* 3) 2x8 data words (each 32 bit)
* 4) If data left to send -> continue with step 2
*
* Unused data words are send as 0x7A89C197. Each dataword is either a address word (bit 31 = 0)
* or message word (bit 31 = 1).
*
* Address word:
*
* +---+---+---+---+---+---+---+---+
* | 0 | Address (18bits) |
* +---+---+---+---+---+---+---+---+
* | ... |
* +---+---+---+---+---+---+---+---+
* | ... | F1 F0 | ECC ... |
* +---+---+---+---+---+---+---+---+
* | ... (10 bits) | P |
* +---+---+---+---+---+---+---+---+
*
* Message word
*
* +---+---+---+---+---+---+---+---+
* | 0 | Data (20bits) |
* +---+---+---+---+---+---+---+---+
* | ... |
* +---+---+---+---+---+---+---+---+
* | ... | ECC ... |
* +---+---+---+---+---+---+---+---+
* | ... (10 bits) | P |
* +---+---+---+---+---+---+---+---+
*
* @ingroup datanodes */
class POCSAG: public Sink<uint8_t>
{
public:
/** A posac message. */
class Message {
public:
/** Empty constructor. */
Message();
/** Constructor from address and function. */
Message(uint32_t addr, uint8_t func);
/** Copy constructor. */
Message(const Message &other);
/** Assignment operator. */
Message &operator=(const Message &other);
/** Retruns @c true if the message is empty (has no address). */
inline bool isEmpty() const { return _empty; }
/** Returns the address of the message. */
inline uint32_t address() const { return _address; }
/** Returns the function of the message. */
inline uint8_t function() const { return _function; }
/** Returns the number of data bits. */
inline uint32_t bits() const { return _bits; }
/** Adds some payload from the given POGSAC word. */
void addPayload(uint32_t word);
int estimateText() const;
int estimateNumeric() const;
/** Decodes the message as a text message. */
std::string asText() const;
/** Decodes the message as a numeric message. */
std::string asNumeric() const;
/** Dumps the payload. */
std::string asHex() const;
protected:
/** The address of the message. */
uint32_t _address;
/** The function of the message. */
uint8_t _function;
/** If @c true the message is empty. */
bool _empty;
/** The number of payload bits in the message. */
uint32_t _bits;
/** The actual payload. */
std::vector<uint8_t> _payload;
};
protected:
/** The possible states of the POGSAC receiver. */
typedef enum {
WAIT,
RECEIVE,
CHECK_CONTINUE
WAIT, ///< Wait for a sync word.
RECEIVE, ///< Receive data.
CHECK_CONTINUE ///< Wait for the sync word for continuation.
} State;
public:
/** Constructor. */
POCSAG();
void config(const Config &src_cfg);
void process(const Buffer<uint8_t> &buffer, bool allow_overwrite);
/** Can be overwritten by any other implementation to process the received messages
* stored in @c _queue. */
virtual void handleMessages();
protected:
/** Process a POGSAC word. */
void _process_word(uint32_t word);
void _reset_all_messages();
void _reset_message(uint8_t slot);
void _finish_all_messages();
void _finish_message(uint8_t slot);
/** Clear the message. */
void _reset_message();
/** Add the (non-empty) message to the queue. */
void _finish_message();
protected:
/** The current state. */
State _state;
/** The last received bits. */
uint64_t _bits;
/** The number of received bits. */
uint8_t _bitcount;
/** The current slot. */
uint8_t _slot;
Message _messages[8];
/** The current message. */
Message _message;
/** The completed messages. */
std::list<Message> _queue;
};
/** A simple extention of the @c POCSAG node that prints the received messages to a
* @c std::ostream.
* @ingroup datanodes */
class POCSAGDump: public POCSAG
{
public:
/** Constructor.
* @param stream Specifies the stream, the received messages are serialized into. */
POCSAGDump(std::ostream &stream);
/** Dumps the received messages. */
void handleMessages();
protected:
/** The output stream. */
std::ostream &_stream;
};
}
#endif // POSAG_HH
#endif // __SDR_POSAG_HH__

@ -290,9 +290,10 @@
#include "demod.hh"
#include "psk31.hh"
#include "afsk.hh"
#include "fsk.hh"
#include "baudot.hh"
#include "ax25.hh"
#include "pocsag.hh"
#include "fftplan.hh"

Loading…
Cancel
Save