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:
furrtek
2017-07-23 12:20:32 +01:00
parent b57b41753f
commit 5a67a7080a
23 changed files with 949 additions and 437 deletions

View File

@@ -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

View File

@@ -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__*/

View 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;
}

View 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

View File

@@ -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;
}
}

View File

@@ -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 { };
};