Fixed POCSAG decoding

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

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

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

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

@ -1,4 +1,4 @@
#include "afsk.hh" #include "fsk.hh"
#include "logger.hh" #include "logger.hh"
#include "traits.hh" #include "traits.hh"
#include "interpolate.hh" #include "interpolate.hh"
@ -6,6 +6,9 @@
using namespace sdr; using namespace sdr;
/* ******************************************************************************************** *
* Implementation of FSKDetector
* ******************************************************************************************** */
FSKDetector::FSKDetector(float baud, float Fmark, float Fspace) FSKDetector::FSKDetector(float baud, float Fmark, float Fspace)
: Sink<int16_t>(), Source(), _baud(baud), _corrLen(0), _Fmark(Fmark), _Fspace(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) BitStream::BitStream(float baud, Mode mode)
: Sink<uint8_t>(), Source(), _baud(baud), _mode(mode), _corrLen(0) : 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 // Check if buffer type matches
if (Config::typeId<uint8_t>() != src_cfg.type()) { if (Config::typeId<uint8_t>() != src_cfg.type()) {
ConfigError err; 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>(); << ", expected " << Config::typeId<int16_t>();
throw err; throw err;
} }
@ -136,11 +143,11 @@ BitStream::config(const Config &src_cfg) {
_buffer = Buffer<uint8_t>(1+src_cfg.bufferSize()/_corrLen); _buffer = Buffer<uint8_t>(1+src_cfg.bufferSize()/_corrLen);
LogMessage msg(LOG_DEBUG); LogMessage msg(LOG_DEBUG);
msg << "Config FSKBitStreamBase node: " << std::endl msg << "Config BitStream node: " << std::endl
<< " input sample rate: " << src_cfg.sampleRate() << " Hz" << std::endl << " symbol rate: " << src_cfg.sampleRate() << " Hz" << std::endl
<< " baud rate: " << _baud << std::endl << " baud rate: " << _baud << std::endl
<< " samples per bit: " << 1./_omega << std::endl << " symbols/bit: " << 1./_omega << std::endl
<< " phase incr/symbol: " << _omega; << " bit mode: " << ( (NORMAL == _mode) ? "normal" : "transition" );
Logger::get().log(msg); Logger::get().log(msg);
// Forward config. // Forward config.
@ -193,3 +200,37 @@ BitStream::process(const Buffer<uint8_t> &buffer, bool allow_overwrite)
if (o>0) { this->send(_buffer.head(o)); } 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__ #ifndef __SDR_FSK_HH__
#define __SDR_AFSK_HH__ #define __SDR_FSK_HH__
#include "node.hh" #include "node.hh"
#include "traits.hh" #include "traits.hh"
@ -8,11 +8,20 @@
namespace sdr { namespace sdr {
/** Implements the basic FSK/AFSK symbol detection. /** 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 */ * @ingroup demods */
class FSKDetector: public Sink<int16_t>, public Source class FSKDetector: public Sink<int16_t>, public Source
{ {
public: 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); FSKDetector(float baud, float Fmark, float Fspace);
void config(const Config &src_cfg); void config(const Config &src_cfg);
@ -23,11 +32,16 @@ protected:
uint8_t _process(int16_t sample); uint8_t _process(int16_t sample);
protected: protected:
/** Baudrate of the transmission. Needed to compute the filter length of the FIR mark/space
* filters. */
float _baud; float _baud;
/** The filter lenght. */
size_t _corrLen; size_t _corrLen;
/** The current FIR filter LUT index. */ /** The current FIR filter LUT index. */
size_t _lutIdx; size_t _lutIdx;
/** Mark "tone" frequency. */
float _Fmark; float _Fmark;
/** Space "tone" frequency. */
float _Fspace; float _Fspace;
/** Mark frequency FIR filter LUT. */ /** Mark frequency FIR filter LUT. */
Buffer< std::complex<float> > _markLUT; 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> template <class Scalar>
class ASKDetector: public Sink<Scalar>, public Source class ASKDetector: public Sink<Scalar>, public Source
{ {
public: public:
ASKDetector() /** Constructor. */
: Sink<Scalar>(), Source() ASKDetector(bool invert=false)
: Sink<Scalar>(), Source(), _invert(invert)
{ {
// pass... // pass...
} }
@ -69,8 +94,9 @@ public:
LogMessage msg(LOG_DEBUG); LogMessage msg(LOG_DEBUG);
msg << "Config ASKDetector node: " << std::endl msg << "Config ASKDetector node: " << std::endl
<< " detection threshold: " << 0 << std::endl << " threshold: " << 0 << std::endl
<< " sample/symbol rate: " << src_cfg.sampleRate() << " Hz"; << " invert: " << ( _invert ? "yes" : "no" ) << std::endl
<< " symbol rate: " << src_cfg.sampleRate() << " Hz";
Logger::get().log(msg); Logger::get().log(msg);
// Forward config. // Forward config.
@ -79,17 +105,22 @@ public:
void process(const Buffer<Scalar> &buffer, bool allow_overwrite) { void process(const Buffer<Scalar> &buffer, bool allow_overwrite) {
for (size_t i=0; i<buffer.size(); i++) { 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); this->send(_buffer.head(buffer.size()), false);
} }
protected: protected:
/** If true the symbol logic is inverted. */
bool _invert;
/** The output buffer. */
Buffer<uint8_t> _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 class BitStream: public Sink<uint8_t>, public Source
{ {
public: public:
@ -100,45 +131,62 @@ public:
} Mode; } Mode;
public: 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); BitStream(float baud, Mode mode = TRANSITION);
void config(const Config &src_cfg); void config(const Config &src_cfg);
void process(const Buffer<uint8_t> &buffer, bool allow_overwrite); void process(const Buffer<uint8_t> &buffer, bool allow_overwrite);
protected: protected:
/** The baud rate. */
float _baud; float _baud;
/** The bit detection mode. */
Mode _mode; Mode _mode;
/** The approximative bit length in samples. */
size_t _corrLen; size_t _corrLen;
/** Last received symbols. */
Buffer<int8_t> _symbols; Buffer<int8_t> _symbols;
/** Insertion index for the next symbol. */
size_t _symIdx; size_t _symIdx;
/** Sum over all received symbol (encoded as -1 & 1). */
int32_t _symSum; int32_t _symSum;
/** Last sum over all received symbol (encoded as -1 & 1). */
int32_t _lastSymSum; int32_t _lastSymSum;
/** Current bit "phase". */
float _phase; float _phase;
/** Phase velocity. */
float _omega; float _omega;
/** Minimum phase velocity. */
float _omegaMin; float _omegaMin;
/** Maximum phase velocity. */
float _omegaMax; float _omegaMax;
/** PLL gain. */
float _pllGain; float _pllGain;
/** The last decoded bits (needed for transition mode). */
uint8_t _lastBits; uint8_t _lastBits;
/** Output buffer. */
Buffer<uint8_t> _buffer; Buffer<uint8_t> _buffer;
}; };
/** Trivial node to dump a bit-stream to a std::ostream.
* @ingroup sinks */
class BitDump : public Sink<uint8_t> class BitDump : public Sink<uint8_t>
{ {
public: public:
void config(const Config &src_cfg) {} /** Constructor.
void process(const Buffer<uint8_t> &buffer, bool allow_overwrite) { * @param stream Specifies the output stream. */
for (size_t i=0; i<buffer.size(); i++) { BitDump(std::ostream &stream);
std::cerr << int(buffer[i]) << " ";
} void config(const Config &src_cfg);
std::cerr << std::endl; 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 "pocsag.hh"
#include "bch31_21.hh" #include "bch31_21.hh"
#include "logger.hh"
using namespace sdr; using namespace sdr;
@ -8,6 +9,9 @@ inline bool is_address(uint32_t word) {
} }
/* ********************************************************************************************* *
* Implementation of POCSAG
* ********************************************************************************************* */
POCSAG::POCSAG() POCSAG::POCSAG()
: Sink<uint8_t>() : Sink<uint8_t>()
{ {
@ -25,6 +29,10 @@ POCSAG::config(const Config &src_cfg) {
throw err; throw err;
} }
LogMessage msg(LOG_DEBUG);
msg << "Config POCSAG node.";
Logger::get().log(msg);
_state = WAIT; _state = WAIT;
_bits = 0; _bits = 0;
} }
@ -43,7 +51,7 @@ POCSAG::process(const Buffer<uint8_t> &buffer, bool allow_overwrite)
uint32_t word = (_bits & 0xffffffff); uint32_t word = (_bits & 0xffffffff);
if ( (0 == pocsag_repair(word)) && (0x7cd215d8 == word)) { if ( (0 == pocsag_repair(word)) && (0x7cd215d8 == word)) {
// init messages // init messages
_reset_all_messages(); _reset_message();
_state = RECEIVE; _bitcount = 0; _slot = 0; _state = RECEIVE; _bitcount = 0; _slot = 0;
} }
} else if (RECEIVE == _state) { } else if (RECEIVE == _state) {
@ -77,7 +85,8 @@ POCSAG::process(const Buffer<uint8_t> &buffer, bool allow_overwrite)
_state = RECEIVE; _slot = 0; _bitcount = 0; _state = RECEIVE; _slot = 0; _bitcount = 0;
} else { } else {
// Otherwise -> end of transmission, wait for next sync // Otherwise -> end of transmission, wait for next sync
_finish_all_messages(); _state = WAIT; _finish_message(); _state = WAIT;
// Process received messages
this->handleMessages(); this->handleMessages();
} }
} }
@ -89,66 +98,157 @@ POCSAG::process(const Buffer<uint8_t> &buffer, bool allow_overwrite)
void void
POCSAG::_process_word(uint32_t word) POCSAG::_process_word(uint32_t word)
{ {
// Check if slot is skipped /*std::cerr << "POCSAG: RX " << std::hex << word
if (0x7A89C197 == word) { // Skip slot << std::dec << " @ " << int(_slot) << std::endl; */
_finish_message(_slot); return;
}
// Check if word is an address word
if (is_address(word)) { if (0x7A89C197 == word) {
_finish_message(_slot); // Skip
_finish_message();
} else if (is_address(word)) {
// If address word
_finish_message();
// Assemble address // 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); uint8_t func = ((word>>11) & 0x03);
_messages[_slot] = Message(addr, func); // init new message
_message = Message(addr, func);
} else { } else {
if (_messages[_slot].isEmpty()) { // on data word
std::cerr << "Oops: Payload w/o address in slot " << int(_slot) if (_message.isEmpty()) {
<< " word: " << std::hex << word << std::dec << std::endl; 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 void
POCSAG::_reset_message(uint8_t slot) { POCSAG::_reset_message() {
_messages[slot] = Message(); _message = Message();
} }
void void
POCSAG::_reset_all_messages() { POCSAG::_finish_message() {
for (uint8_t i=0; i<8; i++) { if (_message.isEmpty()) { return; }
_reset_message(i); _queue.push_back(_message);
} _reset_message();
}
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);
}
} }
void void
POCSAG::handleMessages() { 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. // You may re-implement this virutal method to process the queued messages in _queue.
while (_queue.size()) { while (_queue.size()) {
Message msg = _queue.back(); _queue.pop_back(); Message msg = _queue.back(); _queue.pop_back();
std::cerr << "POCSAG: @" << msg.address() std::cerr << "POCSAG: @" << msg.address()
<< ", F=" << int(msg.function()) << ", F=" << int(msg.function())
<< ", bits=" << msg.bits() << std::endl; << ", 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() POCSAG::Message::Message()
: _address(0), _function(0), _empty(true), _bits(0) : _address(0), _function(0), _empty(true), _bits(0)
@ -181,15 +281,92 @@ POCSAG::Message::operator =(const Message &other) {
void void
POCSAG::Message::addPayload(uint32_t word) { 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; uint32_t mask = 0x40000000;
for (size_t i=0; i<20; i++) { for (int i=19; i>=0; i--) {
// on new byte // on new byte -> add empty byte to payload
if (0 == (_bits % 8)) { _payload.push_back(0x00); } if (0 == (_bits % 8)) { _payload.push_back(0x00); }
// add bit to last byte of payload // 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 // Increment bit counter and update mask
_bits++; mask = (mask>>1); _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 ...) * 1) at least 576 bits of alternating value (1 0 1 0 ...)
* 2) a 32-bit sync word (0x7CD215D8) * 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) * Unused data words are send as 0x7A89C197. Each dataword is either a address word (bit 31 = 0)
* or message word (bit 31 = 1). * 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 */ * @ingroup datanodes */
class POCSAG: public Sink<uint8_t> class POCSAG: public Sink<uint8_t>
{ {
public: public:
/** A posac message. */
class Message { class Message {
public: public:
/** Empty constructor. */
Message(); Message();
/** Constructor from address and function. */
Message(uint32_t addr, uint8_t func); Message(uint32_t addr, uint8_t func);
/** Copy constructor. */
Message(const Message &other); Message(const Message &other);
/** Assignment operator. */
Message &operator=(const Message &other); Message &operator=(const Message &other);
/** Retruns @c true if the message is empty (has no address). */
inline bool isEmpty() const { return _empty; } inline bool isEmpty() const { return _empty; }
/** Returns the address of the message. */
inline uint32_t address() const { return _address; } inline uint32_t address() const { return _address; }
/** Returns the function of the message. */
inline uint8_t function() const { return _function; } inline uint8_t function() const { return _function; }
/** Returns the number of data bits. */
inline uint32_t bits() const { return _bits; } inline uint32_t bits() const { return _bits; }
/** Adds some payload from the given POGSAC word. */
void addPayload(uint32_t 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: protected:
/** The address of the message. */
uint32_t _address; uint32_t _address;
/** The function of the message. */
uint8_t _function; uint8_t _function;
/** If @c true the message is empty. */
bool _empty; bool _empty;
/** The number of payload bits in the message. */
uint32_t _bits; uint32_t _bits;
/** The actual payload. */
std::vector<uint8_t> _payload; std::vector<uint8_t> _payload;
}; };
protected: protected:
/** The possible states of the POGSAC receiver. */
typedef enum { typedef enum {
WAIT, WAIT, ///< Wait for a sync word.
RECEIVE, RECEIVE, ///< Receive data.
CHECK_CONTINUE CHECK_CONTINUE ///< Wait for the sync word for continuation.
} State; } State;
public: public:
/** Constructor. */
POCSAG(); POCSAG();
void config(const Config &src_cfg); void config(const Config &src_cfg);
void process(const Buffer<uint8_t> &buffer, bool allow_overwrite); 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(); virtual void handleMessages();
protected: protected:
/** Process a POGSAC word. */
void _process_word(uint32_t word); void _process_word(uint32_t word);
void _reset_all_messages(); /** Clear the message. */
void _reset_message(uint8_t slot); void _reset_message();
void _finish_all_messages(); /** Add the (non-empty) message to the queue. */
void _finish_message(uint8_t slot); void _finish_message();
protected: protected:
/** The current state. */
State _state; State _state;
/** The last received bits. */
uint64_t _bits; uint64_t _bits;
/** The number of received bits. */
uint8_t _bitcount; uint8_t _bitcount;
/** The current slot. */
uint8_t _slot; uint8_t _slot;
/** The current message. */
Message _messages[8]; Message _message;
/** The completed messages. */
std::list<Message> _queue; 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 "demod.hh"
#include "psk31.hh" #include "psk31.hh"
#include "afsk.hh" #include "fsk.hh"
#include "baudot.hh" #include "baudot.hh"
#include "ax25.hh" #include "ax25.hh"
#include "pocsag.hh"
#include "fftplan.hh" #include "fftplan.hh"

Loading…
Cancel
Save