mirror of
				https://github.com/portapack-mayhem/mayhem-firmware.git
				synced 2025-11-04 07:59:43 +00:00 
			
		
		
		
	Yet another POCSAG bugfix (multi-batch messages are not cut anymore)
Added BCH ECC functions for checking, error correction and encoding
This commit is contained in:
		@@ -135,14 +135,15 @@ set(CPPSRC
 | 
			
		||||
	debounce.cpp
 | 
			
		||||
	touch.cpp
 | 
			
		||||
	touch_adc.cpp
 | 
			
		||||
	encoder.cpp
 | 
			
		||||
	audio.cpp
 | 
			
		||||
	adsb.cpp
 | 
			
		||||
	afsk.cpp
 | 
			
		||||
	audio.cpp
 | 
			
		||||
	${COMMON}/bch_code.cpp
 | 
			
		||||
	bht.cpp
 | 
			
		||||
	ctcss.cpp
 | 
			
		||||
	rds.cpp
 | 
			
		||||
	encoder.cpp
 | 
			
		||||
	freqman.cpp
 | 
			
		||||
	rds.cpp
 | 
			
		||||
	${COMMON}/lcd_ili9341.cpp
 | 
			
		||||
	${COMMON}/ui.cpp
 | 
			
		||||
	${COMMON}/ui_text.cpp
 | 
			
		||||
 
 | 
			
		||||
@@ -2030,6 +2030,33 @@ __/common/ais_packet.cpp.s:
 | 
			
		||||
	cd /home/furrtek/portapack-hackrf && $(MAKE) -f firmware/application/CMakeFiles/application.elf.dir/build.make firmware/application/CMakeFiles/application.elf.dir/__/common/ais_packet.cpp.s
 | 
			
		||||
.PHONY : __/common/ais_packet.cpp.s
 | 
			
		||||
 | 
			
		||||
__/common/bch_code.obj: __/common/bch_code.cpp.obj
 | 
			
		||||
 | 
			
		||||
.PHONY : __/common/bch_code.obj
 | 
			
		||||
 | 
			
		||||
# target to build an object file
 | 
			
		||||
__/common/bch_code.cpp.obj:
 | 
			
		||||
	cd /home/furrtek/portapack-hackrf && $(MAKE) -f firmware/application/CMakeFiles/application.elf.dir/build.make firmware/application/CMakeFiles/application.elf.dir/__/common/bch_code.cpp.obj
 | 
			
		||||
.PHONY : __/common/bch_code.cpp.obj
 | 
			
		||||
 | 
			
		||||
__/common/bch_code.i: __/common/bch_code.cpp.i
 | 
			
		||||
 | 
			
		||||
.PHONY : __/common/bch_code.i
 | 
			
		||||
 | 
			
		||||
# target to preprocess a source file
 | 
			
		||||
__/common/bch_code.cpp.i:
 | 
			
		||||
	cd /home/furrtek/portapack-hackrf && $(MAKE) -f firmware/application/CMakeFiles/application.elf.dir/build.make firmware/application/CMakeFiles/application.elf.dir/__/common/bch_code.cpp.i
 | 
			
		||||
.PHONY : __/common/bch_code.cpp.i
 | 
			
		||||
 | 
			
		||||
__/common/bch_code.s: __/common/bch_code.cpp.s
 | 
			
		||||
 | 
			
		||||
.PHONY : __/common/bch_code.s
 | 
			
		||||
 | 
			
		||||
# target to generate assembly for a file
 | 
			
		||||
__/common/bch_code.cpp.s:
 | 
			
		||||
	cd /home/furrtek/portapack-hackrf && $(MAKE) -f firmware/application/CMakeFiles/application.elf.dir/build.make firmware/application/CMakeFiles/application.elf.dir/__/common/bch_code.cpp.s
 | 
			
		||||
.PHONY : __/common/bch_code.cpp.s
 | 
			
		||||
 | 
			
		||||
__/common/buffer.obj: __/common/buffer.cpp.obj
 | 
			
		||||
 | 
			
		||||
.PHONY : __/common/buffer.obj
 | 
			
		||||
@@ -5544,6 +5571,9 @@ help:
 | 
			
		||||
	@echo "... __/common/ais_packet.obj"
 | 
			
		||||
	@echo "... __/common/ais_packet.i"
 | 
			
		||||
	@echo "... __/common/ais_packet.s"
 | 
			
		||||
	@echo "... __/common/bch_code.obj"
 | 
			
		||||
	@echo "... __/common/bch_code.i"
 | 
			
		||||
	@echo "... __/common/bch_code.s"
 | 
			
		||||
	@echo "... __/common/buffer.obj"
 | 
			
		||||
	@echo "... __/common/buffer.i"
 | 
			
		||||
	@echo "... __/common/buffer.s"
 | 
			
		||||
 
 | 
			
		||||
@@ -36,7 +36,7 @@ using namespace pocsag;
 | 
			
		||||
#include "utility.hpp"
 | 
			
		||||
 | 
			
		||||
void POCSAGLogger::on_packet(const pocsag::POCSAGPacket& packet, const uint32_t frequency) {
 | 
			
		||||
	std::string entry = pocsag::bitrate_str(packet.bitrate()) + " " + to_string_dec_uint(frequency) + "Hz ";
 | 
			
		||||
	std::string entry = to_string_dec_uint(frequency) + "Hz " + pocsag::bitrate_str(packet.bitrate()) + " RAW: ";
 | 
			
		||||
	
 | 
			
		||||
	// Raw hex dump of all the codewords
 | 
			
		||||
	for (size_t c = 0; c < 16; c++)
 | 
			
		||||
@@ -47,20 +47,15 @@ void POCSAGLogger::on_packet(const pocsag::POCSAGPacket& packet, const uint32_t
 | 
			
		||||
 | 
			
		||||
void POCSAGLogger::on_decoded(
 | 
			
		||||
	const pocsag::POCSAGPacket& packet,
 | 
			
		||||
	const std::string info,
 | 
			
		||||
	const std::string text) {
 | 
			
		||||
	
 | 
			
		||||
	// Decoded address and message
 | 
			
		||||
	log_file.write_entry(packet.timestamp(), info);
 | 
			
		||||
	log_file.write_entry(packet.timestamp(), text);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
namespace ui {
 | 
			
		||||
 | 
			
		||||
void POCSAGAppView::update_freq(rf::Frequency f) {
 | 
			
		||||
	char finalstr[10] = { 0 };
 | 
			
		||||
	
 | 
			
		||||
	options_freq.set_selected_index(0);
 | 
			
		||||
	options_freq.set_selected_index(0);		// "Entered"
 | 
			
		||||
	set_target_frequency(f);
 | 
			
		||||
	
 | 
			
		||||
	portapack::persistent_memory::set_tuned_frequency(f);		// Maybe not ?
 | 
			
		||||
@@ -68,14 +63,11 @@ void POCSAGAppView::update_freq(rf::Frequency f) {
 | 
			
		||||
	auto mhz = to_string_dec_int(f / 1000000, 4);
 | 
			
		||||
	auto hz100 = to_string_dec_int((f / 100) % 10000, 4, '0');
 | 
			
		||||
 | 
			
		||||
	strcat(finalstr, mhz.c_str());
 | 
			
		||||
	strcat(finalstr, ".");
 | 
			
		||||
	strcat(finalstr, hz100.c_str());
 | 
			
		||||
 | 
			
		||||
	this->button_setfreq.set_text(finalstr);
 | 
			
		||||
	this->button_setfreq.set_text(mhz + "." + hz100);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
POCSAGAppView::POCSAGAppView(NavigationView& nav) {
 | 
			
		||||
	
 | 
			
		||||
	baseband::run_image(portapack::spi_flash::image_tag_pocsag);
 | 
			
		||||
 | 
			
		||||
	add_children({
 | 
			
		||||
@@ -91,15 +83,19 @@ POCSAGAppView::POCSAGAppView(NavigationView& nav) {
 | 
			
		||||
		&console
 | 
			
		||||
	});
 | 
			
		||||
	
 | 
			
		||||
	receiver_model.set_sampling_rate(3072000);
 | 
			
		||||
	receiver_model.set_baseband_bandwidth(1750000);
 | 
			
		||||
	receiver_model.enable();
 | 
			
		||||
	
 | 
			
		||||
	check_log.set_value(logging);
 | 
			
		||||
	check_log.on_select = [this](Checkbox&, bool v) {
 | 
			
		||||
		logging = v;
 | 
			
		||||
	};
 | 
			
		||||
	
 | 
			
		||||
	options_bitrate.set_selected_index(1);	// 1200bps
 | 
			
		||||
	options_bitrate.on_change = [this](size_t, OptionsField::value_t v) {
 | 
			
		||||
		this->on_bitrate_changed(v);
 | 
			
		||||
	};
 | 
			
		||||
	options_bitrate.set_selected_index(1);	// 1200bps
 | 
			
		||||
 | 
			
		||||
	options_freq.on_change = [this](size_t, OptionsField::value_t v) {
 | 
			
		||||
		this->on_band_changed(v);
 | 
			
		||||
@@ -116,19 +112,13 @@ POCSAGAppView::POCSAGAppView(NavigationView& nav) {
 | 
			
		||||
	logger = std::make_unique<POCSAGLogger>();
 | 
			
		||||
	if (logger) logger->append("pocsag.txt");
 | 
			
		||||
	
 | 
			
		||||
	receiver_model.set_sampling_rate(3072000);
 | 
			
		||||
	receiver_model.set_baseband_bandwidth(1750000);
 | 
			
		||||
	receiver_model.enable();
 | 
			
		||||
	
 | 
			
		||||
	const auto new_volume = volume_t::decibel(60 - 99) + audio::headphone::volume_range().max;
 | 
			
		||||
	/*const auto new_volume = volume_t::decibel(60 - 99) + audio::headphone::volume_range().max;
 | 
			
		||||
	receiver_model.set_headphone_volume(new_volume);
 | 
			
		||||
	audio::output::start();
 | 
			
		||||
	
 | 
			
		||||
	baseband::set_pocsag(pocsag::BitRate::FSK1200);
 | 
			
		||||
	audio::output::start();*/
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
POCSAGAppView::~POCSAGAppView() {
 | 
			
		||||
	audio::output::stop();
 | 
			
		||||
	//audio::output::stop();
 | 
			
		||||
	receiver_model.disable();
 | 
			
		||||
	baseband::shutdown();
 | 
			
		||||
}
 | 
			
		||||
@@ -145,8 +135,9 @@ void POCSAGAppView::set_parent_rect(const Rect new_parent_rect) {
 | 
			
		||||
void POCSAGAppView::on_packet(const POCSAGPacketMessage * message) {
 | 
			
		||||
	std::string alphanum_text = "";
 | 
			
		||||
	
 | 
			
		||||
	// Log raw data
 | 
			
		||||
	if (logger && logging) logger->on_packet(message->packet, target_frequency());
 | 
			
		||||
	// Log raw data whatever it contains
 | 
			
		||||
	if (logger && logging)
 | 
			
		||||
		logger->on_packet(message->packet, target_frequency());
 | 
			
		||||
	
 | 
			
		||||
	if (message->packet.flag() != NORMAL) {
 | 
			
		||||
		console.writeln(
 | 
			
		||||
@@ -155,45 +146,47 @@ void POCSAGAppView::on_packet(const POCSAGPacketMessage * message) {
 | 
			
		||||
		);
 | 
			
		||||
	}
 | 
			
		||||
	
 | 
			
		||||
	bool result = decode_batch(message->packet, &pocsag_state);
 | 
			
		||||
	bool result = pocsag_decode_batch(message->packet, &pocsag_state);
 | 
			
		||||
 | 
			
		||||
	if (result) {
 | 
			
		||||
		std::string console_info;
 | 
			
		||||
		
 | 
			
		||||
		console_info = to_string_time(message->packet.timestamp()) + " ";
 | 
			
		||||
		console_info += pocsag::bitrate_str(message->packet.bitrate());
 | 
			
		||||
		console_info += " ADDR:" + to_string_dec_uint(pocsag_state.address);
 | 
			
		||||
		console_info += " F" + to_string_dec_uint(pocsag_state.function);
 | 
			
		||||
		
 | 
			
		||||
		if (pocsag_state.out_type == ADDRESS) {
 | 
			
		||||
			// Address only
 | 
			
		||||
			console_info = to_string_time(message->packet.timestamp()) + " ";
 | 
			
		||||
			console_info += pocsag::bitrate_str(message->packet.bitrate()) + " ";
 | 
			
		||||
			console_info += "ADDR:" + to_string_dec_uint(pocsag_state.address);
 | 
			
		||||
			console_info += " F" + to_string_dec_uint(pocsag_state.function);
 | 
			
		||||
			
 | 
			
		||||
			console.writeln(console_info);
 | 
			
		||||
			
 | 
			
		||||
			if (logger) logger->on_decoded(message->packet, console_info, pocsag_state.output);
 | 
			
		||||
			if (logger && logging)
 | 
			
		||||
				logger->on_decoded(message->packet, console_info);
 | 
			
		||||
			
 | 
			
		||||
			last_address = pocsag_state.address;
 | 
			
		||||
		} else if (pocsag_state.out_type == MESSAGE) {
 | 
			
		||||
			if (pocsag_state.address != last_address) {
 | 
			
		||||
				// New message
 | 
			
		||||
				console_info = to_string_time(message->packet.timestamp()) + " ";
 | 
			
		||||
				console_info += pocsag::bitrate_str( message->packet.bitrate()) + " ";
 | 
			
		||||
				console_info += "ADDR:" + to_string_dec_uint(pocsag_state.address);
 | 
			
		||||
				console_info += " F" + to_string_dec_uint(pocsag_state.function);
 | 
			
		||||
				
 | 
			
		||||
				console.writeln(console_info);
 | 
			
		||||
				
 | 
			
		||||
				console.writeln("Alpha:" + pocsag_state.output);
 | 
			
		||||
				
 | 
			
		||||
				if (logger) logger->on_decoded(message->packet, console_info, pocsag_state.output);
 | 
			
		||||
				if (logger && logging) {
 | 
			
		||||
					logger->on_decoded(message->packet, console_info);
 | 
			
		||||
					logger->on_decoded(message->packet, pocsag_state.output);
 | 
			
		||||
				}
 | 
			
		||||
				
 | 
			
		||||
				last_address = pocsag_state.address;
 | 
			
		||||
			} else {
 | 
			
		||||
				// Message continues...
 | 
			
		||||
				console.writeln(pocsag_state.output);
 | 
			
		||||
				
 | 
			
		||||
				if (logger && logging)
 | 
			
		||||
					logger->on_decoded(message->packet, pocsag_state.output);
 | 
			
		||||
			}
 | 
			
		||||
		} else if (pocsag_state.out_type == EMPTY)
 | 
			
		||||
			console.writeln("-");
 | 
			
		||||
		
 | 
			
		||||
		console.write(pocsag_state.output);
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -27,12 +27,13 @@
 | 
			
		||||
#include "ui_navigation.hpp"
 | 
			
		||||
#include "ui_receiver.hpp"
 | 
			
		||||
#include "ui_rssi.hpp"
 | 
			
		||||
#include "ui_channel.hpp"
 | 
			
		||||
//#include "ui_channel.hpp"
 | 
			
		||||
 | 
			
		||||
#include "event_m0.hpp"
 | 
			
		||||
 | 
			
		||||
#include "log_file.hpp"
 | 
			
		||||
 | 
			
		||||
#include "bch_code.hpp"
 | 
			
		||||
#include "pocsag.hpp"
 | 
			
		||||
#include "pocsag_packet.hpp"
 | 
			
		||||
 | 
			
		||||
@@ -43,7 +44,7 @@ public:
 | 
			
		||||
	}
 | 
			
		||||
	
 | 
			
		||||
	void on_packet(const pocsag::POCSAGPacket& packet, const uint32_t frequency);
 | 
			
		||||
	void on_decoded(const pocsag::POCSAGPacket& packet,	const std::string info,	const std::string text);
 | 
			
		||||
	void on_decoded(const pocsag::POCSAGPacket& packet,	const std::string text);
 | 
			
		||||
 | 
			
		||||
private:
 | 
			
		||||
	LogFile log_file { };
 | 
			
		||||
@@ -69,7 +70,12 @@ private:
 | 
			
		||||
	static constexpr uint32_t sampling_rate = 3072000;
 | 
			
		||||
	//static constexpr uint32_t baseband_bandwidth = 1750000;
 | 
			
		||||
 | 
			
		||||
	bool logging { false };
 | 
			
		||||
	BCHCode BCH_code {
 | 
			
		||||
		{ 1, 0, 1, 0, 0, 1 },
 | 
			
		||||
		5, 31, 21, 2
 | 
			
		||||
	};
 | 
			
		||||
 | 
			
		||||
	bool logging { true };
 | 
			
		||||
	uint32_t last_address = 0xFFFFFFFF;
 | 
			
		||||
	pocsag::POCSAGState pocsag_state { };
 | 
			
		||||
	
 | 
			
		||||
 
 | 
			
		||||
@@ -47,7 +47,7 @@ void POCSAGProcessor::execute(const buffer_c8_t& buffer) {
 | 
			
		||||
		const int32_t audio_sample = __SSAT(sample_int, 16);
 | 
			
		||||
		
 | 
			
		||||
		slicer_sr <<= 1;
 | 
			
		||||
		slicer_sr |= (audio_sample < 0);
 | 
			
		||||
		slicer_sr |= (audio_sample < 0);		// Do we need hysteresis ?
 | 
			
		||||
 | 
			
		||||
		// Detect transitions to adjust clock
 | 
			
		||||
		if ((slicer_sr ^ (slicer_sr >> 1)) & 1) {
 | 
			
		||||
@@ -79,19 +79,18 @@ void POCSAGProcessor::execute(const buffer_c8_t& buffer) {
 | 
			
		||||
					if (sync_timeout < POCSAG_TIMEOUT) {
 | 
			
		||||
						sync_timeout++;
 | 
			
		||||
 | 
			
		||||
						if (rx_data == POCSAG_SYNC) {
 | 
			
		||||
						if (rx_data == POCSAG_SYNCWORD) {
 | 
			
		||||
							packet.clear();
 | 
			
		||||
							codeword_count = 0;
 | 
			
		||||
							rx_bit = 0;
 | 
			
		||||
							msg_timeout = 0;
 | 
			
		||||
							rx_state = SYNC;
 | 
			
		||||
						} /*else if (rx_data == POCSAG_IDLE) {
 | 
			
		||||
							rx_state = WAITING;
 | 
			
		||||
						}*/
 | 
			
		||||
						}
 | 
			
		||||
						
 | 
			
		||||
					} else {
 | 
			
		||||
						rx_state = WAITING;		// Timed out: abort
 | 
			
		||||
						push_packet(pocsag::PacketFlag::TOO_LONG);	// DEBUG
 | 
			
		||||
						// Timeout here is normal (end of message)
 | 
			
		||||
						rx_state = WAITING;
 | 
			
		||||
						//push_packet(pocsag::PacketFlag::TIMED_OUT);
 | 
			
		||||
					}
 | 
			
		||||
					break;
 | 
			
		||||
				
 | 
			
		||||
@@ -113,12 +112,12 @@ void POCSAGProcessor::execute(const buffer_c8_t& buffer) {
 | 
			
		||||
								codeword_count++;
 | 
			
		||||
							} else {
 | 
			
		||||
								push_packet(pocsag::PacketFlag::NORMAL);
 | 
			
		||||
								rx_state = WAITING;
 | 
			
		||||
								rx_state = PREAMBLE;
 | 
			
		||||
								sync_timeout = 0;
 | 
			
		||||
							}
 | 
			
		||||
						}
 | 
			
		||||
					} else {
 | 
			
		||||
						// Timed out (no end of message received)
 | 
			
		||||
						packet.set(0, codeword_count);	// DEBUG
 | 
			
		||||
						packet.set(0, codeword_count);	// Replace first codeword with count, for debug
 | 
			
		||||
						push_packet(pocsag::PacketFlag::TIMED_OUT);
 | 
			
		||||
						rx_state = WAITING;
 | 
			
		||||
					}
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										364
									
								
								firmware/common/bch_code.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										364
									
								
								firmware/common/bch_code.cpp
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,364 @@
 | 
			
		||||
/*
 | 
			
		||||
 * Copyright (C) 2015 Craig Shelley (craig@microtron.org.uk)
 | 
			
		||||
 * Copyright (C) 2016 Furrtek
 | 
			
		||||
 *
 | 
			
		||||
 * BCH Encoder/Decoder - Adapted from GNURadio
 | 
			
		||||
 * 
 | 
			
		||||
 * This file is part of PortaPack.
 | 
			
		||||
 *
 | 
			
		||||
 * This program is free software; you can redistribute it and/or modify
 | 
			
		||||
 * it under the terms of the GNU General Public License as published by
 | 
			
		||||
 * the Free Software Foundation; either version 2, or (at your option)
 | 
			
		||||
 * any later version.
 | 
			
		||||
 *
 | 
			
		||||
 * This program is distributed in the hope that it will be useful,
 | 
			
		||||
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
			
		||||
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | 
			
		||||
 * GNU General Public License for more details.
 | 
			
		||||
 *
 | 
			
		||||
 * You should have received a copy of the GNU General Public License
 | 
			
		||||
 * along with this program; see the file COPYING.  If not, write to
 | 
			
		||||
 * the Free Software Foundation, Inc., 51 Franklin Street,
 | 
			
		||||
 * Boston, MA 02110-1301, USA.
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#include <math.h>
 | 
			
		||||
#include <stdlib.h>
 | 
			
		||||
#include <ch.h>
 | 
			
		||||
 | 
			
		||||
#include "bch_code.hpp"
 | 
			
		||||
 | 
			
		||||
void BCHCode::generate_gf() {
 | 
			
		||||
	/*
 | 
			
		||||
	* generate GF(2**m) from the irreducible polynomial p(X) in p[0]..p[m]
 | 
			
		||||
	* lookup tables:  index->polynomial form   alpha_to[] contains j=alpha**i;
 | 
			
		||||
	* polynomial form -> index form  index_of[j=alpha**i] = i alpha=2 is the
 | 
			
		||||
	* primitive element of GF(2**m) 
 | 
			
		||||
	*/
 | 
			
		||||
	
 | 
			
		||||
	int i, mask;
 | 
			
		||||
	mask = 1;
 | 
			
		||||
	alpha_to[m] = 0;
 | 
			
		||||
	
 | 
			
		||||
	for (i = 0; i < m; i++) 
 | 
			
		||||
	{
 | 
			
		||||
		alpha_to[i] = mask;
 | 
			
		||||
		
 | 
			
		||||
		index_of[alpha_to[i]] = i;
 | 
			
		||||
		
 | 
			
		||||
		if (p[i] != 0)
 | 
			
		||||
			alpha_to[m] ^= mask;
 | 
			
		||||
		
 | 
			
		||||
		mask <<= 1;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	index_of[alpha_to[m]] = m;
 | 
			
		||||
	
 | 
			
		||||
	mask >>= 1;
 | 
			
		||||
	
 | 
			
		||||
	for (i = m + 1; i < n; i++) 
 | 
			
		||||
	{
 | 
			
		||||
		if (alpha_to[i - 1] >= mask)
 | 
			
		||||
		  alpha_to[i] = alpha_to[m] ^ ((alpha_to[i - 1] ^ mask) << 1);
 | 
			
		||||
		else
 | 
			
		||||
		  alpha_to[i] = alpha_to[i - 1] << 1;
 | 
			
		||||
	
 | 
			
		||||
		index_of[alpha_to[i]] = i;
 | 
			
		||||
	}
 | 
			
		||||
	
 | 
			
		||||
	index_of[0] = -1;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
void BCHCode::gen_poly() {
 | 
			
		||||
	/* 
 | 
			
		||||
	* Compute generator polynomial of BCH code of length = 31, redundancy = 10
 | 
			
		||||
	* (OK, this is not very efficient, but we only do it once, right? :)
 | 
			
		||||
	*/
 | 
			
		||||
 | 
			
		||||
	int ii, jj, ll, kaux;
 | 
			
		||||
	int test, aux, nocycles, root, noterms, rdncy;
 | 
			
		||||
	int cycle[15][6], size[15], min[11], zeros[11];
 | 
			
		||||
	
 | 
			
		||||
	// Generate cycle sets modulo 31 
 | 
			
		||||
	cycle[0][0] = 0; size[0] = 1;
 | 
			
		||||
	cycle[1][0] = 1; size[1] = 1;
 | 
			
		||||
	jj = 1;			// cycle set index 
 | 
			
		||||
	
 | 
			
		||||
	do 
 | 
			
		||||
	{
 | 
			
		||||
		// Generate the jj-th cycle set 
 | 
			
		||||
		ii = 0;
 | 
			
		||||
		do 
 | 
			
		||||
		{
 | 
			
		||||
			ii++;
 | 
			
		||||
			cycle[jj][ii] = (cycle[jj][ii - 1] * 2) % n;
 | 
			
		||||
			size[jj]++;
 | 
			
		||||
			aux = (cycle[jj][ii] * 2) % n;
 | 
			
		||||
 | 
			
		||||
		} while (aux != cycle[jj][0]);
 | 
			
		||||
		
 | 
			
		||||
		// Next cycle set representative 
 | 
			
		||||
		ll = 0;
 | 
			
		||||
		do 
 | 
			
		||||
		{
 | 
			
		||||
			ll++;
 | 
			
		||||
			test = 0;
 | 
			
		||||
			for (ii = 1; ((ii <= jj) && (!test)); ii++)	
 | 
			
		||||
			// Examine previous cycle sets 
 | 
			
		||||
			  for (kaux = 0; ((kaux < size[ii]) && (!test)); kaux++)
 | 
			
		||||
					if (ll == cycle[ii][kaux])
 | 
			
		||||
						test = 1;
 | 
			
		||||
		} 
 | 
			
		||||
		while ((test) && (ll < (n - 1)));
 | 
			
		||||
		
 | 
			
		||||
		if (!(test)) 
 | 
			
		||||
		{
 | 
			
		||||
			jj++;	// next cycle set index 
 | 
			
		||||
			cycle[jj][0] = ll;
 | 
			
		||||
			size[jj] = 1;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
	} while (ll < (n - 1));
 | 
			
		||||
	
 | 
			
		||||
	nocycles = jj;		// number of cycle sets modulo n 
 | 
			
		||||
	// Search for roots 1, 2, ..., d-1 in cycle sets 
 | 
			
		||||
	
 | 
			
		||||
	kaux = 0;
 | 
			
		||||
	rdncy = 0;
 | 
			
		||||
	
 | 
			
		||||
	for (ii = 1; ii <= nocycles; ii++) 
 | 
			
		||||
	{
 | 
			
		||||
		min[kaux] = 0;
 | 
			
		||||
		
 | 
			
		||||
		for (jj = 0; jj < size[ii]; jj++)
 | 
			
		||||
			for (root = 1; root < d; root++)
 | 
			
		||||
				if (root == cycle[ii][jj])
 | 
			
		||||
					min[kaux] = ii;
 | 
			
		||||
		
 | 
			
		||||
		if (min[kaux]) 
 | 
			
		||||
		{
 | 
			
		||||
			rdncy += size[min[kaux]];
 | 
			
		||||
			kaux++;
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	noterms = kaux;
 | 
			
		||||
	kaux = 1;
 | 
			
		||||
	
 | 
			
		||||
	for (ii = 0; ii < noterms; ii++)
 | 
			
		||||
		for (jj = 0; jj < size[min[ii]]; jj++) 
 | 
			
		||||
		{
 | 
			
		||||
			zeros[kaux] = cycle[min[ii]][jj];
 | 
			
		||||
			kaux++;
 | 
			
		||||
		}
 | 
			
		||||
	
 | 
			
		||||
	// Compute generator polynomial 
 | 
			
		||||
	g[0] = alpha_to[zeros[1]];
 | 
			
		||||
	g[1] = 1;		// g(x) = (X + zeros[1]) initially 
 | 
			
		||||
	
 | 
			
		||||
	for (ii = 2; ii <= rdncy; ii++) 
 | 
			
		||||
	{
 | 
			
		||||
	  g[ii] = 1;
 | 
			
		||||
	  for (jj = ii - 1; jj > 0; jj--)
 | 
			
		||||
	    if (g[jj] != 0)
 | 
			
		||||
	      g[jj] = g[jj - 1] ^ alpha_to[(index_of[g[jj]] + zeros[ii]) % n];
 | 
			
		||||
	    else
 | 
			
		||||
	      g[jj] = g[jj - 1];
 | 
			
		||||
	  
 | 
			
		||||
		g[0] = alpha_to[(index_of[g[0]] + zeros[ii]) % n];
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int * BCHCode::encode(int data[]) {
 | 
			
		||||
	// Calculate redundant bits bb[]
 | 
			
		||||
 | 
			
		||||
	int    h, i, j=0, start=0, end=(n-k);	// 10
 | 
			
		||||
	int Mr[31];
 | 
			
		||||
	
 | 
			
		||||
	if (!valid) return nullptr;
 | 
			
		||||
	
 | 
			
		||||
	for (i = 0; i < n; i++) {
 | 
			
		||||
		Mr[i] = 0;
 | 
			
		||||
	}
 | 
			
		||||
	
 | 
			
		||||
	for (h = 0; h < k; ++h)
 | 
			
		||||
		Mr[h] = data[h];
 | 
			
		||||
	
 | 
			
		||||
	while (end < n)
 | 
			
		||||
	{
 | 
			
		||||
		for (i=end; i>start-2; --i)
 | 
			
		||||
		{
 | 
			
		||||
			if (Mr[start] != 0)
 | 
			
		||||
			{
 | 
			
		||||
				Mr[i]^=g[j];
 | 
			
		||||
				++j;
 | 
			
		||||
			}
 | 
			
		||||
			else
 | 
			
		||||
			{
 | 
			
		||||
				++start;
 | 
			
		||||
				j = 0;
 | 
			
		||||
				end = start+(n-k);
 | 
			
		||||
				break;
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	
 | 
			
		||||
	j = 0;
 | 
			
		||||
	for (i = start; i<end; ++i) {
 | 
			
		||||
		bb[j]=Mr[i];
 | 
			
		||||
		++j;
 | 
			
		||||
	}
 | 
			
		||||
	
 | 
			
		||||
	return bb;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
int BCHCode::decode(int recd[]) {
 | 
			
		||||
	// We do not need the Berlekamp algorithm to decode.
 | 
			
		||||
	// We solve before hand two equations in two variables.
 | 
			
		||||
 | 
			
		||||
	int i, j, q;
 | 
			
		||||
	int elp[3], s[5], s3;
 | 
			
		||||
	int count = 0, syn_error = 0;
 | 
			
		||||
	int loc[3], reg[3];
 | 
			
		||||
	int	aux;
 | 
			
		||||
	int retval=0;
 | 
			
		||||
	
 | 
			
		||||
	if (!valid) return 0;
 | 
			
		||||
	
 | 
			
		||||
	for (i = 1; i <= 4; i++) {
 | 
			
		||||
		s[i] = 0;
 | 
			
		||||
		for (j = 0; j < n; j++) {
 | 
			
		||||
			if (recd[j] != 0) {
 | 
			
		||||
				s[i] ^= alpha_to[(i * j) % n];
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
		if (s[i] != 0) {
 | 
			
		||||
			syn_error = 1;		// set flag if non-zero syndrome
 | 
			
		||||
		}
 | 
			
		||||
		/* NOTE: If only error detection is needed,
 | 
			
		||||
		 * then exit the program here...
 | 
			
		||||
		 */
 | 
			
		||||
		// Convert syndrome from polynomial form to index form
 | 
			
		||||
		s[i] = index_of[s[i]];
 | 
			
		||||
	};
 | 
			
		||||
	
 | 
			
		||||
	if (syn_error) {			// If there are errors, try to correct them
 | 
			
		||||
		if (s[1] != -1) {
 | 
			
		||||
			s3 = (s[1] * 3) % n;
 | 
			
		||||
			if ( s[3] == s3 ) { 	// Was it a single error ?
 | 
			
		||||
				//printf("One error at %d\n", s[1]);
 | 
			
		||||
				recd[s[1]] ^= 1; 	// Yes: Correct it
 | 
			
		||||
			} else {
 | 
			
		||||
				/* Assume two errors occurred and solve
 | 
			
		||||
				 * for the coefficients of sigma(x), the
 | 
			
		||||
				 * error locator polynomial
 | 
			
		||||
				 */
 | 
			
		||||
				if (s[3] != -1) {
 | 
			
		||||
					aux = alpha_to[s3] ^ alpha_to[s[3]];
 | 
			
		||||
				} else {
 | 
			
		||||
					aux = alpha_to[s3];
 | 
			
		||||
				}
 | 
			
		||||
				elp[0] = 0;
 | 
			
		||||
				elp[1] = (s[2] - index_of[aux] + n) % n;
 | 
			
		||||
				elp[2] = (s[1] - index_of[aux] + n) % n;
 | 
			
		||||
				//printf("sigma(x) = ");
 | 
			
		||||
				//for (i = 0; i <= 2; i++) {
 | 
			
		||||
				//	printf("%3d ", elp[i]);
 | 
			
		||||
				//}
 | 
			
		||||
				//printf("\n");
 | 
			
		||||
				//printf("Roots: ");
 | 
			
		||||
				
 | 
			
		||||
				// Find roots of the error location polynomial
 | 
			
		||||
				for (i = 1; i <= 2; i++) {
 | 
			
		||||
					reg[i] = elp[i];
 | 
			
		||||
				}
 | 
			
		||||
				count = 0;
 | 
			
		||||
				for (i = 1; i <= n; i++) { // Chien search
 | 
			
		||||
					q = 1;
 | 
			
		||||
					for (j = 1; j <= 2; j++) {
 | 
			
		||||
						if (reg[j] != -1) {
 | 
			
		||||
							reg[j] = (reg[j] + j) % n;
 | 
			
		||||
							q ^= alpha_to[reg[j]];
 | 
			
		||||
						}
 | 
			
		||||
					}
 | 
			
		||||
					if (!q) {	// store error location number indices
 | 
			
		||||
						loc[count] = i % n;
 | 
			
		||||
						count++;
 | 
			
		||||
					}
 | 
			
		||||
				}
 | 
			
		||||
				
 | 
			
		||||
				if (count == 2)	{
 | 
			
		||||
					// no. roots = degree of elp hence 2 errors
 | 
			
		||||
					for (i = 0; i < 2; i++)
 | 
			
		||||
						recd[loc[i]] ^= 1;
 | 
			
		||||
				} else {	// Cannot solve: Error detection
 | 
			
		||||
					retval=1;
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
		} else if (s[2] != -1) {	// Error detection
 | 
			
		||||
			retval=1;
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return retval;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * Example usage BCH(31,21,5)
 | 
			
		||||
 *
 | 
			
		||||
 * p[] = coefficients of primitive polynomial used to generate GF(2**5)
 | 
			
		||||
 * m = order of the field GF(2**5) = 5
 | 
			
		||||
 * n = 2**5 - 1 = 31
 | 
			
		||||
 * t = 2 = error correcting capability
 | 
			
		||||
 * d = 2*t + 1 = 5 = designed minimum distance
 | 
			
		||||
 * k = n - deg(g(x)) = 21 = dimension
 | 
			
		||||
 * g[] = coefficients of generator polynomial, g(x) [n - k + 1]=[11]
 | 
			
		||||
 * alpha_to [] = log table of GF(2**5)
 | 
			
		||||
 * index_of[] = antilog table of GF(2**5)
 | 
			
		||||
 * data[] = coefficients of data polynomial, i(x)
 | 
			
		||||
 * bb[] = coefficients of redundancy polynomial ( x**(10) i(x) ) modulo g(x)
 | 
			
		||||
 */
 | 
			
		||||
BCHCode::BCHCode(
 | 
			
		||||
	std::vector<int> p_init, int m, int n, int k, int t
 | 
			
		||||
) : m { m },
 | 
			
		||||
	n { n },
 | 
			
		||||
	k { k },
 | 
			
		||||
	t { t } {
 | 
			
		||||
	size_t i;
 | 
			
		||||
	
 | 
			
		||||
	d = 5;
 | 
			
		||||
	
 | 
			
		||||
	alpha_to = (int *)chHeapAlloc(NULL, sizeof(int) * (n + 1));
 | 
			
		||||
	index_of = (int *)chHeapAlloc(0, sizeof(int) * (n + 1));
 | 
			
		||||
	p = (int *)chHeapAlloc(0, sizeof(int) * (m + 1));
 | 
			
		||||
	g = (int *)chHeapAlloc(0, sizeof(int) * (n - k + 1));
 | 
			
		||||
	bb = (int *)chHeapAlloc(0, sizeof(int) * (n - k + 1));
 | 
			
		||||
	
 | 
			
		||||
	if (alpha_to == NULL ||
 | 
			
		||||
		index_of == NULL ||
 | 
			
		||||
		p == NULL ||
 | 
			
		||||
		g == NULL ||
 | 
			
		||||
		bb == NULL)
 | 
			
		||||
		valid = false;
 | 
			
		||||
	else
 | 
			
		||||
		valid = true;
 | 
			
		||||
	
 | 
			
		||||
	if (valid) {
 | 
			
		||||
		for (i = 0; i < (m + 1); i++) {
 | 
			
		||||
			p[i] = p_init[i];
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		generate_gf();			/* generate the Galois Field GF(2**m) */
 | 
			
		||||
		gen_poly();				/* Compute the generator polynomial of BCH code */
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
BCHCode::~BCHCode() {
 | 
			
		||||
	if (alpha_to != NULL) chHeapFree(alpha_to);
 | 
			
		||||
	if (index_of != NULL) chHeapFree(index_of);
 | 
			
		||||
	if (p != NULL) chHeapFree(p);
 | 
			
		||||
	if (g != NULL) chHeapFree(g);
 | 
			
		||||
	if (bb != NULL) chHeapFree(bb);
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										61
									
								
								firmware/common/bch_code.hpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										61
									
								
								firmware/common/bch_code.hpp
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,61 @@
 | 
			
		||||
/*
 | 
			
		||||
 * Copyright (C) 2015 Craig Shelley (craig@microtron.org.uk)
 | 
			
		||||
 * Copyright (C) 2016 Furrtek
 | 
			
		||||
 *
 | 
			
		||||
 * BCH Encoder/Decoder - Adapted from GNURadio
 | 
			
		||||
 * 
 | 
			
		||||
 * This file is part of PortaPack.
 | 
			
		||||
 *
 | 
			
		||||
 * This program is free software; you can redistribute it and/or modify
 | 
			
		||||
 * it under the terms of the GNU General Public License as published by
 | 
			
		||||
 * the Free Software Foundation; either version 2, or (at your option)
 | 
			
		||||
 * any later version.
 | 
			
		||||
 *
 | 
			
		||||
 * This program is distributed in the hope that it will be useful,
 | 
			
		||||
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
			
		||||
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | 
			
		||||
 * GNU General Public License for more details.
 | 
			
		||||
 *
 | 
			
		||||
 * You should have received a copy of the GNU General Public License
 | 
			
		||||
 * along with this program; see the file COPYING.  If not, write to
 | 
			
		||||
 * the Free Software Foundation, Inc., 51 Franklin Street,
 | 
			
		||||
 * Boston, MA 02110-1301, USA.
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#ifndef __BCHCODE_H__
 | 
			
		||||
#define __BCHCODE_H__
 | 
			
		||||
 | 
			
		||||
#include <vector>
 | 
			
		||||
 | 
			
		||||
class BCHCode {
 | 
			
		||||
public:
 | 
			
		||||
	BCHCode(std::vector<int> p_init, int m, int n, int k, int t);
 | 
			
		||||
	~BCHCode();
 | 
			
		||||
	
 | 
			
		||||
	BCHCode(const BCHCode&) = delete;
 | 
			
		||||
	BCHCode(BCHCode&&) = delete;
 | 
			
		||||
	BCHCode& operator=(const BCHCode&) = delete;
 | 
			
		||||
	BCHCode& operator=(BCHCode&&) = delete;
 | 
			
		||||
 | 
			
		||||
	int * encode(int data[]);
 | 
			
		||||
	int decode(int recd[]);
 | 
			
		||||
	
 | 
			
		||||
private:
 | 
			
		||||
	void gen_poly();
 | 
			
		||||
	void generate_gf();
 | 
			
		||||
	
 | 
			
		||||
	bool valid { false };
 | 
			
		||||
 | 
			
		||||
	int d { };
 | 
			
		||||
	int * p { };			// coefficients of primitive polynomial used to generate GF(2**5)
 | 
			
		||||
	int m { };				// order of the field GF(2**5) = 5
 | 
			
		||||
	int n { };				// 2**5 - 1 = 31
 | 
			
		||||
	int k { };				// n - deg(g(x)) = 21 = dimension
 | 
			
		||||
	int t { };				// 2 = error correcting capability
 | 
			
		||||
	int * alpha_to { };		// log table of GF(2**5)
 | 
			
		||||
	int * index_of { };		// antilog table of GF(2**5)
 | 
			
		||||
	int * g { };			// coefficients of generator polynomial, g(x) [n - k + 1]=[11]
 | 
			
		||||
	int * bb { };			// coefficients of redundancy polynomial ( x**(10) i(x) ) modulo g(x)
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
#endif/*__BCHCODE_H__*/
 | 
			
		||||
@@ -23,13 +23,10 @@
 | 
			
		||||
#include "pocsag.hpp"
 | 
			
		||||
 | 
			
		||||
#include "baseband_api.hpp"
 | 
			
		||||
 | 
			
		||||
#include "portapack.hpp"
 | 
			
		||||
#include "portapack_persistent_memory.hpp"
 | 
			
		||||
using namespace portapack;
 | 
			
		||||
 | 
			
		||||
#include "string_format.hpp"
 | 
			
		||||
 | 
			
		||||
#include "utility.hpp"
 | 
			
		||||
 | 
			
		||||
namespace pocsag {
 | 
			
		||||
@@ -48,12 +45,118 @@ std::string flag_str(PacketFlag packetflag) {
 | 
			
		||||
		case PacketFlag::NORMAL:	return "OK";
 | 
			
		||||
		case PacketFlag::IDLE:		return "IDLE";
 | 
			
		||||
		case PacketFlag::TIMED_OUT:	return "TIMED OUT";
 | 
			
		||||
		case PacketFlag::TOO_LONG:	return "TOO LONG";
 | 
			
		||||
		default:					return "";
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool decode_batch(const POCSAGPacket& batch, POCSAGState * const state) {
 | 
			
		||||
void insert_BCH(BCHCode& BCH_code, uint32_t * codeword) {
 | 
			
		||||
	uint32_t parity = 0;
 | 
			
		||||
	int data[21];
 | 
			
		||||
	int bit;
 | 
			
		||||
	int * bb;
 | 
			
		||||
	size_t c;
 | 
			
		||||
	
 | 
			
		||||
	for (c = 0; c < 21; c++) {
 | 
			
		||||
		bit = (((*codeword) << c) & 0x80000000U) ? 1 : 0;
 | 
			
		||||
		if (bit) parity++;
 | 
			
		||||
		data[c] = bit;
 | 
			
		||||
	}
 | 
			
		||||
	
 | 
			
		||||
	bb = BCH_code.encode(data);
 | 
			
		||||
	
 | 
			
		||||
	// Make sure ECC bits are cleared
 | 
			
		||||
	(*codeword) &= 0xFFFFF801;
 | 
			
		||||
	
 | 
			
		||||
	for (c = 0; c < 10; c++) {
 | 
			
		||||
		bit = bb[c];
 | 
			
		||||
		(*codeword) |= (bit << (10 - c));
 | 
			
		||||
		if (bit) parity++;
 | 
			
		||||
	}
 | 
			
		||||
	
 | 
			
		||||
	// Even parity
 | 
			
		||||
	(*codeword) |= (parity & 1);
 | 
			
		||||
}
 | 
			
		||||
	
 | 
			
		||||
void pocsag_encode(
 | 
			
		||||
	BCHCode& BCH_code, const std::string text, const uint32_t address,
 | 
			
		||||
	std::vector<uint32_t>& codewords) {
 | 
			
		||||
	
 | 
			
		||||
	size_t b, c, address_slot;
 | 
			
		||||
	size_t bit_idx, char_idx = 0;
 | 
			
		||||
	uint32_t codeword;
 | 
			
		||||
	char ascii_char;
 | 
			
		||||
	
 | 
			
		||||
	size_t text_size = text.size();
 | 
			
		||||
	
 | 
			
		||||
	// Preamble
 | 
			
		||||
	for (b = 0; b < (POCSAG_PREAMBLE_LENGTH / 32); b++) {
 | 
			
		||||
		codewords.push_back(0xAAAAAAAA);
 | 
			
		||||
	}
 | 
			
		||||
	
 | 
			
		||||
	// Address
 | 
			
		||||
	codeword = (address & 0x1FFFF8U) << 13;
 | 
			
		||||
	address_slot = (address & 7) * 2;
 | 
			
		||||
	// Function
 | 
			
		||||
	codeword = 3 << 11;
 | 
			
		||||
	
 | 
			
		||||
	insert_BCH(BCH_code, &codeword);
 | 
			
		||||
	
 | 
			
		||||
	// Address batch
 | 
			
		||||
	codewords.push_back(POCSAG_SYNCWORD);
 | 
			
		||||
	for (c = 0; c < 16; c++) {
 | 
			
		||||
		if (c == address_slot)
 | 
			
		||||
			codewords.push_back(codeword);
 | 
			
		||||
		else
 | 
			
		||||
			codewords.push_back(POCSAG_IDLEWORD);
 | 
			
		||||
	}
 | 
			
		||||
	
 | 
			
		||||
	codeword = 0;
 | 
			
		||||
	bit_idx = 20 + 11;
 | 
			
		||||
	
 | 
			
		||||
	// Messages batch(es)
 | 
			
		||||
	do {
 | 
			
		||||
		codewords.push_back(POCSAG_SYNCWORD);
 | 
			
		||||
		
 | 
			
		||||
		for (c = 0; c < 16; c++) {
 | 
			
		||||
			// Fill up 20 bits
 | 
			
		||||
			do {
 | 
			
		||||
				bit_idx -= 7;
 | 
			
		||||
				
 | 
			
		||||
				if (char_idx < text_size)
 | 
			
		||||
					ascii_char = text[char_idx] & 0x7F;
 | 
			
		||||
				else
 | 
			
		||||
					ascii_char = 0;		// Padding
 | 
			
		||||
				
 | 
			
		||||
				// Bottom's up
 | 
			
		||||
				ascii_char = (ascii_char & 0xF0) >> 4 | (ascii_char & 0x0F) << 4;	// *6543210 -> 3210*654
 | 
			
		||||
				ascii_char = (ascii_char & 0xCC) >> 2 | (ascii_char & 0x33) << 2;	// 3210*654 -> 103254*6
 | 
			
		||||
				ascii_char = (ascii_char & 0xAA) >> 2 | (ascii_char & 0x55);		// 103254*6 -> *0123456
 | 
			
		||||
				
 | 
			
		||||
				codeword |= (ascii_char << bit_idx);
 | 
			
		||||
				
 | 
			
		||||
				char_idx++;
 | 
			
		||||
				
 | 
			
		||||
			} while (bit_idx > 11);
 | 
			
		||||
			
 | 
			
		||||
			codeword &= 0x7FFFF800;		// Trim data
 | 
			
		||||
			codeword |= 0x80000000;		// Message type
 | 
			
		||||
			insert_BCH(BCH_code, &codeword);
 | 
			
		||||
			
 | 
			
		||||
			codewords.push_back(codeword);
 | 
			
		||||
			
 | 
			
		||||
			if (bit_idx != 11) {
 | 
			
		||||
				bit_idx = 20 + bit_idx;
 | 
			
		||||
				codeword = ascii_char << bit_idx;
 | 
			
		||||
			} else {
 | 
			
		||||
				bit_idx = 20 + 11;
 | 
			
		||||
				codeword = 0;
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
		
 | 
			
		||||
	} while (char_idx < text_size);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool pocsag_decode_batch(const POCSAGPacket& batch, POCSAGState * const state) {
 | 
			
		||||
	uint32_t codeword;
 | 
			
		||||
	char ascii_char;
 | 
			
		||||
	std::string output_text = "";
 | 
			
		||||
@@ -67,7 +170,7 @@ bool decode_batch(const POCSAGPacket& batch, POCSAGState * const state) {
 | 
			
		||||
		if (!(codeword & 0x80000000U)) {
 | 
			
		||||
			// Address codeword
 | 
			
		||||
			if (state->mode == STATE_CLEAR) {
 | 
			
		||||
				if (codeword != POCSAG_IDLE) {
 | 
			
		||||
				if (codeword != POCSAG_IDLEWORD) {
 | 
			
		||||
					state->function = (codeword >> 11) & 3;
 | 
			
		||||
					state->address = (codeword >> 10) & 0x1FFFF8U;	// 18 MSBs are transmitted
 | 
			
		||||
					state->mode = STATE_HAVE_ADDRESS;
 | 
			
		||||
 
 | 
			
		||||
@@ -23,16 +23,20 @@
 | 
			
		||||
#ifndef __POCSAG_H__
 | 
			
		||||
#define __POCSAG_H__
 | 
			
		||||
 | 
			
		||||
#define POCSAG_PREAMBLE_LENGTH 576
 | 
			
		||||
#define POCSAG_TIMEOUT (576 * 2)	// Preamble length * 2
 | 
			
		||||
#define POCSAG_SYNC 0x7CD215D8
 | 
			
		||||
#define POCSAG_IDLE 0x7A89C197
 | 
			
		||||
#define POCSAG_SYNCWORD 0x7CD215D8
 | 
			
		||||
#define POCSAG_IDLEWORD 0x7A89C197
 | 
			
		||||
#define POCSAG_AUDIO_RATE 24000
 | 
			
		||||
#define POCSAG_BATCH_LENGTH (17 * 32)
 | 
			
		||||
 | 
			
		||||
#include "pocsag_packet.hpp"
 | 
			
		||||
#include "bch_code.hpp"
 | 
			
		||||
 | 
			
		||||
namespace pocsag {
 | 
			
		||||
 | 
			
		||||
// Todo: these enums suck, make a better decode_batch
 | 
			
		||||
 | 
			
		||||
enum Mode : uint32_t {
 | 
			
		||||
	STATE_CLEAR,
 | 
			
		||||
	STATE_HAVE_ADDRESS,
 | 
			
		||||
@@ -58,7 +62,9 @@ struct POCSAGState {
 | 
			
		||||
std::string bitrate_str(BitRate bitrate);
 | 
			
		||||
std::string flag_str(PacketFlag packetflag);
 | 
			
		||||
 | 
			
		||||
bool decode_batch(const POCSAGPacket& batch, POCSAGState * const state);
 | 
			
		||||
void insert_BCH(BCHCode& BCH_code, uint32_t * codeword);
 | 
			
		||||
void pocsag_encode(BCHCode& BCH_code, const std::string text, const uint32_t address, std::vector<uint32_t>& codewords);
 | 
			
		||||
bool pocsag_decode_batch(const POCSAGPacket& batch, POCSAGState * const state);
 | 
			
		||||
 | 
			
		||||
} /* namespace pocsag */
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user