mirror of
https://github.com/portapack-mayhem/mayhem-firmware.git
synced 2025-08-23 09:57:33 +00:00
ADS-B TX works well enough for dump1090 and gr-air-modes
Hooked ADS-B RX to baseband instead of debug IQ file, not tested
This commit is contained in:
@@ -275,12 +275,19 @@ macro(DeclareTargets chunk_tag name)
|
||||
set(BASEBAND_IMAGES ${BASEBAND_IMAGES} ${PROJECT_NAME}.img)
|
||||
endmacro()
|
||||
|
||||
### ADS-B RX
|
||||
|
||||
set(MODE_CPPSRC
|
||||
proc_adsbrx.cpp
|
||||
)
|
||||
DeclareTargets(PADR adsbrx)
|
||||
|
||||
### ADS-B TX
|
||||
|
||||
set(MODE_CPPSRC
|
||||
proc_adsbtx.cpp
|
||||
)
|
||||
DeclareTargets(PADS ads)
|
||||
DeclareTargets(PADT adsbtx)
|
||||
|
||||
### AFSK
|
||||
|
||||
|
@@ -35,7 +35,14 @@ class ChannelStatsCollector {
|
||||
public:
|
||||
template<typename Callback>
|
||||
void feed(const buffer_c16_t& src, Callback callback) {
|
||||
max_squared = compute_max_squared(src, max_squared);
|
||||
auto src_p = src.p;
|
||||
while(src_p < &src.p[src.count]) {
|
||||
const uint32_t sample = *__SIMD32(src_p)++;
|
||||
const uint32_t mag_sq = __SMUAD(sample, sample);
|
||||
if( mag_sq > max_squared ) {
|
||||
max_squared = mag_sq;
|
||||
}
|
||||
}
|
||||
count += src.count;
|
||||
|
||||
const size_t samples_per_update = src.sampling_rate * update_interval;
|
||||
@@ -54,22 +61,6 @@ private:
|
||||
static constexpr float update_interval { 0.1f };
|
||||
uint32_t max_squared { 0 };
|
||||
size_t count { 0 };
|
||||
|
||||
static uint32_t compute_max_squared(
|
||||
const buffer_c16_t& src,
|
||||
uint32_t max_squared
|
||||
) {
|
||||
auto src_p = src.p;
|
||||
while(src_p < &src.p[src.count]) {
|
||||
const uint32_t sample = *__SIMD32(src_p)++;
|
||||
const uint32_t mag_sq = __SMUAD(sample, sample);
|
||||
if( mag_sq > max_squared ) {
|
||||
max_squared = mag_sq;
|
||||
}
|
||||
}
|
||||
|
||||
return max_squared;
|
||||
}
|
||||
};
|
||||
|
||||
#endif/*__CHANNEL_STATS_COLLECTOR_H__*/
|
||||
|
158
firmware/baseband/proc_adsbrx.cpp
Normal file
158
firmware/baseband/proc_adsbrx.cpp
Normal file
@@ -0,0 +1,158 @@
|
||||
/*
|
||||
* 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 "proc_adsbrx.hpp"
|
||||
#include "portapack_shared_memory.hpp"
|
||||
#include "sine_table_int8.hpp"
|
||||
#include "event_m4.hpp"
|
||||
|
||||
#include <cstdint>
|
||||
#include <cstddef>
|
||||
|
||||
using namespace adsb;
|
||||
|
||||
void ADSBRXProcessor::execute(const buffer_c8_t& buffer) {
|
||||
int8_t re, im;
|
||||
float mag;
|
||||
uint32_t c;
|
||||
uint8_t level, bit, byte;
|
||||
bool confidence, first_in_window, last_in_window;
|
||||
|
||||
// This is called at 2M/2048 = 977Hz
|
||||
// One pulse = 500ns = 2 samples
|
||||
// One bit = 2 pulses = 1us = 4 samples
|
||||
|
||||
if (!configured) return;
|
||||
|
||||
for (size_t i = 0; i < buffer.count; i++) {
|
||||
|
||||
// Compute sample's magnitude
|
||||
re = buffer.p[i].real();
|
||||
im = buffer.p[i].imag();
|
||||
mag = __builtin_sqrtf((re * re) + (im * im)) * k;
|
||||
|
||||
// Only used for preamble detection and visualisation
|
||||
level = (mag < 0.3) ? 0 : // Blank weak signals
|
||||
(mag > prev_mag) ? 1 : 0;
|
||||
|
||||
if (decoding) {
|
||||
// Decode
|
||||
|
||||
// 1 bit lasts 2 samples
|
||||
if (sample_count & 1) {
|
||||
if ((prev_mag < threshold_low) && (mag < threshold_low)) {
|
||||
// Both under window, silence.
|
||||
if (null_count > 3) {
|
||||
//text_debug_b.set("Bits:" + bits.substr(0, 25));
|
||||
//text_debug_c.set("Hex:" + hex_str.substr(0, 26));
|
||||
//text_debug_d.set("DF=" + to_string_dec_uint(frame.get_DF()) + " ICAO=" + to_string_hex(frame.get_ICAO_address(), 6));
|
||||
|
||||
const ADSBFrameMessage message(frame);
|
||||
shared_memory.application_queue.push(message);
|
||||
|
||||
decoding = false;
|
||||
} else
|
||||
null_count++;
|
||||
|
||||
confidence = false;
|
||||
if (prev_mag > mag)
|
||||
bit = 1;
|
||||
else
|
||||
bit = 0;
|
||||
|
||||
} else {
|
||||
|
||||
null_count = 0;
|
||||
|
||||
first_in_window = ((prev_mag >= threshold_low) && (prev_mag <= threshold_high));
|
||||
last_in_window = ((mag >= threshold_low) && (mag <= threshold_high));
|
||||
|
||||
if ((first_in_window && !last_in_window) || (!first_in_window && last_in_window)) {
|
||||
confidence = true;
|
||||
if (prev_mag > mag)
|
||||
bit = 1;
|
||||
else
|
||||
bit = 0;
|
||||
} else {
|
||||
confidence = false;
|
||||
if (prev_mag > mag)
|
||||
bit = 1;
|
||||
else
|
||||
bit = 0;
|
||||
}
|
||||
}
|
||||
|
||||
byte = bit | (byte << 1);
|
||||
bit_count++;
|
||||
if (!(bit_count & 7)) {
|
||||
// Got one byte
|
||||
frame.push_byte(byte);
|
||||
}
|
||||
}
|
||||
sample_count++;
|
||||
} else {
|
||||
// Look for preamble
|
||||
|
||||
// Shift
|
||||
for (c = 0; c < (ADSB_PREAMBLE_LENGTH - 1); c++)
|
||||
shifter[c] = shifter[c + 1];
|
||||
shifter[15] = std::make_pair(mag, level);
|
||||
|
||||
// Compare
|
||||
for (c = 0; c < ADSB_PREAMBLE_LENGTH; c++) {
|
||||
if (shifter[c].second != adsb_preamble[c])
|
||||
break;
|
||||
}
|
||||
|
||||
if (c == ADSB_PREAMBLE_LENGTH) {
|
||||
decoding = true;
|
||||
sample_count = 0;
|
||||
null_count = 0;
|
||||
bit_count = 0;
|
||||
frame.clear();
|
||||
|
||||
// Compute preamble pulses power to set thresholds
|
||||
threshold = (shifter[0].first + shifter[2].first + shifter[7].first + shifter[9].first) / 4;
|
||||
threshold_high = threshold * 1.414; // +3dB
|
||||
threshold_low = threshold * 0.707; // -3dB
|
||||
}
|
||||
}
|
||||
|
||||
prev_mag = mag;
|
||||
}
|
||||
}
|
||||
|
||||
void ADSBRXProcessor::on_message(const Message* const message) {
|
||||
if (message->id == Message::ID::ADSBConfigure) {
|
||||
null_count = 0;
|
||||
bit_count = 0;
|
||||
sample_count = 0;
|
||||
decoding = false;
|
||||
configured = true;
|
||||
}
|
||||
}
|
||||
|
||||
int main() {
|
||||
EventDispatcher event_dispatcher { std::make_unique<ADSBRXProcessor>() };
|
||||
event_dispatcher.run();
|
||||
return 0;
|
||||
}
|
62
firmware/baseband/proc_adsbrx.hpp
Normal file
62
firmware/baseband/proc_adsbrx.hpp
Normal file
@@ -0,0 +1,62 @@
|
||||
/*
|
||||
* 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 __PROC_ADSBRX_H__
|
||||
#define __PROC_ADSBRX_H__
|
||||
|
||||
#include "baseband_processor.hpp"
|
||||
#include "baseband_thread.hpp"
|
||||
|
||||
#include "adsb_frame.hpp"
|
||||
|
||||
using namespace adsb;
|
||||
|
||||
#define ADSB_PREAMBLE_LENGTH 16
|
||||
|
||||
class ADSBRXProcessor : public BasebandProcessor {
|
||||
public:
|
||||
void execute(const buffer_c8_t& buffer) override;
|
||||
|
||||
void on_message(const Message* const message) override;
|
||||
|
||||
private:
|
||||
static constexpr float k = 1.0f / 128.0f;
|
||||
|
||||
static constexpr size_t baseband_fs = 2000000;
|
||||
|
||||
BasebandThread baseband_thread { baseband_fs, this, NORMALPRIO + 20, baseband::Direction::Receive };
|
||||
|
||||
ADSBFrame frame { };
|
||||
bool configured { false };
|
||||
float prev_mag { 0 };
|
||||
float threshold, threshold_low, threshold_high;
|
||||
size_t null_count, bit_count, sample_count;
|
||||
std::pair<float, uint8_t> shifter[ADSB_PREAMBLE_LENGTH];
|
||||
bool decoding { };
|
||||
bool preamble { }, active { };
|
||||
uint16_t bit_pos { 0 };
|
||||
uint8_t cur_bit { 0 };
|
||||
uint32_t sample { 0 };
|
||||
int8_t re { }, im { };
|
||||
};
|
||||
|
||||
#endif
|
@@ -32,26 +32,29 @@ void ADSBTXProcessor::execute(const buffer_c8_t& buffer) {
|
||||
// This is called at 4M/2048 = 1953Hz
|
||||
// One pulse = 500ns = 2 samples
|
||||
// One bit = 2 pulses = 1us = 4 samples
|
||||
// Test this with ./dump1090 --freq 434000000 --gain 20
|
||||
// Or ./dump1090 --freq 434000000 --gain 20 --interactive --net --net-http-port 8080 --net-beast
|
||||
|
||||
if (!configured) return;
|
||||
|
||||
for (size_t i = 0; i < buffer.count; i++) {
|
||||
|
||||
if (active) {
|
||||
if (!sample) {
|
||||
sample = 3;
|
||||
|
||||
if (preamble) {
|
||||
if (bit_pos >= 16) {
|
||||
preamble = false;
|
||||
bit_pos = 0;
|
||||
} else {
|
||||
cur_bit = (preamble_parts << bit_pos) >> 15;
|
||||
bit_pos++;
|
||||
}
|
||||
}
|
||||
|
||||
if (!preamble) {
|
||||
/*if (terminate) {
|
||||
for (size_t i = 0; i < buffer.count; i++) {
|
||||
buffer.p[i] = { 0, 0 };
|
||||
}
|
||||
terminate--;
|
||||
if (!terminate) {
|
||||
message.done = true;
|
||||
shared_memory.application_queue.push(message);
|
||||
configured = false;
|
||||
return;
|
||||
}
|
||||
} else {*/
|
||||
|
||||
for (size_t i = 0; i < buffer.count; i++) {
|
||||
|
||||
/*if (active) {
|
||||
if (!sample) {
|
||||
sample = 300;
|
||||
if (bit_pos >= 112) {
|
||||
active = false; // Stop
|
||||
cur_bit = 0;
|
||||
@@ -59,38 +62,39 @@ void ADSBTXProcessor::execute(const buffer_c8_t& buffer) {
|
||||
cur_bit = shared_memory.bb_data.data[bit_pos];
|
||||
bit_pos++;
|
||||
}
|
||||
} else
|
||||
sample--;
|
||||
|
||||
if (!preamble) {
|
||||
if (sample == 150)
|
||||
cur_bit ^= 1; // Invert
|
||||
}
|
||||
} else
|
||||
sample--;
|
||||
} else {*/
|
||||
/*cur_bit = 0;
|
||||
if (bit_pos >= 16384) {
|
||||
configured = false;
|
||||
message.done = true;
|
||||
shared_memory.application_queue.push(message);
|
||||
}*/
|
||||
if (bit_pos >= (240 << 1)) {
|
||||
configured = false;
|
||||
terminate = 100;
|
||||
cur_bit = 0;
|
||||
} else {
|
||||
cur_bit = shared_memory.bb_data.data[bit_pos >> 1];
|
||||
bit_pos++;
|
||||
}
|
||||
//}
|
||||
|
||||
if (sample == 1)
|
||||
cur_bit ^= 1; // Invert
|
||||
} else {
|
||||
//cur_bit = 0;
|
||||
if (bit_pos == 8192) { // ?
|
||||
configured = false;
|
||||
message.done = true;
|
||||
shared_memory.application_queue.push(message);
|
||||
if (cur_bit) {
|
||||
// Crude AM
|
||||
buffer.p[i] = am_lut[phase & 3];
|
||||
phase++;
|
||||
} else {
|
||||
buffer.p[i] = { 0, 0 };
|
||||
}
|
||||
bit_pos++;
|
||||
}
|
||||
|
||||
delta = tone_sample * fm_delta;
|
||||
tone_sample += 128;
|
||||
|
||||
if (cur_bit) {
|
||||
phase += delta;
|
||||
sphase = phase + (64 << 24);
|
||||
|
||||
re = (sine_table_i8[(sphase & 0xFF000000U) >> 24]);
|
||||
im = (sine_table_i8[(phase & 0xFF000000U) >> 24]);
|
||||
} else {
|
||||
re = 0;
|
||||
im = 0;
|
||||
}
|
||||
|
||||
buffer.p[i] = {re, im};
|
||||
}
|
||||
//}
|
||||
}
|
||||
|
||||
void ADSBTXProcessor::on_message(const Message* const p) {
|
||||
@@ -98,13 +102,10 @@ void ADSBTXProcessor::on_message(const Message* const p) {
|
||||
|
||||
if (message.id == Message::ID::ADSBConfigure) {
|
||||
bit_pos = 0;
|
||||
sample = 0;
|
||||
cur_bit = 0;
|
||||
preamble = true;
|
||||
phase = 0;
|
||||
active = true;
|
||||
configured = true;
|
||||
|
||||
fm_delta = 50000 * (0xFFFFFFULL / 4000000); // Fixed bw for now
|
||||
terminate = 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -26,8 +26,6 @@
|
||||
#include "baseband_processor.hpp"
|
||||
#include "baseband_thread.hpp"
|
||||
|
||||
#define TEST_F2D(f) (uint32_t)((f) * ((1ULL << 32) / 4000000))
|
||||
|
||||
class ADSBTXProcessor : public BasebandProcessor {
|
||||
public:
|
||||
void execute(const buffer_c8_t& buffer) override;
|
||||
@@ -39,16 +37,18 @@ private:
|
||||
|
||||
BasebandThread baseband_thread { 4000000, this, NORMALPRIO + 20, baseband::Direction::Transmit };
|
||||
|
||||
const uint16_t preamble_parts = 0b1010000101000000;
|
||||
bool preamble { }, active { };
|
||||
uint16_t bit_pos { 0 };
|
||||
uint8_t cur_bit { 0 };
|
||||
uint32_t sample { 0 };
|
||||
uint32_t tone_phase { 0 };
|
||||
uint32_t fm_delta { 0 };
|
||||
uint32_t phase { 0 }, sphase { 0 };
|
||||
int32_t tone_sample { 0 }, delta { 0 };
|
||||
int8_t re { }, im { };
|
||||
const complex8_t am_lut[4] = {
|
||||
{ 127, 0 },
|
||||
{ 0, 127 },
|
||||
{ -127, 0 },
|
||||
{ 0, -127 }
|
||||
};
|
||||
|
||||
bool active { };
|
||||
uint32_t terminate { };
|
||||
uint32_t bit_pos { 0 };
|
||||
uint32_t cur_bit { 0 };
|
||||
uint32_t phase { 0 };
|
||||
|
||||
TXDoneMessage message { };
|
||||
};
|
||||
|
Reference in New Issue
Block a user