Started writing (copying...) AFSK RX

Encoders: removed bit duration setting (frame duration is more useful)
This commit is contained in:
furrtek 2017-08-29 09:42:04 +01:00
parent cd6a1a7f3f
commit 42439d1885
23 changed files with 453 additions and 423 deletions

View File

@ -163,8 +163,9 @@ set(CPPSRC
ui_about.cpp
ui_adsb_rx.cpp
ui_adsb_tx.cpp
ui_afsk_rx.cpp
ui_alphanum.cpp
ui_aprstx.cpp
ui_aprs_tx.cpp
ui_audio.cpp
ui_baseband_stats_view.cpp
ui_bht_tx.cpp

View File

@ -117,9 +117,16 @@ void set_sstv_data(const uint8_t vis_code, const uint32_t pixel_duration) {
send_message(&message);
}
void set_afsk(const uint32_t bitrate) {
const AFSKRxConfigureMessage message {
bitrate
};
send_message(&message);
}
void set_afsk_data(const uint32_t afsk_samples_per_bit, const uint32_t afsk_phase_inc_mark, const uint32_t afsk_phase_inc_space,
const uint8_t afsk_repeat, const uint32_t afsk_bw, const uint8_t symbol_count) {
const AFSKConfigureMessage message {
const AFSKTxConfigureMessage message {
afsk_samples_per_bit,
afsk_phase_inc_mark,
afsk_phase_inc_space,
@ -131,7 +138,7 @@ void set_afsk_data(const uint32_t afsk_samples_per_bit, const uint32_t afsk_phas
}
void kill_afsk() {
const AFSKConfigureMessage message {
const AFSKTxConfigureMessage message {
0,
0,
0,

View File

@ -67,6 +67,7 @@ void set_pwmrssi(int32_t avg, bool enabled);
void set_afsk_data(const uint32_t afsk_samples_per_bit, const uint32_t afsk_phase_inc_mark, const uint32_t afsk_phase_inc_space,
const uint8_t afsk_repeat, const uint32_t afsk_bw, const uint8_t symbol_count);
void kill_afsk();
void set_afsk(const uint32_t bitrate);
void set_ook_data(const uint32_t stream_length, const uint32_t samples_per_bit, const uint8_t repeat,
const uint32_t pause_symbols);
void set_fsk_data(const uint32_t stream_length, const uint32_t samples_per_bit, const uint32_t shift,

View File

@ -26,10 +26,10 @@
* B(2,4) means:
* Alphabet size = 2 (binary)
* Code length = 4
* The length of the bitstream is (2 ^ 4) - 1 = 15
* The length of the bitstream is (2 ^ 4) + (4 - 1) = 19 bits
* The primitive polynomials come from the de_bruijn_polys[] array (one for each code length)
* The shift register is init with 1 and shifted left each step
* The polynomial is kept on the right, and ANDed with the corresponding shift register bits
* The polynomial is kept on the right, and used as a AND mask applied on the corresponding shift register bits
* The resulting bits are XORed together to produce the new bit pushed in the shift register
*
* 0001 (init)
@ -52,9 +52,28 @@
* AND 1001
* 1000 XOR'd -> 1
* ...
* After 16 steps: (0+) 000111101011001000
* Each of the 16 possible values appears in the sequence:
* -0000-111101011001000 0
* 0-0001-11101011001000 1
* 0000111101011-0010-00 2
* 00-0011-1101011001000 3
* 00001111010110-0100-0 4
* 00001111-0101-1001000 5
* 0000111101-0110-01000 6
* 000-0111-101011001000 7
* 000011110101100-1000- 8
* 000011110101-1001-000 9
* 0000111-1010-11001000 10
* 000011110-1011-001000 11
* 00001111010-1100-1000 12
* 000011-1101-011001000 13
* 00001-1110-1011001000 14
* 0000-1111-01011001000 15
*/
void de_bruijn::init(const uint32_t n) {
size_t de_bruijn::init(const uint32_t n) {
// Cap
if ((n < 3) || (n > 16))
length = 3;
else
@ -62,6 +81,8 @@ void de_bruijn::init(const uint32_t n) {
poly = de_bruijn_polys[length - 3];
shift_register = 1;
return (1U << length) + (length - 1);
}
uint32_t de_bruijn::compute(const uint32_t steps) {

View File

@ -25,7 +25,7 @@
#ifndef __DE_BRUIJN_H__
#define __DE_BRUIJN_H__
// Starts at n = 3
// n from 3 to 16
const uint32_t de_bruijn_polys[14] {
0b0000000000000101,
0b0000000000001001,
@ -45,7 +45,7 @@ const uint32_t de_bruijn_polys[14] {
struct de_bruijn {
public:
void init(const uint32_t n);
size_t init(const uint32_t n);
uint32_t compute(const uint32_t steps);
private:

View File

@ -65,16 +65,6 @@ private:
uint32_t last_address = 0xFFFFFFFF;
pocsag::POCSAGState pocsag_state { };
MessageHandlerRegistration message_handler_packet {
Message::ID::POCSAGPacket,
[this](Message* const p) {
const auto message = static_cast<const POCSAGPacketMessage*>(p);
this->on_packet(message);
}
};
static constexpr ui::Dim header_height = 1 * 16;
RFAmpField field_rf_amp {
{ 13 * 8, 0 * 16 }
};
@ -138,6 +128,14 @@ private:
uint32_t target_frequency() const;
void set_target_frequency(const uint32_t new_value);
MessageHandlerRegistration message_handler_packet {
Message::ID::POCSAGPacket,
[this](Message* const p) {
const auto message = static_cast<const POCSAGPacketMessage*>(p);
this->on_packet(message);
}
};
};
} /* namespace ui */

View File

@ -0,0 +1,92 @@
/*
* Copyright (C) 2014 Jared Boone, ShareBrained Technology, Inc.
* Copyright (C) 2017 Furrtek
*
* 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 "ui_afsk_rx.hpp"
#include "baseband_api.hpp"
//#include "string_format.hpp"
using namespace portapack;
namespace ui {
void AFSKRxView::focus() {
field_frequency.focus();
}
void AFSKRxView::update_freq(rf::Frequency f) {
receiver_model.set_tuning_frequency(f);
}
void AFSKRxView::on_bitrate_changed(const uint32_t new_bitrate) {
baseband::set_afsk(new_bitrate);
}
AFSKRxView::AFSKRxView(NavigationView& nav) {
baseband::run_image(portapack::spi_flash::image_tag_afsk_rx);
add_children({
&rssi,
&channel,
&field_rf_amp,
&field_lna,
&field_vga,
&field_frequency,
&options_bitrate,
&console
});
//receiver_model.set_sampling_rate(3072000);
//receiver_model.set_baseband_bandwidth(1750000);
//receiver_model.enable();
field_frequency.set_value(receiver_model.tuning_frequency());
field_frequency.set_step(receiver_model.frequency_step());
field_frequency.on_change = [this](rf::Frequency f) {
update_freq(f);
};
field_frequency.on_edit = [this, &nav]() {
// TODO: Provide separate modal method/scheme?
auto new_view = nav.push<FrequencyKeypadView>(receiver_model.tuning_frequency());
new_view->on_changed = [this](rf::Frequency f) {
update_freq(f);
field_frequency.set_value(f);
};
};
options_bitrate.on_change = [this](size_t, OptionsField::value_t v) {
on_bitrate_changed(v);
};
options_bitrate.set_selected_index(1); // 1200bps
}
void AFSKRxView::on_data(uint_fast8_t byte) {
std::string str_byte(1, byte);
console.write(str_byte);
}
AFSKRxView::~AFSKRxView() {
receiver_model.disable();
baseband::shutdown();
}
} /* namespace ui */

View File

@ -0,0 +1,96 @@
/*
* Copyright (C) 2014 Jared Boone, ShareBrained Technology, Inc.
* Copyright (C) 2017 Furrtek
*
* 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 __UI_AFSK_RX_H__
#define __UI_AFSK_RX_H__
#include "ui.hpp"
#include "ui_navigation.hpp"
#include "ui_receiver.hpp"
#include "ui_widget.hpp"
#include "utility.hpp"
namespace ui {
class AFSKRxView : public View {
public:
AFSKRxView(NavigationView& nav);
~AFSKRxView();
void focus() override;
std::string title() const override { return "AFSK RX (beta)"; };
private:
void on_data(uint_fast8_t byte);
RFAmpField field_rf_amp {
{ 13 * 8, 0 * 16 }
};
LNAGainField field_lna {
{ 15 * 8, 0 * 16 }
};
VGAGainField field_vga {
{ 18 * 8, 0 * 16 }
};
RSSI rssi {
{ 21 * 8, 0, 6 * 8, 4 },
};
Channel channel {
{ 21 * 8, 5, 6 * 8, 4 },
};
FrequencyField field_frequency {
{ 0 * 8, 0 * 8 },
};
OptionsField options_bitrate {
{ 12 * 8, 21 },
7,
{
{ "600bps ", 600 },
{ "1200bps", 1200 },
{ "2400bps", 2400 }
}
};
Console console {
{ 0, 4 * 16, 240, 240 }
};
void update_freq(rf::Frequency f);
void on_bitrate_changed(const uint32_t new_bitrate);
void on_data_afsk(const AFSKDataMessage& message);
MessageHandlerRegistration message_handler_packet {
Message::ID::AFSKData,
[this](Message* const p) {
const auto message = static_cast<const AFSKDataMessage*>(p);
this->on_data(message->byte);
}
};
};
} /* namespace ui */
#endif/*__UI_AFSK_RX_H__*/

View File

@ -1,140 +0,0 @@
/*
* Copyright (C) 2014 Jared Boone, ShareBrained Technology, Inc.
*
* 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 "ui_afskrx.hpp"
#include "ui_spectrum.hpp"
#include "ui_console.hpp"
#include "event_m0.hpp"
#include "ff.h"
#include "portapack.hpp"
using namespace portapack;
namespace ui {
AFSKRXView::AFSKRXView(
NavigationView& nav
)
{
add_children({
&button_done,
&text_rx
});
button_done.on_select = [&nav](Button&){
nav.pop();
};
receiver_model.set_baseband_configuration({
.mode = 6,
.sampling_rate = 3072000,
.decimation_factor = 4,
});
receiver_model.set_baseband_bandwidth(1750000);
}
AFSKRXView::~AFSKRXView() {
receiver_model.disable();
}
void AFSKRXView::on_show() {
EventDispatcher::message_map().register_handler(Message::ID::AFSKData,
[this](Message* const p) {
const auto message = static_cast<const AFSKDataMessage*>(p);
this->on_data_afsk(*message);
}
);
receiver_model.enable();
}
void AFSKRXView::on_hide() {
EventDispatcher::message_map().unregister_handler(Message::ID::AFSKData);
}
void AFSKRXView::on_data_afsk(const AFSKDataMessage& message) {
Coord oy,ny;
//text_rx.set(to_string_dec_int(abs(message.data), 8, '0'));
portapack::display.fill_rectangle({0,160-64,240,128},{32,32,32});
oy = 160;
for (int c=0;c<128;c++) {
ny = 160-(message.data[c]>>10);
portapack::display.draw_line({static_cast<Coord>(c),oy},{static_cast<Coord>(c+1),ny},{255,127,0});
oy = ny;
}
/*auto payload = message.packet.payload;
auto payload_length = message.packet.bits_received;
std::string hex_data;
std::string hex_error;
uint8_t byte_data = 0;
uint8_t byte_error = 0;
for(size_t i=0; i<payload_length; i+=2) {
const auto bit_data = payload[i+1];
const auto bit_error = (payload[i+0] == payload[i+1]);
byte_data <<= 1;
byte_data |= bit_data ? 1 : 0;
byte_error <<= 1;
byte_error |= bit_error ? 1 : 0;
if( ((i >> 1) & 7) == 7 ) {
hex_data += to_string_hex(byte_data, 2);
hex_error += to_string_hex(byte_error, 2);
}
}
auto console = reinterpret_cast<Console*>(widget_content.get());
console->writeln(hex_data.substr(0, 240 / 8));
if( !f_error(&fil_tpms) ) {
rtc::RTC datetime;
rtcGetTime(&RTCD1, &datetime);
std::string timestamp =
to_string_dec_uint(datetime.year(), 4) +
to_string_dec_uint(datetime.month(), 2, '0') +
to_string_dec_uint(datetime.day(), 2, '0') +
to_string_dec_uint(datetime.hour(), 2, '0') +
to_string_dec_uint(datetime.minute(), 2, '0') +
to_string_dec_uint(datetime.second(), 2, '0');
const auto tuning_frequency = receiver_model.tuning_frequency();
// TODO: function doesn't take uint64_t, so when >= 1<<32, weirdness will ensue!
const auto tuning_frequency_str = to_string_dec_uint(tuning_frequency, 10);
std::string log = timestamp + " " + tuning_frequency_str + " FSK 38.4 19.2 " + hex_data + "/" + hex_error + "\r\n";
f_puts(log.c_str(), &fil_tpms);
f_sync(&fil_tpms);
}*/
}
void AFSKRXView::focus() {
button_done.focus();
}
} /* namespace ui */

View File

@ -1,74 +0,0 @@
/*
* Copyright (C) 2014 Jared Boone, ShareBrained Technology, Inc.
*
* 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 __UI_AFSKRX_H__
#define __UI_AFSKRX_H__
#include "ui.hpp"
#include "ui_font_fixed_8x16.hpp"
#include "ui_navigation.hpp"
#include "ui_painter.hpp"
#include "ui_widget.hpp"
#include "ui_console.hpp"
#include "utility.hpp"
#include "receiver_model.hpp"
#include <cstddef>
#include <cstdint>
#include <algorithm>
#include <functional>
namespace ui {
class AFSKRXView : public View {
public:
AFSKRXView(NavigationView& nav);
~AFSKRXView();
void focus() override;
void on_show() override;
void on_hide() override;
private:
std::unique_ptr<Widget> widget_content;
Button button_done {
{ 4 * 8, 0 * 16, 3 * 8, 16 },
" < "
};
Text text_rx {
{ 1 * 8, 2 * 16, 28 * 8, 16 },
"Ready..."
};
void on_tuning_frequency_changed(rf::Frequency f);
void on_edit_frequency();
void on_data_afsk(const AFSKDataMessage& message);
};
} /* namespace ui */
#endif/*__UI_RECEIVER_H__*/

View File

@ -20,7 +20,7 @@
* Boston, MA 02110-1301, USA.
*/
#include "ui_aprstx.hpp"
#include "ui_aprs_tx.hpp"
#include "ui_alphanum.hpp"
#include "aprs.hpp"

View File

@ -32,9 +32,7 @@ namespace ui {
EncodersConfigView::EncodersConfigView(
NavigationView& nav, Rect parent_rect
) {
using name_t = std::string;
using value_t = int32_t;
using option_t = std::pair<name_t, value_t>;
using option_t = std::pair<std::string, int32_t>;
std::vector<option_t> enc_options;
size_t i;
@ -47,9 +45,8 @@ EncodersConfigView::EncodersConfigView(
add_children({
&labels,
&options_enctype,
&numberfield_clk,
&numberfield_bitduration,
&numberfield_wordduration,
&field_clk,
&field_frameduration,
&symfield_word,
&text_format,
&waveform
@ -71,31 +68,19 @@ EncodersConfigView::EncodersConfigView(
};
// Selecting input clock changes symbol and word duration
numberfield_clk.on_change = [this](int32_t value) {
field_clk.on_change = [this](int32_t value) {
// value is in kHz, new_value is in us
int32_t new_value = 1000000 / ((value * 1000) / encoder_def->clk_per_symbol);
if (new_value != numberfield_bitduration.value()) {
numberfield_bitduration.set_value(new_value, false);
numberfield_wordduration.set_value(new_value * encoder_def->word_length, false);
}
};
// Selecting symbol duration changes input clock and word duration
numberfield_bitduration.on_change = [this](int32_t value) {
int32_t new_value = 1000000 / (((float)value * 1000) / encoder_def->clk_per_symbol);
if (new_value != numberfield_clk.value()) {
numberfield_clk.set_value(new_value, false);
numberfield_wordduration.set_value(value * encoder_def->word_length, false);
}
int32_t new_value = (encoder_def->clk_per_symbol * 1000000) / (value * 1000);
if (new_value != field_frameduration.value())
field_frameduration.set_value(new_value * encoder_def->word_length, false);
};
// Selecting word duration changes input clock and symbol duration
numberfield_wordduration.on_change = [this](int32_t value) {
int32_t new_value = value / encoder_def->word_length;
if (new_value != numberfield_bitduration.value()) {
numberfield_bitduration.set_value(new_value, false);
numberfield_clk.set_value(1000000 / (((float)new_value * 1000) / encoder_def->clk_per_symbol), false);
}
field_frameduration.on_change = [this](int32_t value) {
// value is in us, new_value is in kHz
int32_t new_value = (value * 1000) / (encoder_def->word_length * encoder_def->clk_per_symbol);
if (new_value != field_clk.value())
field_clk.set_value(1000000 / new_value, false);
};
}
@ -104,16 +89,13 @@ void EncodersConfigView::focus() {
}
void EncodersConfigView::on_type_change(size_t index) {
std::string word_format, format_string = "";
std::string format_string = "";
size_t word_length;
char symbol_type;
//size_t address_length;
//enc_type = index;
encoder_def = &encoder_defs[index];
numberfield_clk.set_value(encoder_def->default_speed / 1000);
field_clk.set_value(encoder_def->default_speed / 1000);
// SymField setup
word_length = encoder_def->word_length;
@ -139,16 +121,14 @@ void EncodersConfigView::on_type_change(size_t index) {
}
void EncodersConfigView::on_show() {
// TODO: Remove ?
//options_enctype.set_selected_index(enc_type);
//on_type_change(enc_type);
options_enctype.set_selected_index(0);
on_type_change(0);
}
void EncodersConfigView::draw_waveform() {
size_t length = frame_symbols.length();
size_t n;
for (n = 0; n < length; n++) {
for (size_t n = 0; n < length; n++) {
if (frame_symbols[n] == '0')
waveform_buffer[n] = 0;
else
@ -179,7 +159,7 @@ uint8_t EncodersConfigView::repeat_min() {
}
uint32_t EncodersConfigView::samples_per_bit() {
return OOK_SAMPLERATE / ((numberfield_clk.value() * 1000) / encoder_def->clk_per_fragment);
return OOK_SAMPLERATE / ((field_clk.value() * 1000) / encoder_def->clk_per_fragment);
}
uint32_t EncodersConfigView::pause_symbols() {
@ -199,20 +179,26 @@ EncodersScanView::EncodersScanView(
add_children({
&labels,
&field_debug,
&text_debug
&text_debug,
&text_length
});
// DEBUG
field_debug.on_change = [this](int32_t value) {
uint32_t l;
size_t length;
de_bruijn debruijn_seq;
debruijn_seq.init(value);
length = debruijn_seq.init(value);
l = 1;
l <<= value;
l--;
if (l > 25)
l = 25;
text_debug.set(to_string_bin(debruijn_seq.compute(l), 25));
text_length.set(to_string_dec_uint(length));
};
}
@ -256,7 +242,7 @@ void EncodersView::on_txdone(int n, const bool txdone) {
if (!txdone) {
// Repeating...
//repeat_index = n + 1;
repeat_index = n + 1;
/*if (tx_mode == SCAN) {
scan_progress++;
@ -299,8 +285,9 @@ void EncodersView::on_txdone(int n, const bool txdone) {
void EncodersView::start_tx(const bool scan) {
(void)scan;
uint8_t* ook_bitstream = shared_memory.bb_data.data;
uint32_t ook_bitstream_length;
uint8_t byte = 0;
size_t bitstream_length =0;
uint8_t * bitstream = shared_memory.bb_data.data;
repeat_min = view_config.repeat_min();
@ -324,28 +311,22 @@ void EncodersView::start_tx(const bool scan) {
view_config.generate_frame();
// Clear bitstream
memset(ook_bitstream, 0, 256);
size_t n = 0;
for (auto c : view_config.frame_symbols) {
byte <<= 1;
if (c != '0')
ook_bitstream[n >> 3] |= (1 << (7 - (n & 7)));
n++;
byte |= 1;
if ((bitstream_length & 7) == 7)
bitstream[bitstream_length >> 3] = byte;
bitstream_length++;
}
ook_bitstream_length = n;
transmitter_model.set_sampling_rate(OOK_SAMPLERATE);
transmitter_model.set_rf_amp(true);
transmitter_model.set_baseband_bandwidth(1750000);
transmitter_model.enable();
baseband::set_ook_data(
ook_bitstream_length,
// 2280000/2 = 1140000Hz = 0,877192982us
// numberfield_clk.value() / encoder_def->clk_per_fragment
// 455000 / 12 = 37917Hz = 26,37339452us
bitstream_length,
view_config.samples_per_bit(),
repeat_min,
view_config.pause_symbols()

View File

@ -67,12 +67,10 @@ private:
{ { 1 * 8, 0 }, "Type:", Color::light_grey() },
{ { 16 * 8, 0 }, "Clk:", Color::light_grey() },
{ { 24 * 8, 0 }, "kHz", Color::light_grey() },
{ { 16 * 8, 2 * 8 }, "Bit:", Color::light_grey() },
{ { 25 * 8, 2 * 8 }, "us", Color::light_grey() },
{ { 15 * 8, 4 * 8 }, "Word:", Color::light_grey() },
{ { 26 * 8, 4 * 8 }, "us", Color::light_grey() },
{ { 2 * 8, 6 * 8 }, "Word:", Color::light_grey() },
{ { 1 * 8, 13 * 8 }, "Waveform:", Color::light_grey() }
{ { 14 * 8, 2 * 8 }, "Frame:", Color::light_grey() },
{ { 26 * 8, 2 * 8 }, "us", Color::light_grey() },
{ { 2 * 8, 4 * 8 }, "Symbols:", Color::light_grey() },
{ { 1 * 8, 11 * 8 }, "Waveform:", Color::light_grey() }
};
OptionsField options_enctype { // Options are loaded at runtime
@ -82,7 +80,7 @@ private:
}
};
NumberField numberfield_clk {
NumberField field_clk {
{ 21 * 8, 0 },
3,
{ 1, 500 },
@ -90,16 +88,8 @@ private:
' '
};
NumberField numberfield_bitduration {
NumberField field_frameduration {
{ 21 * 8, 2 * 8 },
4,
{ 50, 9999 },
1,
' '
};
NumberField numberfield_wordduration {
{ 21 * 8, 4 * 8 },
5,
{ 300, 99999 },
100,
@ -107,18 +97,18 @@ private:
};
SymField symfield_word {
{ 2 * 8, 8 * 8 },
{ 2 * 8, 6 * 8 },
20,
SymField::SYMFIELD_DEF
};
Text text_format {
{ 2 * 8, 10 * 8, 24 * 8, 16 },
{ 2 * 8, 8 * 8, 24 * 8, 16 },
""
};
Waveform waveform {
{ 0, 16 * 8, 240, 32 },
{ 0, 14 * 8, 240, 32 },
waveform_buffer,
0,
0,
@ -136,7 +126,7 @@ public:
private:
Labels labels {
{ { 1 * 8, 1 * 8 }, "Test", Color::light_grey() }
{ { 1 * 8, 1 * 8 }, "Coming soon...", Color::light_grey() }
};
// DEBUG
@ -153,6 +143,12 @@ private:
{ 1 * 8, 8 * 8, 24 * 8, 16 },
""
};
// DEBUG
Text text_length {
{ 1 * 8, 10 * 8, 24 * 8, 16 },
""
};
};
class EncodersView : public View {
@ -192,7 +188,7 @@ private:
.foreground = Color::blue(),
};
Rect view_rect = { 0, 5 * 8, 240, 168 };
Rect view_rect = { 0, 4 * 8, 240, 168 };
EncodersConfigView view_config { nav_, view_rect };
EncodersScanView view_scan { nav_, view_rect };

View File

@ -177,9 +177,9 @@ private:
};
MessageHandlerRegistration message_handler_audio_level {
Message::ID::AudioLevel,
Message::ID::AudioLevelReport,
[this](const Message* const p) {
const auto message = static_cast<const AudioLevelMessage*>(p);
const auto message = static_cast<const AudioLevelReportMessage*>(p);
this->audio_level = message->value;
}
};

View File

@ -33,7 +33,8 @@
#include "ui_about.hpp"
#include "ui_adsb_tx.hpp"
#include "ui_adsb_rx.hpp"
#include "ui_aprstx.hpp"
#include "ui_aprs_tx.hpp"
#include "ui_afsk_rx.hpp"
#include "ui_bht_tx.hpp"
#include "ui_coasterp.hpp"
#include "ui_siggen.hpp"
@ -284,7 +285,7 @@ ReceiversMenuView::ReceiversMenuView(NavigationView& nav) {
add_items({
{ "ADS-B: Planes", ui::Color::green(), &bitmap_icon_adsb, [&nav](){ nav.push<ADSBRxView>(); }, },
{ "AIS: Boats", ui::Color::green(), &bitmap_icon_ais, [&nav](){ nav.push<AISAppView>(); } },
{ "APRS", ui::Color::grey(), &bitmap_icon_aprs, [&nav](){ nav.push<NotImplementedView>(); } },
{ "APRS", ui::Color::orange(),&bitmap_icon_aprs, [&nav](){ nav.push<AFSKRxView>(); } },
{ "Audio", ui::Color::green(), &bitmap_icon_speaker, [&nav](){ nav.push<AnalogAudioView>(false); } },
{ "ERT: Utility Meters", ui::Color::green(), &bitmap_icon_ert, [&nav](){ nav.push<ERTAppView>(); } },
{ "POCSAG", ui::Color::green(), &bitmap_icon_pocsag, [&nav](){ nav.push<POCSAGAppView>(); } },

View File

@ -294,7 +294,14 @@ DeclareTargets(PADT adsbtx)
set(MODE_CPPSRC
proc_afsk.cpp
)
DeclareTargets(PAFS afsk)
DeclareTargets(PAFT afsktx)
### AFSK RX
set(MODE_CPPSRC
proc_afskrx.cpp
)
DeclareTargets(PAFR afskrx)
### AIS

View File

@ -93,9 +93,9 @@ void AFSKProcessor::execute(const buffer_c8_t& buffer) {
}
void AFSKProcessor::on_message(const Message* const msg) {
const auto message = *reinterpret_cast<const AFSKConfigureMessage*>(msg);
const auto message = *reinterpret_cast<const AFSKTxConfigureMessage*>(msg);
if (message.id == Message::ID::AFSKConfigure) {
if (message.id == Message::ID::AFSKTxConfigure) {
if (message.samples_per_bit) {
afsk_samples_per_bit = message.samples_per_bit;
afsk_phase_inc_mark = message.phase_inc_mark * AFSK_DELTA_COEF;

View File

@ -1,5 +1,6 @@
/*
* Copyright (C) 2015 Jared Boone, ShareBrained Technology, Inc.
* Copyright (C) 2016 Furrtek
*
* This file is part of PortaPack.
*
@ -20,90 +21,93 @@
*/
#include "proc_afskrx.hpp"
#include "sine_table.hpp"
#include "portapack_shared_memory.hpp"
using namespace lpc43xx;
#include "event_m4.hpp"
void AFSKRXProcessor::execute(const buffer_c8_t& buffer) {
if( !configured ) {
return;
}
/* Called every 2048/3072000 second -- 1500Hz. */
#include <cstdint>
#include <cstddef>
void AFSKRxProcessor::execute(const buffer_c8_t& buffer) {
// This is called at 1500Hz
if (!configured) return;
const auto decim_0_out = decim_0.execute(buffer, dst_buffer);
const auto decim_1_out = decim_1.execute(decim_0_out, dst_buffer);
const auto channel_out = channel_filter.execute(decim_1_out, dst_buffer);
auto audio = demod.execute(channel_out, work_audio_buffer);
feed_channel_stats(channel_out);
/*static uint64_t audio_present_history = 0;
const auto audio_present_now = squelch.execute(audio);
audio_present_history = (audio_present_history << 1) | (audio_present_now ? 1 : 0);
const bool audio_present = (audio_present_history != 0);
*/
//if( !audio_present ) {
// Zero audio buffer.
/*for(size_t i=0; i<audio.count; i++) {
if ((i % 3) > 1)
audio.p[i] = 4096;
auto audio = demod.execute(channel_out, audio_buffer);
for (size_t c = 0; c < audio.count; c++) {
const int32_t sample_int = audio.p[c] * 32768.0f;
const int32_t audio_sample = __SSAT(sample_int, 16);
/*slicer_sr <<= 1;
slicer_sr |= (audio_sample < 0); // Do we need hysteresis ?
// Detect transitions to adjust clock
if ((slicer_sr ^ (slicer_sr >> 1)) & 1) {
if (sphase < (0x8000u - sphase_delta_half))
sphase += sphase_delta_eighth;
else
audio.p[i] = -4096;
}*/
sphase -= sphase_delta_eighth;
}
sphase += sphase_delta;*/
// Symbol time elapsed
//if (sphase >= 0x10000u) {
// sphase &= 0xFFFFu;
rx_data <<= 1;
rx_data |= 1;
bit_count++;
if (bit_count == 8) {
data_message.byte = rx_data;
shared_memory.application_queue.push(data_message);
bit_count = 0;
}
//}
//audio_hpf.execute_in_place(audio);
for(size_t i=0; i<audio.count; i++) {
if (spur > 10) {
if (audio.p[i] > 2000)
sign = 1;
if (audio.p[i] < -2000)
sign = 0;
spur = 0;
} else {
spur++;
}
if (sign != prev_sign) {
if (freq_timer < 15) // 48
bit = 0;
else
bit++;
freq_timer = 0;
}
prev_sign = sign;
if (freq_timer < 1000) freq_timer++; // TODO: Limit in a more intelligent way
}
if (bit_timer >= 40) {
bit_timer = 0;
// Check bit state here !
} else {
bit_timer++;
void AFSKRxProcessor::on_message(const Message* const message) {
if (message->id == Message::ID::AFSKRxConfigure)
configure(*reinterpret_cast<const AFSKRxConfigureMessage*>(message));
}
if (sc >= 600) {
sc = 0;
//AFSKDataMessage message;
//memcpy(message.data,aud,128*2);
//shared_memory.application_queue.push(message);
audc = 0;
} else {
sc++;
void AFSKRxProcessor::configure(const AFSKRxConfigureMessage& message) {
constexpr size_t decim_0_input_fs = baseband_fs;
constexpr size_t decim_0_output_fs = decim_0_input_fs / decim_0.decimation_factor;
constexpr size_t decim_1_input_fs = decim_0_output_fs;
constexpr size_t decim_1_output_fs = decim_1_input_fs / decim_1.decimation_factor;
constexpr size_t channel_filter_input_fs = decim_1_output_fs;
const size_t channel_filter_output_fs = channel_filter_input_fs / 2;
const size_t demod_input_fs = channel_filter_output_fs;
decim_0.configure(taps_16k0_decim_0.taps, 33554432);
decim_1.configure(taps_16k0_decim_1.taps, 131072);
channel_filter.configure(taps_16k0_channel.taps, 2);
demod.configure(demod_input_fs, 5000);
bitrate = message.bitrate;
sphase_delta = 0x10000u * bitrate / 24000;
sphase_delta_half = sphase_delta / 2; // Just for speed
sphase_delta_eighth = sphase_delta / 8;
configured = true;
}
if (audc < 4) {
memcpy(aud+(audc*32),audio.p,32*2);
audc++;
}
audio_output.write(audio);
}
void AFSKRXProcessor::data_handler(
const double data
) {
/*AFSKDataMessage message;
message.data = 'T';
shared_memory.application_queue.push(message);*/
int main() {
EventDispatcher event_dispatcher { std::make_unique<AFSKRxProcessor>() };
event_dispatcher.run();
return 0;
}

View File

@ -1,5 +1,6 @@
/*
* Copyright (C) 2015 Jared Boone, ShareBrained Technology, Inc.
* Copyright (C) 2016 Furrtek
*
* This file is part of PortaPack.
*
@ -23,46 +24,59 @@
#define __PROC_AFSKRX_H__
#include "baseband_processor.hpp"
#include "baseband_thread.hpp"
#include "rssi_thread.hpp"
#include "dsp_decimate.hpp"
#include "dsp_demodulate.hpp"
#include "audio_output.hpp"
#include "fifo.hpp"
#include "message.hpp"
class AFSKRXProcessor : public BasebandProcessor {
class AFSKRxProcessor : public BasebandProcessor {
public:
void execute(const buffer_c8_t& buffer) override;
void on_message(const Message* const message) override;
private:
std::array<complex16_t, 512> dst;
static constexpr size_t baseband_fs = 3072000;
BasebandThread baseband_thread { baseband_fs, this, NORMALPRIO + 20, baseband::Direction::Receive };
RSSIThread rssi_thread { NORMALPRIO + 10 };
std::array<complex16_t, 512> dst { };
const buffer_c16_t dst_buffer {
dst.data(),
dst.size()
};
const buffer_f32_t work_audio_buffer {
(float*)dst.data(),
sizeof(dst) / sizeof(float)
std::array<float, 32> audio { };
const buffer_f32_t audio_buffer {
audio.data(),
audio.size()
};
dsp::decimate::FIRAndDecimateComplex channel_filter;
dsp::demodulate::FM demod; // 48000 5000
// Can't use FIFO class here since it only allows power-of-two sizes
std::array<int32_t, 24000/1200> delay_line { 0 };
dsp::decimate::FIRC8xR16x24FS4Decim8 decim_0;
dsp::decimate::FIRC16xR16x32Decim8 decim_1;
dsp::decimate::FIRC8xR16x24FS4Decim8 decim_0 { };
dsp::decimate::FIRC16xR16x32Decim8 decim_1 { };
dsp::decimate::FIRAndDecimateComplex channel_filter { };
AudioOutput audio_output;
dsp::demodulate::FM demod { };
uint16_t bit_timer = 0, freq_timer = 0;
uint16_t sc;
uint8_t audc, spur, sign, prev_sign, bit = 0;
int16_t aud[128];
void data_handler(const double data);
uint32_t bitrate { };
uint32_t sphase { 0 };
uint32_t sphase_delta { 0 };
uint32_t sphase_delta_half { 0 };
uint32_t sphase_delta_eighth { 0 };
uint32_t rx_data { 0 };
uint32_t bit_count { 0 };
bool configured { false };
void configure(const NBFMConfigureMessage& message);
void configure(const AFSKRxConfigureMessage& message);
AFSKDataMessage data_message { 0 };
};
#endif/*__PROC_TPMS_H__*/

View File

@ -59,7 +59,7 @@ private:
int8_t re { 0 }, im { 0 };
AudioLevelMessage level_message { };
AudioLevelReportMessage level_message { };
TXDoneMessage txdone_message { };
};

View File

@ -71,12 +71,13 @@ public:
CaptureThreadDone = 18,
ReplayConfig = 19,
ReplayThreadDone = 20,
AFSKRxConfigure = 21,
TXDone = 30,
Retune = 31,
TonesConfigure = 32,
AFSKConfigure = 33,
AFSKTxConfigure = 33,
PWMRSSIConfigure = 34,
OOKConfigure = 35,
RDSConfigure = 36,
@ -93,11 +94,12 @@ public:
POCSAGPacket = 50,
ADSBFrame = 51,
AFSKData = 52,
RequestSignal = 52,
FIFOData = 53,
RequestSignal = 60,
FIFOData = 61,
AudioLevel = 54,
AudioLevelReport = 70,
MAX
};
@ -331,6 +333,18 @@ public:
adsb::ADSBFrame frame;
};
class AFSKDataMessage : public Message {
public:
constexpr AFSKDataMessage(
const uint_fast8_t byte
) : Message { ID::AFSKData },
byte { byte }
{
}
uint_fast8_t byte;
};
class ShutdownMessage : public Message {
public:
constexpr ShutdownMessage(
@ -598,7 +612,17 @@ public:
bool done = false;
};
class AFSKRxConfigureMessage : public Message {
public:
constexpr AFSKRxConfigureMessage(
const uint32_t bitrate
) : Message { ID::AFSKRxConfigure },
bitrate(bitrate)
{
}
const uint32_t bitrate;
};
class PWMRSSIConfigureMessage : public Message {
public:
@ -665,10 +689,10 @@ public:
uint32_t range = 0;
};
class AudioLevelMessage : public Message {
class AudioLevelReportMessage : public Message {
public:
constexpr AudioLevelMessage(
) : Message { ID::AudioLevel }
constexpr AudioLevelReportMessage(
) : Message { ID::AudioLevelReport }
{
}
@ -729,16 +753,16 @@ public:
const uint32_t tone_delta;
};
class AFSKConfigureMessage : public Message {
class AFSKTxConfigureMessage : public Message {
public:
constexpr AFSKConfigureMessage(
constexpr AFSKTxConfigureMessage(
const uint32_t samples_per_bit,
const uint32_t phase_inc_mark,
const uint32_t phase_inc_space,
const uint8_t repeat,
const uint32_t fm_delta,
const uint8_t symbol_count
) : Message { ID::AFSKConfigure },
) : Message { ID::AFSKTxConfigure },
samples_per_bit(samples_per_bit),
phase_inc_mark(phase_inc_mark),
phase_inc_space(phase_inc_space),

View File

@ -64,6 +64,7 @@ private:
};
constexpr image_tag_t image_tag_adsb_rx { 'P', 'A', 'D', 'R' };
constexpr image_tag_t image_tag_afsk_rx { 'P', 'A', 'F', 'R' };
constexpr image_tag_t image_tag_ais { 'P', 'A', 'I', 'S' };
constexpr image_tag_t image_tag_am_audio { 'P', 'A', 'M', 'A' };
constexpr image_tag_t image_tag_capture { 'P', 'C', 'A', 'P' };
@ -76,7 +77,7 @@ constexpr image_tag_t image_tag_wideband_spectrum { 'P', 'S', 'P', 'E' };
constexpr image_tag_t image_tag_jammer { 'P', 'J', 'A', 'M' };
constexpr image_tag_t image_tag_audio_tx { 'P', 'A', 'T', 'X' };
constexpr image_tag_t image_tag_afsk { 'P', 'A', 'F', 'S' };
constexpr image_tag_t image_tag_afsk { 'P', 'A', 'F', 'T' };
constexpr image_tag_t image_tag_tones { 'P', 'T', 'O', 'N' };
constexpr image_tag_t image_tag_rds { 'P', 'R', 'D', 'S' };
constexpr image_tag_t image_tag_ook { 'P', 'O', 'O', 'K' };