mirror of
https://github.com/portapack-mayhem/mayhem-firmware.git
synced 2024-12-14 12:08:40 +00:00
More ADS-B TX experimentation
Lots of junk added in Numbers Station regarding voice files Removed warnings caused by unfinished ADS-B function
This commit is contained in:
parent
790ec34ac0
commit
bebec9ccf7
@ -23,6 +23,7 @@
|
|||||||
// Color bitmaps generated with:
|
// Color bitmaps generated with:
|
||||||
// Gimp image > indexed colors (16), then "xxd -i *.bmp"
|
// Gimp image > indexed colors (16), then "xxd -i *.bmp"
|
||||||
|
|
||||||
|
//TODO: Waveform widget as FFT view in scanner
|
||||||
//BUG: Replay freezes when SD card not present
|
//BUG: Replay freezes when SD card not present
|
||||||
//BUG: RDS doesn't stop baseband when stopping tx ?
|
//BUG: RDS doesn't stop baseband when stopping tx ?
|
||||||
//BUG: Check AFSK transmit end, skips last bits ?
|
//BUG: Check AFSK transmit end, skips last bits ?
|
||||||
|
@ -76,6 +76,7 @@ void generate_frame_emergency(uint8_t * const adsb_frame, const uint32_t ICAO_ad
|
|||||||
ADSB_generate_CRC(adsb_frame);
|
ADSB_generate_CRC(adsb_frame);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
void generate_frame_pos(uint8_t * const adsb_frame, const uint32_t ICAO_address, const uint32_t altitude,
|
void generate_frame_pos(uint8_t * const adsb_frame, const uint32_t ICAO_address, const uint32_t altitude,
|
||||||
const float latitude, const float longitude) {
|
const float latitude, const float longitude) {
|
||||||
uint8_t c, time_parity;
|
uint8_t c, time_parity;
|
||||||
@ -103,9 +104,9 @@ void generate_frame_pos(uint8_t * const adsb_frame, const uint32_t ICAO_address,
|
|||||||
//rlat = delta_lat * ((yz / 524288.0) + int(lat / delta_lat));
|
//rlat = delta_lat * ((yz / 524288.0) + int(lat / delta_lat));
|
||||||
//delta_lon = 360.0 / (NL(rlat) - time_parity);
|
//delta_lon = 360.0 / (NL(rlat) - time_parity);
|
||||||
//xz = 524288.0 * (mod(lon, delta_lon) / delta_lon); // Round to int !
|
//xz = 524288.0 * (mod(lon, delta_lon) / delta_lon); // Round to int !
|
||||||
/*if (time_parity) {
|
if (time_parity) {
|
||||||
A = sign(rlat0)[NL(rlat0) - NL(rlat1)];
|
A = sign(rlat0)[NL(rlat0) - NL(rlat1)];
|
||||||
}*/
|
}
|
||||||
// int xz and yz, then:
|
// int xz and yz, then:
|
||||||
// xz >>= 2;
|
// xz >>= 2;
|
||||||
// yz >>= 2;
|
// yz >>= 2;
|
||||||
@ -115,13 +116,15 @@ void generate_frame_pos(uint8_t * const adsb_frame, const uint32_t ICAO_address,
|
|||||||
adsb_frame[5] = ((altitude_coded & 0x7F0) >> 3) | 1;
|
adsb_frame[5] = ((altitude_coded & 0x7F0) >> 3) | 1;
|
||||||
adsb_frame[6] = ((altitude_coded & 0x00F) << 4) | (LAT >> 15); // Then 0, even/odd, and the 2 LAT-CPR MSBs
|
adsb_frame[6] = ((altitude_coded & 0x00F) << 4) | (LAT >> 15); // Then 0, even/odd, and the 2 LAT-CPR MSBs
|
||||||
}
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
void ADSB_generate_CRC(uint8_t * const in_frame) {
|
void ADSB_generate_CRC(uint8_t * const in_frame) {
|
||||||
uint8_t adsb_crc[14]; // Temp buffer
|
uint8_t adsb_crc[14]; // Temp buffer
|
||||||
uint8_t b, c, s, bitn;
|
uint8_t b, c, s, bitn;
|
||||||
const uint32_t crc_poly = 0x1205FFF;
|
const uint32_t crc_poly = 0x1205FFF;
|
||||||
|
|
||||||
in_frame[11] = 0x00; // Clear CRC
|
// Clear CRC
|
||||||
|
in_frame[11] = 0x00;
|
||||||
in_frame[12] = 0x00;
|
in_frame[12] = 0x00;
|
||||||
in_frame[13] = 0x00;
|
in_frame[13] = 0x00;
|
||||||
|
|
||||||
@ -140,7 +143,6 @@ void ADSB_generate_CRC(uint8_t * const in_frame) {
|
|||||||
|
|
||||||
// Insert CRC in frame
|
// Insert CRC in frame
|
||||||
memcpy(&in_frame[11], &adsb_crc[11], 3);
|
memcpy(&in_frame[11], &adsb_crc[11], 3);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
} /* namespace adsb */
|
} /* namespace adsb */
|
||||||
|
@ -33,8 +33,8 @@ namespace adsb {
|
|||||||
void make_frame_mode_s(uint8_t * const adsb_frame, const uint32_t ICAO_address);
|
void make_frame_mode_s(uint8_t * const adsb_frame, const uint32_t ICAO_address);
|
||||||
|
|
||||||
void generate_frame_id(uint8_t * const adsb_frame, const uint32_t ICAO_address, std::string & callsign);
|
void generate_frame_id(uint8_t * const adsb_frame, const uint32_t ICAO_address, std::string & callsign);
|
||||||
void generate_frame_pos(uint8_t * const adsb_frame, const uint32_t ICAO_address, const uint32_t altitude,
|
//void generate_frame_pos(uint8_t * const adsb_frame, const uint32_t ICAO_address, const uint32_t altitude,
|
||||||
const float latitude, const float longitude);
|
// const float latitude, const float longitude);
|
||||||
void generate_frame_emergency(uint8_t * const adsb_frame, const uint32_t ICAO_address, const uint8_t code);
|
void generate_frame_emergency(uint8_t * const adsb_frame, const uint32_t ICAO_address, const uint8_t code);
|
||||||
|
|
||||||
void ADSB_generate_CRC(uint8_t * const in_message);
|
void ADSB_generate_CRC(uint8_t * const in_message);
|
||||||
|
@ -127,13 +127,25 @@ static void to_string_hex_internal(char* p, const uint64_t n, const int32_t l) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string to_string_hex(const uint64_t n, const int32_t l) {
|
std::string to_string_hex(const uint64_t n, int32_t l) {
|
||||||
char p[32];
|
char p[32];
|
||||||
|
|
||||||
|
l = std::min(l, 31L);
|
||||||
to_string_hex_internal(p, n, l - 1);
|
to_string_hex_internal(p, n, l - 1);
|
||||||
p[l] = 0;
|
p[l] = 0;
|
||||||
return p;
|
return p;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::string to_string_hex_array(uint8_t * const array, const int32_t l) {
|
||||||
|
std::string str_return = "";
|
||||||
|
uint8_t bytes;
|
||||||
|
|
||||||
|
for (bytes = 0; bytes < l; bytes++)
|
||||||
|
str_return += to_string_hex(array[bytes], 2);
|
||||||
|
|
||||||
|
return str_return;
|
||||||
|
}
|
||||||
|
|
||||||
std::string to_string_datetime(const rtc::RTC& value) {
|
std::string to_string_datetime(const rtc::RTC& value) {
|
||||||
return to_string_dec_uint(value.year(), 4, '0') + "/" +
|
return to_string_dec_uint(value.year(), 4, '0') + "/" +
|
||||||
to_string_dec_uint(value.month(), 2, '0') + "/" +
|
to_string_dec_uint(value.month(), 2, '0') + "/" +
|
||||||
|
@ -34,6 +34,7 @@ std::string to_string_bin(const uint32_t n, const uint8_t l = 0);
|
|||||||
std::string to_string_dec_uint(const uint32_t n, const int32_t l = 0, const char fill = 0);
|
std::string to_string_dec_uint(const uint32_t n, const int32_t l = 0, const char fill = 0);
|
||||||
std::string to_string_dec_int(const int32_t n, const int32_t l = 0, const char fill = 0);
|
std::string to_string_dec_int(const int32_t n, const int32_t l = 0, const char fill = 0);
|
||||||
std::string to_string_hex(const uint64_t n, const int32_t l = 0);
|
std::string to_string_hex(const uint64_t n, const int32_t l = 0);
|
||||||
|
std::string to_string_hex_array(uint8_t * const array, const int32_t l = 0);
|
||||||
|
|
||||||
std::string to_string_short_freq(const uint64_t f, const int32_t l = 4);
|
std::string to_string_short_freq(const uint64_t f, const int32_t l = 4);
|
||||||
|
|
||||||
|
@ -38,7 +38,8 @@ using namespace portapack;
|
|||||||
namespace ui {
|
namespace ui {
|
||||||
|
|
||||||
void ADSBTxView::focus() {
|
void ADSBTxView::focus() {
|
||||||
sym_icao.focus();
|
//sym_icao.focus();
|
||||||
|
tx_view.focus();
|
||||||
}
|
}
|
||||||
|
|
||||||
ADSBTxView::~ADSBTxView() {
|
ADSBTxView::~ADSBTxView() {
|
||||||
@ -59,21 +60,15 @@ void ADSBTxView::generate_frame() {
|
|||||||
|
|
||||||
memset(adsb_bin, 0, 112);
|
memset(adsb_bin, 0, 112);
|
||||||
|
|
||||||
// Convert to binary (1 bit per byte, faster for baseband code)
|
// Convert to binary (1 byte per bit, faster for baseband code)
|
||||||
for (c = 0; c < 112; c++) {
|
for (c = 0; c < 112; c++) {
|
||||||
if ((adsb_frame[c >> 3] << (c & 7)) & 0x80)
|
if ((adsb_frame[c >> 3] << (c & 7)) & 0x80)
|
||||||
adsb_bin[c] = 0xFF;
|
adsb_bin[c] = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Display for debug
|
// Display in hex for debug
|
||||||
str_debug = "";
|
text_frame_a.set(to_string_hex_array(&adsb_frame[0], 7));
|
||||||
for (c = 0; c < 7; c++)
|
text_frame_b.set(to_string_hex_array(&adsb_frame[7], 7));
|
||||||
str_debug += to_string_hex(adsb_frame[c], 2);
|
|
||||||
text_frame_a.set(str_debug);
|
|
||||||
str_debug = "";
|
|
||||||
for (c = 0; c < 7; c++)
|
|
||||||
str_debug += to_string_hex(adsb_frame[c + 7], 2);
|
|
||||||
text_frame_b.set(str_debug);
|
|
||||||
|
|
||||||
button_callsign.set_text(callsign);
|
button_callsign.set_text(callsign);
|
||||||
}
|
}
|
||||||
@ -81,25 +76,25 @@ void ADSBTxView::generate_frame() {
|
|||||||
bool ADSBTxView::start_tx() {
|
bool ADSBTxView::start_tx() {
|
||||||
generate_frame();
|
generate_frame();
|
||||||
|
|
||||||
transmitter_model.set_tuning_frequency(434000000); // FOR TESTING - DEBUG
|
|
||||||
transmitter_model.set_sampling_rate(2000000U);
|
|
||||||
transmitter_model.set_rf_amp(true);
|
|
||||||
transmitter_model.set_lna(40);
|
|
||||||
transmitter_model.set_vga(40);
|
|
||||||
transmitter_model.set_baseband_bandwidth(1750000);
|
|
||||||
transmitter_model.enable();
|
|
||||||
|
|
||||||
memcpy(shared_memory.bb_data.data, adsb_bin, 112);
|
memcpy(shared_memory.bb_data.data, adsb_bin, 112);
|
||||||
baseband::set_adsb();
|
baseband::set_adsb();
|
||||||
|
|
||||||
|
transmitter_model.set_tuning_frequency(434000000); // FOR TESTING - DEBUG
|
||||||
|
transmitter_model.set_sampling_rate(4000000U);
|
||||||
|
transmitter_model.set_rf_amp(true);
|
||||||
|
transmitter_model.set_vga(40);
|
||||||
|
transmitter_model.set_baseband_bandwidth(2500000);
|
||||||
|
transmitter_model.enable();
|
||||||
|
|
||||||
return false; // DEBUG
|
return false; // DEBUG
|
||||||
}
|
}
|
||||||
|
|
||||||
void ADSBTxView::on_txdone(const int n) {
|
void ADSBTxView::on_txdone(const bool v) {
|
||||||
//size_t sr;
|
//size_t sr;
|
||||||
|
|
||||||
if (n == 200) {
|
if (v) {
|
||||||
transmitter_model.disable();
|
transmitter_model.disable();
|
||||||
|
tx_view.set_transmitting(false);
|
||||||
//progress.set_value(0);
|
//progress.set_value(0);
|
||||||
} else {
|
} else {
|
||||||
//progress.set_value(n);
|
//progress.set_value(n);
|
||||||
|
@ -61,7 +61,7 @@ private:
|
|||||||
bool start_tx();
|
bool start_tx();
|
||||||
void generate_frame();
|
void generate_frame();
|
||||||
void generate_frame_pos();
|
void generate_frame_pos();
|
||||||
void on_txdone(const int n);
|
void on_txdone(const bool v);
|
||||||
|
|
||||||
const Style style_val {
|
const Style style_val {
|
||||||
.font = font::fixed_8x16,
|
.font = font::fixed_8x16,
|
||||||
@ -166,7 +166,7 @@ private:
|
|||||||
Message::ID::TXDone,
|
Message::ID::TXDone,
|
||||||
[this](const Message* const p) {
|
[this](const Message* const p) {
|
||||||
const auto message = *reinterpret_cast<const TXDoneMessage*>(p);
|
const auto message = *reinterpret_cast<const TXDoneMessage*>(p);
|
||||||
this->on_txdone(message.progress);
|
this->on_txdone(message.done);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
@ -34,6 +34,8 @@ using namespace portapack;
|
|||||||
|
|
||||||
namespace ui {
|
namespace ui {
|
||||||
|
|
||||||
|
// TODO: This app takes way too much space, find a way to shrink/simplify or make it an SD card module (loadable)
|
||||||
|
|
||||||
void NumbersStationView::focus() {
|
void NumbersStationView::focus() {
|
||||||
if (file_error)
|
if (file_error)
|
||||||
nav_.display_modal("No voices", "No valid voices found in\nthe /numbers directory.", ABORT, nullptr);
|
nav_.display_modal("No voices", "No valid voices found in\nthe /numbers directory.", ABORT, nullptr);
|
||||||
@ -46,29 +48,34 @@ NumbersStationView::~NumbersStationView() {
|
|||||||
baseband::shutdown();
|
baseband::shutdown();
|
||||||
}
|
}
|
||||||
|
|
||||||
void NumbersStationView::on_tuning_frequency_changed(rf::Frequency f) {
|
NumbersStationView::wav_file_t * NumbersStationView::get_wav(uint32_t index) {
|
||||||
transmitter_model.set_tuning_frequency(f);
|
return ¤t_voice->available_wavs[index];
|
||||||
}
|
}
|
||||||
|
|
||||||
void NumbersStationView::prepare_audio() {
|
void NumbersStationView::prepare_audio() {
|
||||||
uint8_t code;
|
uint8_t code;
|
||||||
|
wav_file_t * wav_file;
|
||||||
|
|
||||||
/*if (sample_counter >= sample_duration) {
|
if (sample_counter >= sample_length) {
|
||||||
if (segment == ANNOUNCE) {
|
if (segment == ANNOUNCE) {
|
||||||
if (!announce_loop) {
|
if (!announce_loop) {
|
||||||
|
code_index = 0;
|
||||||
segment = MESSAGE;
|
segment = MESSAGE;
|
||||||
} else {
|
} else {
|
||||||
reader->open("/numbers/announce.wav");
|
wav_file = get_wav(11);
|
||||||
sample_duration = sound_sizes[10];
|
reader->open(current_voice->dir + file_names[wav_file->index].name + ".wav");
|
||||||
|
sample_length = wav_file->length;
|
||||||
announce_loop--;
|
announce_loop--;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (segment == MESSAGE) {
|
if (segment == MESSAGE) {
|
||||||
code = symfield_code.get_sym(code_index);
|
if (code_index == 25) {
|
||||||
|
|
||||||
if (code_index == 25)
|
|
||||||
transmitter_model.disable();
|
transmitter_model.disable();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
code = symfield_code.get_sym(code_index);
|
||||||
|
|
||||||
if (code >= 10) {
|
if (code >= 10) {
|
||||||
memset(audio_buffer, 0, 1024);
|
memset(audio_buffer, 0, 1024);
|
||||||
@ -78,10 +85,12 @@ void NumbersStationView::prepare_audio() {
|
|||||||
pause = 33075; // P: 0.75s @ 44100Hz
|
pause = 33075; // P: 0.75s @ 44100Hz
|
||||||
} else if (code == 12) {
|
} else if (code == 12) {
|
||||||
transmitter_model.disable();
|
transmitter_model.disable();
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
reader->open("/numbers/" + file_names[code] + ".wav");
|
wav_file = get_wav(code);
|
||||||
sample_duration = sound_sizes[code];
|
reader->open(current_voice->dir + file_names[code].name + ".wav");
|
||||||
|
sample_length = wav_file->length;
|
||||||
}
|
}
|
||||||
code_index++;
|
code_index++;
|
||||||
}
|
}
|
||||||
@ -102,17 +111,17 @@ void NumbersStationView::prepare_audio() {
|
|||||||
if (pause >= 1024) {
|
if (pause >= 1024) {
|
||||||
pause -= 1024;
|
pause -= 1024;
|
||||||
} else {
|
} else {
|
||||||
sample_counter = sample_duration;
|
sample_counter = sample_length;
|
||||||
pause = 0;
|
pause = 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
baseband::set_fifo_data(audio_buffer);*/
|
baseband::set_fifo_data(audio_buffer);
|
||||||
}
|
}
|
||||||
|
|
||||||
void NumbersStationView::start_tx() {
|
void NumbersStationView::start_tx() {
|
||||||
//sample_duration = sound_sizes[10]; // Announce
|
//sample_length = sound_sizes[10]; // Announce
|
||||||
sample_counter = sample_duration;
|
sample_counter = sample_length;
|
||||||
|
|
||||||
code_index = 0;
|
code_index = 0;
|
||||||
announce_loop = 2;
|
announce_loop = 2;
|
||||||
@ -126,7 +135,7 @@ void NumbersStationView::start_tx() {
|
|||||||
transmitter_model.enable();
|
transmitter_model.enable();
|
||||||
|
|
||||||
baseband::set_audiotx_data(
|
baseband::set_audiotx_data(
|
||||||
(1536000 / 44100) - 1,
|
(1536000 / 44100) - 1, // TODO: Read wav file's samplerate
|
||||||
12000,
|
12000,
|
||||||
1,
|
1,
|
||||||
false,
|
false,
|
||||||
@ -135,28 +144,37 @@ void NumbersStationView::start_tx() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void NumbersStationView::on_tick_second() {
|
void NumbersStationView::on_tick_second() {
|
||||||
if (check_armed.value()) {
|
armed_blink = not armed_blink;
|
||||||
armed_blink = not armed_blink;
|
|
||||||
|
if (armed_blink)
|
||||||
if (armed_blink)
|
check_armed.set_style(&style_red);
|
||||||
check_armed.set_style(&style_red);
|
else
|
||||||
else
|
check_armed.set_style(&style());
|
||||||
check_armed.set_style(&style());
|
|
||||||
|
check_armed.set_dirty();
|
||||||
check_armed.set_dirty();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void NumbersStationView::on_voice_changed(size_t index) {
|
void NumbersStationView::on_voice_changed(size_t index) {
|
||||||
std::string flags_string = "";
|
std::string code_list = "";
|
||||||
|
|
||||||
if (voices[index].accent) {
|
for (const auto& wavs : voices[index].available_wavs)
|
||||||
flags_string = "^";
|
code_list += wavs.code;
|
||||||
}
|
|
||||||
if (voices[index].announce) {
|
for (uint32_t c = 0; c < 25; c++)
|
||||||
flags_string += "A";
|
symfield_code.set_symbol_list(c, code_list);
|
||||||
}
|
|
||||||
text_voice_flags.set(flags_string);
|
current_voice = &voices[index];
|
||||||
|
}
|
||||||
|
|
||||||
|
bool NumbersStationView::check_wav_validity(const std::string dir, const std::string file) {
|
||||||
|
if (reader->open("/numbers/" + dir + "/" + file)) {
|
||||||
|
// Check format (mono, 8 bits)
|
||||||
|
if ((reader->channels() == 1) && (reader->bits_per_sample() == 8))
|
||||||
|
return true;
|
||||||
|
else
|
||||||
|
return false;
|
||||||
|
} else
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
NumbersStationView::NumbersStationView(
|
NumbersStationView::NumbersStationView(
|
||||||
@ -167,8 +185,9 @@ NumbersStationView::NumbersStationView(
|
|||||||
using option_t = std::pair<std::string, int32_t>;
|
using option_t = std::pair<std::string, int32_t>;
|
||||||
using options_t = std::vector<option_t>;
|
using options_t = std::vector<option_t>;
|
||||||
options_t voice_options;
|
options_t voice_options;
|
||||||
uint32_t c, i, ia;
|
voice_t temp_voice { };
|
||||||
bool valid;
|
bool valid;
|
||||||
|
uint32_t c;
|
||||||
//uint8_t y, m, d, dayofweek;
|
//uint8_t y, m, d, dayofweek;
|
||||||
|
|
||||||
reader = std::make_unique<WAVFileReader>();
|
reader = std::make_unique<WAVFileReader>();
|
||||||
@ -179,42 +198,39 @@ NumbersStationView::NumbersStationView(
|
|||||||
file_error = true;
|
file_error = true;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
// This is awfully repetitive
|
|
||||||
for (const auto& dir : directory_list) {
|
for (const auto& dir : directory_list) {
|
||||||
i = 0;
|
c = 0;
|
||||||
for (const auto& file_name : file_names) {
|
for (const auto& file_name : file_names) {
|
||||||
valid = false;
|
valid = check_wav_validity(dir.string(), file_name.name + ".wav");
|
||||||
if (reader->open("/numbers/" + dir.string() + "/" + file_name + ".wav")) {
|
if ((!valid) && (file_name.required)) {
|
||||||
// Check format (mono, 8 bits)
|
temp_voice.available_wavs.clear();
|
||||||
if ((reader->channels() == 1) && (reader->bits_per_sample() == 8))
|
break; // Invalid required file means invalid voice
|
||||||
valid = true;
|
} else if (valid) {
|
||||||
|
temp_voice.available_wavs.push_back({ file_name.code, c++, 0, 0 }); // TODO: Needs length and samplerate
|
||||||
}
|
}
|
||||||
if (!valid) {
|
|
||||||
if (i < 10)
|
|
||||||
i = 0; // Missingno, invalid voice
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
i++;
|
|
||||||
}
|
}
|
||||||
if (i) {
|
if (!temp_voice.available_wavs.empty()) {
|
||||||
// Voice ok, are there accent files ?
|
// Voice can be used, are there accent files ?
|
||||||
ia = 0;
|
c = 0;
|
||||||
for (const auto& file_name : file_names) {
|
for (const auto& file_name : file_names) {
|
||||||
valid = false;
|
valid = check_wav_validity(dir.string(), file_name.name + "a.wav");
|
||||||
if (reader->open("/numbers/" + dir.string() + "/" + file_name + "a.wav")) {
|
if ((!valid) && (file_name.required)) {
|
||||||
// Check format (mono, 8 bits)
|
c = 0;
|
||||||
if ((reader->channels() == 1) && (reader->bits_per_sample() == 8))
|
break; // Invalid required file means accents can't be used
|
||||||
valid = true;
|
} else if (valid) {
|
||||||
|
c++;
|
||||||
}
|
}
|
||||||
if (!valid)
|
|
||||||
break;
|
|
||||||
ia++;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
voices.push_back({ dir.string(), (ia >= 10), (i == 11) });
|
temp_voice.accent = c ? true : false;
|
||||||
|
temp_voice.dir = dir.string();
|
||||||
|
|
||||||
|
voices.push_back(temp_voice);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (!voices.size()) {
|
|
||||||
|
if (voices.empty()) {
|
||||||
file_error = true;
|
file_error = true;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -254,9 +270,6 @@ NumbersStationView::NumbersStationView(
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
for (c = 0; c < 25; c++)
|
|
||||||
symfield_code.set_symbol_list(c, "0123456789pPE");
|
|
||||||
|
|
||||||
// DEBUG
|
// DEBUG
|
||||||
symfield_code.set_sym(0, 10);
|
symfield_code.set_sym(0, 10);
|
||||||
symfield_code.set_sym(1, 3);
|
symfield_code.set_sym(1, 3);
|
||||||
|
@ -42,6 +42,11 @@ class NumbersStationView : public View {
|
|||||||
public:
|
public:
|
||||||
NumbersStationView(NavigationView& nav);
|
NumbersStationView(NavigationView& nav);
|
||||||
~NumbersStationView();
|
~NumbersStationView();
|
||||||
|
|
||||||
|
NumbersStationView(const NumbersStationView&) = delete;
|
||||||
|
NumbersStationView(NumbersStationView&&) = delete;
|
||||||
|
NumbersStationView& operator=(const NumbersStationView&) = delete;
|
||||||
|
NumbersStationView& operator=(NumbersStationView&&) = delete;
|
||||||
|
|
||||||
void focus() override;
|
void focus() override;
|
||||||
|
|
||||||
@ -64,60 +69,66 @@ private:
|
|||||||
.foreground = Color::red()
|
.foreground = Color::red()
|
||||||
};
|
};
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
char code;
|
||||||
|
uint32_t index;
|
||||||
|
uint32_t length;
|
||||||
|
uint32_t samplerate;
|
||||||
|
} wav_file_t;
|
||||||
|
|
||||||
struct voice_t {
|
struct voice_t {
|
||||||
std::string dir;
|
std::string dir;
|
||||||
|
std::vector<wav_file_t> available_wavs;
|
||||||
bool accent;
|
bool accent;
|
||||||
bool announce;
|
};
|
||||||
|
|
||||||
|
std::vector<voice_t> voices { };
|
||||||
|
voice_t * current_voice { };
|
||||||
|
|
||||||
|
struct wav_file_list_t {
|
||||||
|
std::string name;
|
||||||
|
bool required;
|
||||||
|
char code;
|
||||||
|
};
|
||||||
|
|
||||||
|
const std::vector<wav_file_list_t> file_names = {
|
||||||
|
{ "0", true, '0' },
|
||||||
|
{ "1", true, '1' },
|
||||||
|
{ "2", true, '2' },
|
||||||
|
{ "3", true, '3' },
|
||||||
|
{ "4", true, '4' },
|
||||||
|
{ "5", true, '5' },
|
||||||
|
{ "6", true, '6' },
|
||||||
|
{ "7", true, '7' },
|
||||||
|
{ "8", true, '8' },
|
||||||
|
{ "9", true, '9' },
|
||||||
|
{ "announce", false, 'A' }
|
||||||
};
|
};
|
||||||
|
|
||||||
segments segment { IDLE };
|
segments segment { IDLE };
|
||||||
bool armed { false };
|
bool armed { false };
|
||||||
bool file_error { false };
|
bool file_error { false };
|
||||||
std::vector<voice_t> voices;
|
|
||||||
|
|
||||||
const std::vector<std::string> file_names = {
|
|
||||||
{ "0" },
|
|
||||||
{ "1" },
|
|
||||||
{ "2" },
|
|
||||||
{ "3" },
|
|
||||||
{ "4" },
|
|
||||||
{ "5" },
|
|
||||||
{ "6" },
|
|
||||||
{ "7" },
|
|
||||||
{ "8" },
|
|
||||||
{ "9" },
|
|
||||||
{ "announce" }
|
|
||||||
};
|
|
||||||
|
|
||||||
// const uint8_t month_table[12] = { 0, 3, 2, 5, 0, 3, 5, 1, 4, 6, 2, 4 };
|
// const uint8_t month_table[12] = { 0, 3, 2, 5, 0, 3, 5, 1, 4, 6, 2, 4 };
|
||||||
// const char * day_of_week[7] = { "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday" };
|
// const char * day_of_week[7] = { "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday" };
|
||||||
|
|
||||||
std::unique_ptr<WAVFileReader> reader { };
|
std::unique_ptr<WAVFileReader> reader { };
|
||||||
|
|
||||||
uint8_t code_index, announce_loop;
|
uint8_t code_index { 0 }, announce_loop { 0 };
|
||||||
uint32_t sample_counter;
|
uint32_t sample_counter { 0 };
|
||||||
uint32_t sample_duration;
|
uint32_t sample_length { 0 };
|
||||||
int8_t audio_buffer[1024];
|
int8_t audio_buffer[1024] { };
|
||||||
uint32_t pause = 0;
|
uint32_t pause { 0 };
|
||||||
bool armed_blink { false };
|
bool armed_blink { false };
|
||||||
SignalToken signal_token_tick_second { };
|
SignalToken signal_token_tick_second { };
|
||||||
|
|
||||||
|
wav_file_t * get_wav(uint32_t index);
|
||||||
|
bool check_wav_validity(const std::string dir, const std::string file);
|
||||||
void on_voice_changed(size_t index);
|
void on_voice_changed(size_t index);
|
||||||
void on_tick_second();
|
void on_tick_second();
|
||||||
void on_tuning_frequency_changed(rf::Frequency f);
|
|
||||||
void prepare_audio();
|
void prepare_audio();
|
||||||
void start_tx();
|
void start_tx();
|
||||||
|
|
||||||
// Schedule: save on sd card
|
|
||||||
// For each day of the week, max 8 messages ?
|
|
||||||
// For each message: Normal, accent. Can chose accent on first or last digit
|
|
||||||
// Prelude with number of repeats
|
|
||||||
// Message 1 with number of repeats
|
|
||||||
// Interlude ?
|
|
||||||
// Message 2 with number of repeats
|
|
||||||
// End
|
|
||||||
// Frequency list and sequence
|
|
||||||
|
|
||||||
Labels labels {
|
Labels labels {
|
||||||
{ { 2 * 8, 5 * 8 }, "Voice: Flags:", Color::light_grey() },
|
{ { 2 * 8, 5 * 8 }, "Voice: Flags:", Color::light_grey() },
|
||||||
{ { 1 * 8, 8 * 8 }, "Code:", Color::light_grey() }
|
{ { 1 * 8, 8 * 8 }, "Code:", Color::light_grey() }
|
||||||
|
@ -29,57 +29,67 @@
|
|||||||
|
|
||||||
void ADSBTXProcessor::execute(const buffer_c8_t& buffer) {
|
void ADSBTXProcessor::execute(const buffer_c8_t& buffer) {
|
||||||
|
|
||||||
// This is called at 2M/2048 = 977Hz
|
// This is called at 4M/2048 = 1953Hz
|
||||||
|
// One pulse = 500ns = 2 samples
|
||||||
|
// One bit = 2 pulses = 1us = 4 samples
|
||||||
|
|
||||||
if (!configured) return;
|
if (!configured) return;
|
||||||
|
|
||||||
for (size_t i = 0; i < buffer.count; i++) {
|
for (size_t i = 0; i < buffer.count; i++) {
|
||||||
|
|
||||||
// Synthesis at 2M
|
if (!sample) {
|
||||||
/*if (preamble == true) {
|
sample = 3;
|
||||||
if (bit_pos >= 16) {
|
|
||||||
bit_pos = 0;
|
if (active) {
|
||||||
preamble = false;
|
if (preamble) {
|
||||||
bit_part = 0;
|
if (bit_pos >= 16) {
|
||||||
} else {
|
preamble = false;
|
||||||
cur_bit = (preamble_parts << bit_pos) >> 15;
|
bit_pos = 0;
|
||||||
bit_pos++;
|
} else {
|
||||||
}
|
cur_bit = (preamble_parts << bit_pos) >> 15;
|
||||||
}*/
|
bit_pos++;
|
||||||
//if (preamble == false) {
|
}
|
||||||
if (!bit_part) {
|
}
|
||||||
if (bit_pos >= 112) {
|
|
||||||
// Stop
|
if (!preamble) {
|
||||||
message.progress = 200;
|
if (bit_pos >= 112) {
|
||||||
shared_memory.application_queue.push(message);
|
active = false; // Stop
|
||||||
configured = false;
|
cur_bit = 0;
|
||||||
cur_bit = 0;
|
} else {
|
||||||
} else {
|
cur_bit = shared_memory.bb_data.data[bit_pos];
|
||||||
cur_bit = 0; //shared_memory.tx_data[bit_pos];
|
bit_pos++;
|
||||||
bit_pos++;
|
}
|
||||||
bit_part = 1;
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
bit_part = 0;
|
//cur_bit = 0;
|
||||||
cur_bit ^= 1;
|
if (bit_pos == 8192) { // ?
|
||||||
|
configured = false;
|
||||||
|
message.done = true;
|
||||||
|
shared_memory.application_queue.push(message);
|
||||||
|
}
|
||||||
|
bit_pos++;
|
||||||
}
|
}
|
||||||
//}
|
} else
|
||||||
|
sample--;
|
||||||
|
|
||||||
// 8D48: 10001101 01001000
|
if (sample == 1)
|
||||||
// 1001010110100110 0110010110010101
|
cur_bit ^= 1; // Invert
|
||||||
|
|
||||||
|
delta = tone_sample * fm_delta;
|
||||||
|
tone_sample += 128;
|
||||||
|
|
||||||
if (cur_bit) {
|
if (cur_bit) {
|
||||||
phase = (phase + 0x1FE00); // What ?
|
phase += delta;
|
||||||
sphase = phase + (64 << 18);
|
sphase = phase + (64 << 24);
|
||||||
|
|
||||||
re = (sine_table_i8[(sphase & 0x03FC0000) >> 18]);
|
re = (sine_table_i8[(sphase & 0xFF000000U) >> 24]);
|
||||||
im = (sine_table_i8[(phase & 0x03FC0000) >> 18]);
|
im = (sine_table_i8[(phase & 0xFF000000U) >> 24]);
|
||||||
} else {
|
} else {
|
||||||
re = 0;
|
re = 0;
|
||||||
im = 0;
|
im = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
buffer.p[i] = {(int8_t)re, (int8_t)im};
|
buffer.p[i] = {re, im};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -87,11 +97,14 @@ void ADSBTXProcessor::on_message(const Message* const p) {
|
|||||||
const auto message = *reinterpret_cast<const ADSBConfigureMessage*>(p);
|
const auto message = *reinterpret_cast<const ADSBConfigureMessage*>(p);
|
||||||
|
|
||||||
if (message.id == Message::ID::ADSBConfigure) {
|
if (message.id == Message::ID::ADSBConfigure) {
|
||||||
bit_part = 0;
|
|
||||||
bit_pos = 0;
|
bit_pos = 0;
|
||||||
|
sample = 0;
|
||||||
cur_bit = 0;
|
cur_bit = 0;
|
||||||
preamble = true;
|
preamble = true;
|
||||||
|
active = true;
|
||||||
configured = true;
|
configured = true;
|
||||||
|
|
||||||
|
fm_delta = 50000 * (0xFFFFFFULL / 4000000); // Fixed bw for now
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -26,6 +26,8 @@
|
|||||||
#include "baseband_processor.hpp"
|
#include "baseband_processor.hpp"
|
||||||
#include "baseband_thread.hpp"
|
#include "baseband_thread.hpp"
|
||||||
|
|
||||||
|
#define TEST_F2D(f) (uint32_t)((f) * ((1ULL << 32) / 4000000))
|
||||||
|
|
||||||
class ADSBTXProcessor : public BasebandProcessor {
|
class ADSBTXProcessor : public BasebandProcessor {
|
||||||
public:
|
public:
|
||||||
void execute(const buffer_c8_t& buffer) override;
|
void execute(const buffer_c8_t& buffer) override;
|
||||||
@ -35,18 +37,20 @@ public:
|
|||||||
private:
|
private:
|
||||||
bool configured = false;
|
bool configured = false;
|
||||||
|
|
||||||
BasebandThread baseband_thread { 2000000, this, NORMALPRIO + 20, baseband::Direction::Transmit }; // 2280000
|
BasebandThread baseband_thread { 4000000, this, NORMALPRIO + 20, baseband::Direction::Transmit };
|
||||||
|
|
||||||
const uint16_t preamble_parts = 0b1010000101000000;
|
const uint16_t preamble_parts = 0b1010000101000000;
|
||||||
uint8_t bit_part;
|
bool preamble { }, active { };
|
||||||
bool preamble;
|
uint16_t bit_pos { 0 };
|
||||||
int8_t re, im;
|
uint8_t cur_bit { 0 };
|
||||||
uint16_t bit_pos = 0;
|
uint32_t sample { 0 };
|
||||||
uint8_t cur_bit = 0;
|
uint32_t tone_phase { 0 };
|
||||||
uint32_t phase, sphase;
|
uint32_t fm_delta { 0 };
|
||||||
int32_t sig, frq;
|
uint32_t phase { 0 }, sphase { 0 };
|
||||||
|
int32_t tone_sample { 0 }, delta { 0 };
|
||||||
|
int8_t re { }, im { };
|
||||||
|
|
||||||
TXDoneMessage message;
|
TXDoneMessage message { };
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
Binary file not shown.
Loading…
Reference in New Issue
Block a user