mirror of
https://github.com/portapack-mayhem/mayhem-firmware.git
synced 2025-08-14 12:27:42 +00:00
Merge branch 'pr/325' into next
This commit is contained in:
@@ -315,6 +315,14 @@ set(MODE_CPPSRC
|
||||
proc_afskrx.cpp
|
||||
)
|
||||
DeclareTargets(PAFR afskrx)
|
||||
|
||||
### APRS RX
|
||||
|
||||
set(MODE_CPPSRC
|
||||
proc_aprsrx.cpp
|
||||
)
|
||||
DeclareTargets(PAPR aprsrx)
|
||||
|
||||
### NRF RX
|
||||
|
||||
set(MODE_CPPSRC
|
||||
|
258
firmware/baseband/proc_aprsrx.cpp
Normal file
258
firmware/baseband/proc_aprsrx.cpp
Normal file
@@ -0,0 +1,258 @@
|
||||
/*
|
||||
* Copyright (C) 2015 Jared Boone, ShareBrained Technology, Inc.
|
||||
* Copyright (C) 2016 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 "proc_aprsrx.hpp"
|
||||
#include "portapack_shared_memory.hpp"
|
||||
|
||||
#include "event_m4.hpp"
|
||||
|
||||
#include "stdio.h"
|
||||
|
||||
void APRSRxProcessor::execute(const buffer_c8_t& buffer) {
|
||||
// This is called at 3072000 / 2048 = 1500Hz
|
||||
|
||||
if (!configured) return;
|
||||
|
||||
// FM demodulation
|
||||
const auto decim_0_out = decim_0.execute(buffer, dst_buffer); // 2048 / 8 = 256 (512 I/Q samples)
|
||||
const auto decim_1_out = decim_1.execute(decim_0_out, dst_buffer); // 256 / 8 = 32 (64 I/Q samples)
|
||||
const auto channel_out = channel_filter.execute(decim_1_out, dst_buffer); // 32 / 2 = 16 (32 I/Q samples)
|
||||
|
||||
feed_channel_stats(channel_out);
|
||||
|
||||
auto audio = demod.execute(channel_out, audio_buffer);
|
||||
|
||||
audio_output.write(audio);
|
||||
|
||||
// Audio signal processing
|
||||
for (size_t c = 0; c < audio.count; c++) {
|
||||
|
||||
const int32_t sample_int = audio.p[c] * 32768.0f;
|
||||
int32_t current_sample = __SSAT(sample_int, 16);
|
||||
|
||||
current_sample /= 128;
|
||||
|
||||
// Delay line put
|
||||
delay_line[delay_line_index & 0x3F] = current_sample;
|
||||
|
||||
// Delay line get, and LPF
|
||||
sample_mixed = (delay_line[(delay_line_index - (samples_per_bit/2)) & 0x3F] * current_sample) / 4;
|
||||
sample_filtered = prev_mixed + sample_mixed + (prev_filtered / 2);
|
||||
|
||||
delay_line_index++;
|
||||
|
||||
prev_filtered = sample_filtered;
|
||||
prev_mixed = sample_mixed;
|
||||
|
||||
// Slice
|
||||
sample_bits <<= 1;
|
||||
|
||||
uint8_t bit = (sample_filtered < -20) ? 1 : 0;
|
||||
sample_bits |= bit;
|
||||
|
||||
/*
|
||||
int16_t scaled = bit == 1 ? 32767 : -32767;
|
||||
|
||||
if( stream ) {
|
||||
const size_t bytes_to_write = sizeof(scaled) * 1;
|
||||
const auto result = stream->write(&scaled, bytes_to_write);
|
||||
}
|
||||
*/
|
||||
|
||||
// Check for "clean" transition: either 0011 or 1100
|
||||
if ((((sample_bits >> 2) ^ sample_bits) & 3) == 3) {
|
||||
// Adjust phase
|
||||
if (phase < 0x8000)
|
||||
phase += 0x800; // Is this a proper value ?
|
||||
else
|
||||
phase -= 0x800;
|
||||
}
|
||||
|
||||
phase += phase_inc;
|
||||
|
||||
if (phase >= 0x10000) {
|
||||
phase &= 0xFFFF;
|
||||
|
||||
if (true) {
|
||||
uint8_t bit;
|
||||
if(__builtin_popcount(sample_bits & 0xFF) >= 0x05){
|
||||
bit = 0x1;
|
||||
}
|
||||
else {
|
||||
bit = 0x0;
|
||||
}
|
||||
|
||||
if(parse_bit(bit)){
|
||||
parse_packet();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void APRSRxProcessor::parse_packet(){
|
||||
//validate crc
|
||||
if(packet_buffer_size >= aprs::APRS_MIN_LENGTH){
|
||||
uint16_t crc = 0xFFFF;
|
||||
|
||||
for(size_t i = 0; i < packet_buffer_size; i++){
|
||||
uint8_t byte = packet_buffer[i];
|
||||
crc = ((crc >> 8) ^ crc_ccitt_tab[(crc ^ byte) & 0xFF]) & 0xFFFF;
|
||||
}
|
||||
|
||||
if(crc == 0xF0B8){
|
||||
parse_ax25();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void APRSRxProcessor::parse_ax25(){
|
||||
aprs_packet.clear();
|
||||
aprs_packet.set_valid_checksum(true);
|
||||
|
||||
for(size_t i = 0; i < packet_buffer_size; i++){
|
||||
aprs_packet.set(i, packet_buffer[i]);
|
||||
}
|
||||
|
||||
APRSPacketMessage packet_message { aprs_packet };
|
||||
shared_memory.application_queue.push(packet_message);
|
||||
}
|
||||
|
||||
bool APRSRxProcessor::parse_bit(const uint8_t current_bit){
|
||||
uint8_t decoded_bit = ~(current_bit ^ last_bit) & 0x1;
|
||||
last_bit = current_bit;
|
||||
|
||||
int16_t log = decoded_bit == 0 ? -32768 : 32767;
|
||||
|
||||
//if( stream ) {
|
||||
// const size_t bytes_to_write = sizeof(log) * 1;
|
||||
// const auto result = stream->write(&log, bytes_to_write);
|
||||
// }
|
||||
|
||||
if(decoded_bit & 0x1){
|
||||
if(ones_count < 8){
|
||||
ones_count++;
|
||||
}
|
||||
}
|
||||
else {
|
||||
if(ones_count > 6){ //not valid
|
||||
state = WAIT_FLAG;
|
||||
current_byte = 0;
|
||||
ones_count = 0;
|
||||
byte_index = 0;
|
||||
packet_buffer_size = 0;
|
||||
return false;
|
||||
}
|
||||
else if(ones_count == 6){ //flag
|
||||
bool done = false;
|
||||
if(state == IN_FRAME){
|
||||
done = true;
|
||||
}
|
||||
else {
|
||||
packet_buffer_size = 0;
|
||||
}
|
||||
state = WAIT_FRAME;
|
||||
current_byte = 0;
|
||||
ones_count = 0;
|
||||
byte_index = 0;
|
||||
|
||||
return done;
|
||||
}
|
||||
else if(ones_count == 5){ //bit stuff
|
||||
ones_count = 0;
|
||||
return false;
|
||||
}
|
||||
else {
|
||||
ones_count = 0;
|
||||
}
|
||||
}
|
||||
|
||||
//store
|
||||
current_byte = current_byte >> 1;
|
||||
current_byte |= (decoded_bit == 0x1 ? 0x80 : 0x0);
|
||||
byte_index++;
|
||||
|
||||
if(byte_index >= 8){
|
||||
byte_index = 0;
|
||||
if(state == WAIT_FRAME){
|
||||
state = IN_FRAME;
|
||||
}
|
||||
|
||||
if(state == IN_FRAME){
|
||||
if(packet_buffer_size + 1 >= 256){
|
||||
state = WAIT_FLAG;
|
||||
current_byte = 0;
|
||||
ones_count = 0;
|
||||
byte_index = 0;
|
||||
packet_buffer_size = 0;
|
||||
return false;
|
||||
}
|
||||
packet_buffer[packet_buffer_size++] = current_byte;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void APRSRxProcessor::on_message(const Message* const message) {
|
||||
if (message->id == Message::ID::APRSRxConfigure)
|
||||
configure(*reinterpret_cast<const APRSRxConfigureMessage*>(message));
|
||||
if(message->id == Message::ID::CaptureConfig)
|
||||
capture_config(*reinterpret_cast<const CaptureConfigMessage*>(message));
|
||||
}
|
||||
|
||||
void APRSRxProcessor::capture_config(const CaptureConfigMessage& message) {
|
||||
if( message.config ) {
|
||||
//stream = std::make_unique<StreamInput>(message.config);
|
||||
audio_output.set_stream(std::make_unique<StreamInput>(message.config));
|
||||
} else {
|
||||
//stream.reset();
|
||||
audio_output.set_stream(nullptr);
|
||||
}
|
||||
}
|
||||
|
||||
void APRSRxProcessor::configure(const APRSRxConfigureMessage& message) {
|
||||
decim_0.configure(taps_11k0_decim_0.taps, 33554432);
|
||||
decim_1.configure(taps_11k0_decim_1.taps, 131072);
|
||||
channel_filter.configure(taps_11k0_channel.taps, 2);
|
||||
demod.configure(audio_fs, 5000);
|
||||
|
||||
audio_output.configure(audio_24k_hpf_300hz_config, audio_24k_deemph_300_6_config, 0);
|
||||
|
||||
samples_per_bit = audio_fs / message.baudrate;
|
||||
|
||||
phase_inc = (0x10000 * message.baudrate) / audio_fs;
|
||||
phase = 0;
|
||||
|
||||
// Delay line
|
||||
delay_line_index = 0;
|
||||
|
||||
state = WAIT_FLAG;
|
||||
|
||||
configured = true;
|
||||
}
|
||||
|
||||
int main() {
|
||||
EventDispatcher event_dispatcher { std::make_unique<APRSRxProcessor>() };
|
||||
event_dispatcher.run();
|
||||
return 0;
|
||||
}
|
148
firmware/baseband/proc_aprsrx.hpp
Normal file
148
firmware/baseband/proc_aprsrx.hpp
Normal file
@@ -0,0 +1,148 @@
|
||||
/*
|
||||
* Copyright (C) 2015 Jared Boone, ShareBrained Technology, Inc.
|
||||
* Copyright (C) 2016 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 __PROC_APRSRX_H__
|
||||
#define __PROC_APRSRX_H__
|
||||
|
||||
#include "baseband_processor.hpp"
|
||||
#include "baseband_thread.hpp"
|
||||
#include "rssi_thread.hpp"
|
||||
|
||||
#include "dsp_decimate.hpp"
|
||||
#include "dsp_demodulate.hpp"
|
||||
#include "stream_input.hpp"
|
||||
|
||||
#include "audio_output.hpp"
|
||||
|
||||
#include "fifo.hpp"
|
||||
#include "message.hpp"
|
||||
|
||||
#include "aprs_packet.hpp"
|
||||
|
||||
static uint16_t crc_ccitt_tab[256] = {
|
||||
0x0000, 0x1189, 0x2312, 0x329b, 0x4624, 0x57ad, 0x6536, 0x74bf,
|
||||
0x8c48, 0x9dc1, 0xaf5a, 0xbed3, 0xca6c, 0xdbe5, 0xe97e, 0xf8f7,
|
||||
0x1081, 0x0108, 0x3393, 0x221a, 0x56a5, 0x472c, 0x75b7, 0x643e,
|
||||
0x9cc9, 0x8d40, 0xbfdb, 0xae52, 0xdaed, 0xcb64, 0xf9ff, 0xe876,
|
||||
0x2102, 0x308b, 0x0210, 0x1399, 0x6726, 0x76af, 0x4434, 0x55bd,
|
||||
0xad4a, 0xbcc3, 0x8e58, 0x9fd1, 0xeb6e, 0xfae7, 0xc87c, 0xd9f5,
|
||||
0x3183, 0x200a, 0x1291, 0x0318, 0x77a7, 0x662e, 0x54b5, 0x453c,
|
||||
0xbdcb, 0xac42, 0x9ed9, 0x8f50, 0xfbef, 0xea66, 0xd8fd, 0xc974,
|
||||
0x4204, 0x538d, 0x6116, 0x709f, 0x0420, 0x15a9, 0x2732, 0x36bb,
|
||||
0xce4c, 0xdfc5, 0xed5e, 0xfcd7, 0x8868, 0x99e1, 0xab7a, 0xbaf3,
|
||||
0x5285, 0x430c, 0x7197, 0x601e, 0x14a1, 0x0528, 0x37b3, 0x263a,
|
||||
0xdecd, 0xcf44, 0xfddf, 0xec56, 0x98e9, 0x8960, 0xbbfb, 0xaa72,
|
||||
0x6306, 0x728f, 0x4014, 0x519d, 0x2522, 0x34ab, 0x0630, 0x17b9,
|
||||
0xef4e, 0xfec7, 0xcc5c, 0xddd5, 0xa96a, 0xb8e3, 0x8a78, 0x9bf1,
|
||||
0x7387, 0x620e, 0x5095, 0x411c, 0x35a3, 0x242a, 0x16b1, 0x0738,
|
||||
0xffcf, 0xee46, 0xdcdd, 0xcd54, 0xb9eb, 0xa862, 0x9af9, 0x8b70,
|
||||
0x8408, 0x9581, 0xa71a, 0xb693, 0xc22c, 0xd3a5, 0xe13e, 0xf0b7,
|
||||
0x0840, 0x19c9, 0x2b52, 0x3adb, 0x4e64, 0x5fed, 0x6d76, 0x7cff,
|
||||
0x9489, 0x8500, 0xb79b, 0xa612, 0xd2ad, 0xc324, 0xf1bf, 0xe036,
|
||||
0x18c1, 0x0948, 0x3bd3, 0x2a5a, 0x5ee5, 0x4f6c, 0x7df7, 0x6c7e,
|
||||
0xa50a, 0xb483, 0x8618, 0x9791, 0xe32e, 0xf2a7, 0xc03c, 0xd1b5,
|
||||
0x2942, 0x38cb, 0x0a50, 0x1bd9, 0x6f66, 0x7eef, 0x4c74, 0x5dfd,
|
||||
0xb58b, 0xa402, 0x9699, 0x8710, 0xf3af, 0xe226, 0xd0bd, 0xc134,
|
||||
0x39c3, 0x284a, 0x1ad1, 0x0b58, 0x7fe7, 0x6e6e, 0x5cf5, 0x4d7c,
|
||||
0xc60c, 0xd785, 0xe51e, 0xf497, 0x8028, 0x91a1, 0xa33a, 0xb2b3,
|
||||
0x4a44, 0x5bcd, 0x6956, 0x78df, 0x0c60, 0x1de9, 0x2f72, 0x3efb,
|
||||
0xd68d, 0xc704, 0xf59f, 0xe416, 0x90a9, 0x8120, 0xb3bb, 0xa232,
|
||||
0x5ac5, 0x4b4c, 0x79d7, 0x685e, 0x1ce1, 0x0d68, 0x3ff3, 0x2e7a,
|
||||
0xe70e, 0xf687, 0xc41c, 0xd595, 0xa12a, 0xb0a3, 0x8238, 0x93b1,
|
||||
0x6b46, 0x7acf, 0x4854, 0x59dd, 0x2d62, 0x3ceb, 0x0e70, 0x1ff9,
|
||||
0xf78f, 0xe606, 0xd49d, 0xc514, 0xb1ab, 0xa022, 0x92b9, 0x8330,
|
||||
0x7bc7, 0x6a4e, 0x58d5, 0x495c, 0x3de3, 0x2c6a, 0x1ef1, 0x0f78
|
||||
};
|
||||
|
||||
class APRSRxProcessor : public BasebandProcessor {
|
||||
public:
|
||||
void execute(const buffer_c8_t& buffer) override;
|
||||
|
||||
void on_message(const Message* const message) override;
|
||||
|
||||
private:
|
||||
static constexpr size_t baseband_fs = 3072000;
|
||||
static constexpr size_t audio_fs = baseband_fs / 8 / 8 / 2;
|
||||
|
||||
size_t samples_per_bit { };
|
||||
|
||||
enum State {
|
||||
WAIT_FLAG,
|
||||
WAIT_FRAME,
|
||||
IN_FRAME
|
||||
};
|
||||
|
||||
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()
|
||||
};
|
||||
std::array<float, 32> audio { };
|
||||
const buffer_f32_t audio_buffer {
|
||||
audio.data(),
|
||||
audio.size()
|
||||
};
|
||||
|
||||
// Array size ok down to 375 bauds (24000 / 375)
|
||||
std::array<int32_t, 64> delay_line { 0 };
|
||||
|
||||
dsp::decimate::FIRC8xR16x24FS4Decim8 decim_0 { };
|
||||
dsp::decimate::FIRC16xR16x32Decim8 decim_1 { };
|
||||
dsp::decimate::FIRAndDecimateComplex channel_filter { };
|
||||
|
||||
std::unique_ptr<StreamInput> stream { };
|
||||
|
||||
dsp::demodulate::FM demod { };
|
||||
|
||||
AudioOutput audio_output { };
|
||||
|
||||
State state { };
|
||||
size_t delay_line_index { };
|
||||
uint32_t bit_counter { 0 };
|
||||
uint32_t word_bits { 0 };
|
||||
uint32_t sample_bits { 0 };
|
||||
uint32_t phase { }, phase_inc { };
|
||||
int32_t sample_mixed { }, prev_mixed { }, sample_filtered { }, prev_filtered { };
|
||||
uint8_t last_bit;
|
||||
uint8_t ones_count = 0;
|
||||
uint8_t current_byte = 0;
|
||||
uint8_t byte_index = 0;
|
||||
uint8_t packet_buffer[256];
|
||||
size_t packet_buffer_size = 0;
|
||||
|
||||
bool configured { false };
|
||||
bool wait_start { };
|
||||
bool bit_value { };
|
||||
|
||||
aprs::APRSPacket aprs_packet;
|
||||
|
||||
void configure(const APRSRxConfigureMessage& message);
|
||||
void capture_config(const CaptureConfigMessage& message);
|
||||
void parse_packet();
|
||||
bool parse_bit(const uint8_t bit);
|
||||
void parse_ax25();
|
||||
};
|
||||
|
||||
#endif/*__PROC_TPMS_H__*/
|
Reference in New Issue
Block a user