CTCSS in soundboard. 24 jammer chs instead of 9.

Soundboard random mode now cares about loop option.
Started documenting UI.
This commit is contained in:
furrtek 2017-02-02 09:29:14 +00:00
parent 703d8044a3
commit 607e6c5bd4
16 changed files with 305 additions and 142 deletions

171
docs/widgets.txt Normal file
View File

@ -0,0 +1,171 @@
~ Pretty ugly PortaPack H1 (+HAVOC) UI widget documentation ~
by @furrtek - v0.1 - 2017/02/02
Not approved at all by @Sharebrained
The display is 240 x 320 pixels.
A View is basically a rectangular zone on the screen.
Widgets (aka controls, UI elements...) go into Views.
Views can be pushed to/popped from a stack.
The main menu view is at the bottom of the stack and can't be popped.
The title bar view is a special case, it (normally) can't be hidden by pushed Views.
Since the title bar is 16 pixel high, a regular full-screen view is 240 x 304 pixels.
If the current View can be popped (removed), an arrow shows on the left of the title bar.
Kind of widgets:
==== Rectangle ====
A simple filled or outlined rectangle. No interactivity.
Constructors:
- Empty
- Rectangle(Rect, Color)
Methods:
- set_color(Color)
- set_outline(bool): Default is filled, set to true for outline only
==== Text ====
A simple text label. No interactivity.
Constructors:
- Empty
- Text(Rect)
- Text(Rect, std::string)
Methods:
- set(std::string): Updates text
==== BigFrequency (HAVOC) ====
Displays a big 7-segment style frequency value in the XXXX.xxxMHz format.
If the value is 0, ----.--- is displayed. No leading zeros. No interactivity.
Constructors:
- BigFrequency(Rect, rf::Frequency)
Methods:
- set(rf::Frequency): Updates value
==== ProgressBar (HAVOC) ====
Displays progress as an horizontal progress bar with value and maximum value.
No interactivity.
Constructors:
- ProgressBar(Rect)
Methods:
- set_max(uint32_t): Updates maximum value
- set_value(uint32_t): Updates current value
The maximum value is set by default to 100.
==== Console ====
Displays a scrolling text console. No interactivity.
Constructors:
- Console(Rect)
Methods:
- clear(): Clears text and resets cursor to top-left
- write(std::string): Adds text
- writeln(std::string): Adds text and CR + LF
==== Checkbox (HAVOC) ====
A toggle option shown as a tickable box with a text label.
Interactive: select and touch.
Constructors:
- Empty
- Checkbox(Point, size_t, std::string)
- Checkbox(Point, size_t, std::string, bool)
(Position, length of text label, text, regular size/small)
Methods:
- bool value(): Returns state
- set_text(std::string): Updates text label
- set_value(bool): Sets state
Callbacks:
- on_select: Select key press or touch. Returns state
==== Button ====
A momentary push-button. Interactive: select and touch.
Constructors:
- Empty
- Button(Rect, std::string)
Methods:
- std::string text(): Returns text
- set_text(std::string): Updates text
Callbacks:
- on_select: Select key press or touch
- on_dir: Direction keys press
- on_highlight: Focus get
==== Image ====
Shows a 1-bit bitmap. No interactivity.
Constructors:
- Empty
- Image(Rect, Bitmap*, Color, Color)
(Rect, pointer to bitmap (see bitmap.hpp), foreground, background)
Methods:
- set_bitmap(Bitmap*): Updates bitmap
- set_foreground(Color): Changes foreground color
- set_background(Color): Changes background color
- invert_colors(): Flip foreground color with background
==== ImageButton ====
Same as the Image widget, except interactive (select and touch). No methods.
Constructors:
- Empty
- ImageButton(Rect, Bitmap*, Color, Color)
(Rect, pointer to bitmap (see bitmap.hpp), foreground, background)
Callbacks:
- on_select: Select key press or touch
==== ImageOptionsField (HAVOC) ====
Options list displayed as images.
Interactive: change (jog wheel) and focus.
Constructors:
- Empty
- ImageOptionsField(Rect, options_t)
options_t is a std::vector of std::pair of unsigned char pointers to color bitmap data and an int32_t
Methods:
- set_options(options_t)
- size_t selected_index()
- size_t selected_index_value()
- set_selected_index(size_t)
- set_by_value(value_t)
Callbacks:
- on_change: Option was changed
- on_show_options: Focus get
==== OptionsField ====
==== NumberField ====
==== SymField (HAVOC) ====
==== Waveform (HAVOC) ====

View File

@ -22,55 +22,55 @@
#include "ctcss.hpp"
ctcss_tone ctcss_tones[CTCSS_TONES_NB] = {
{ "XZ", 0, 67000 },
{ "WZ", 1, 69400 },
{ "XA", 39, 71900 },
{ "WA", 3, 74400 },
{ "XB", 4, 77000 },
{ "WB", 5, 79700 },
{ "YZ", 6, 82500 },
{ "YA", 7, 85400 },
{ "YB", 8, 88500 },
{ "ZZ", 9, 91500 },
{ "ZA", 10, 94800 },
{ "ZB", 11, 97400 },
{ "1Z", 12, 100000 },
{ "1A", 13, 103500 },
{ "1B", 14, 107200 },
{ "2Z", 15, 110900 },
{ "2Z", 16, 114800 },
{ "2B", 17, 118800 },
{ "3Z", 18, 123000 },
{ "3A", 19, 127300 },
{ "3B", 20, 131800 },
{ "4Z", 21, 136500 },
{ "4A", 22, 141300 },
{ "4B", 23, 146200 },
{ "5Z", 24, 151400 },
{ "5A", 25, 156700 },
{ "--", 40, 159800 },
{ "5B", 26, 162200 },
{ "--", 41, 165500 },
{ "6Z", 27, 167900 },
{ "--", 42, 171300 },
{ "6A", 28, 173800 },
{ "--", 43, 177300 },
{ "6B", 29, 179900 },
{ "--", 44, 183500 },
{ "7Z", 30, 186200 },
{ "--", 45, 189900 },
{ "7A", 31, 192800 },
{ "--", 46, 196600 },
{ "--", 47, 199500 },
{ "M1", 32, 203500 },
{ "8Z", 48, 206500 },
{ "M2", 33, 210700 },
{ "M3", 34, 218100 },
{ "M4", 35, 225700 },
{ "9Z", 49, 229100 },
{ "--", 36, 233600 },
{ "--", 37, 241800 },
{ "--", 38, 250300 },
{ "0Z", 50, 254100 }
const ctcss_tone ctcss_tones[CTCSS_TONES_NB] = {
{ "XZ", 0, 67.000 },
{ "WZ", 1, 69.400 },
{ "XA", 39, 71.900 },
{ "WA", 3, 74.400 },
{ "XB", 4, 77.000 },
{ "WB", 5, 79.700 },
{ "YZ", 6, 82.500 },
{ "YA", 7, 85.400 },
{ "YB", 8, 88.500 },
{ "ZZ", 9, 91.500 },
{ "ZA", 10, 94.800 },
{ "ZB", 11, 97.400 },
{ "1Z", 12, 100.000 },
{ "1A", 13, 103.500 },
{ "1B", 14, 107.200 },
{ "2Z", 15, 110.900 },
{ "2Z", 16, 114.800 },
{ "2B", 17, 118.800 },
{ "3Z", 18, 123.000 },
{ "3A", 19, 127.300 },
{ "3B", 20, 131.800 },
{ "4Z", 21, 136.500 },
{ "4A", 22, 141.300 },
{ "4B", 23, 146.200 },
{ "5Z", 24, 151.400 },
{ "5A", 25, 156.700 },
{ "--", 40, 159.800 },
{ "5B", 26, 162.200 },
{ "--", 41, 165.500 },
{ "6Z", 27, 167.900 },
{ "--", 42, 171.300 },
{ "6A", 28, 173.800 },
{ "--", 43, 177.300 },
{ "6B", 29, 179.900 },
{ "--", 44, 183.500 },
{ "7Z", 30, 186.200 },
{ "--", 45, 189.900 },
{ "7A", 31, 192.800 },
{ "--", 46, 196.600 },
{ "--", 47, 199.500 },
{ "M1", 32, 203.500 },
{ "8Z", 48, 206.500 },
{ "M2", 33, 210.700 },
{ "M3", 34, 218.100 },
{ "M4", 35, 225.700 },
{ "9Z", 49, 229.100 },
{ "--", 36, 233.600 },
{ "--", 37, 241.800 },
{ "--", 38, 250.300 },
{ "0Z", 50, 254.100 }
};

View File

@ -30,9 +30,9 @@
struct ctcss_tone {
char PL_code[3];
uint16_t num_code;
uint32_t frequency; // Hz * 1000
float frequency;
};
extern ctcss_tone ctcss_tones[CTCSS_TONES_NB];
extern const ctcss_tone ctcss_tones[CTCSS_TONES_NB];
#endif/*__CTCSS_H_*/

View File

@ -32,7 +32,8 @@
#include <cstring>
#include <stdio.h>
#define JAMMER_CH_WIDTH 500000
#define JAMMER_CH_WIDTH 1000000
#define JAMMER_MAX_CH 24
using namespace portapack;
@ -95,7 +96,7 @@ void JammerView::update_button(const uint32_t n) {
void JammerView::on_retune(const rf::Frequency freq, const uint32_t range) {
if (freq) {
transmitter_model.set_tuning_frequency(freq);
text_range_number.set(to_string_dec_uint(range, 1));
text_range_number.set(to_string_dec_uint(range, 2));
}
}
@ -148,8 +149,10 @@ JammerView::JammerView(NavigationView& nav) {
value_ptr = &frequency_range[id].min;
auto new_view = nav.push<FrequencyKeypadView>(*value_ptr);
new_view->on_changed = [this, value_ptr](rf::Frequency f) {
new_view->on_changed = [this, value_ptr, &button](rf::Frequency f) {
*value_ptr = f;
update_button(button.id);
update_range(button.id >> 1);
};
};
@ -235,10 +238,10 @@ JammerView::JammerView(NavigationView& nav) {
} else {
// Disable all ranges by default
for (c = 0; c < 9; c++)
for (c = 0; c < JAMMER_MAX_CH; c++)
jammer_channels[c].enabled = false;
// Generate jamming "channels", maximum: 9
// Generate jamming "channels", maximum: JAMMER_MAX_CH
// Convert ranges min/max to center/bw
for (size_t r = 0; r < 3; r++) {
@ -260,24 +263,24 @@ JammerView::JammerView(NavigationView& nav) {
} while (range_bw_sub >= JAMMER_CH_WIDTH);
ch_width = range_bw / num_channels;
for (c = 0; c < num_channels; c++) {
if (i >= 9) {
if (i >= JAMMER_MAX_CH) {
out_of_ranges = true;
break;
}
jammer_channels[i].enabled = true;
jammer_channels[i].width = ch_width;
jammer_channels[i].width = (ch_width * 0xFFFFFFULL) / 1536000;
jammer_channels[i].center = start_freq + (ch_width / 2) + (ch_width * c);
jammer_channels[i].duration = 15360 * options_hop.selected_index_value();
jammer_channels[i].duration = 30720 * options_hop.selected_index_value();
i++;
}
} else {
if (i >= 9) {
if (i >= JAMMER_MAX_CH) {
out_of_ranges = true;
} else {
jammer_channels[i].enabled = true;
jammer_channels[i].width = range_bw;
jammer_channels[i].width = (range_bw * 0xFFFFFFULL) / 1536000;
jammer_channels[i].center = start_freq + (range_bw / 2);
jammer_channels[i].duration = 15360 * options_hop.selected_index_value();
jammer_channels[i].duration = 30720 * options_hop.selected_index_value();
i++;
}
}
@ -285,15 +288,15 @@ JammerView::JammerView(NavigationView& nav) {
}
if (!out_of_ranges) {
text_range_total.set("/" + to_string_dec_uint(i, 1));
text_range_total.set("/" + to_string_dec_uint(i, 2));
jamming = true;
button_transmit.set_style(&style_cancel);
button_transmit.set_text("STOP");
transmitter_model.set_sampling_rate(1536000U);
transmitter_model.set_sampling_rate(3072000U);
transmitter_model.set_rf_amp(true);
transmitter_model.set_baseband_bandwidth(1750000);
transmitter_model.set_baseband_bandwidth(3500000U);
transmitter_model.set_tx_gain(47);
transmitter_model.enable();

View File

@ -70,7 +70,7 @@ private:
// Free
{{ true, 945000000, 950000000 }, // GSM 900 BW:5M
{ false, 0, 0 },
{ true, 0, 0 }},
{ false, 0, 0 }},
// GSM-R
{{ true, 921000000, 925000000 }, // GSM 900 BW:4M
@ -174,12 +174,12 @@ private:
};
Text text_range_number {
{ 18 * 8, 4, 1 * 8, 16 },
"-"
{ 18 * 8, 4, 2 * 8, 16 },
"--"
};
Text text_range_total {
{ 19 * 8, 4, 2 * 8, 16 },
"/-"
{ 20 * 8, 4, 3 * 8, 16 },
"/--"
};
Text text_speed {

View File

@ -352,8 +352,8 @@ SystemMenuView::SystemMenuView(NavigationView& nav) {
add_items<12>({ {
{ "Play dead", ui::Color::red(), &bitmap_icon_playdead, [&nav](){ nav.push<PlayDeadView>(); } },
{ "Receivers", ui::Color::cyan(), &bitmap_icon_receiver, [&nav](){ nav.push<ReceiverMenuView>(); } },
{ "Capture", ui::Color::cyan(), &bitmap_icon_capture, [&nav](){ nav.push<CaptureAppView>(); } },
{ "Replay", ui::Color::blue(), &bitmap_icon_replay, [&nav](){ nav.push<ReplayAppView>(); } },
{ "Capture", ui::Color::cyan(), &bitmap_icon_capture, [&nav](){ nav.push<NotImplementedView>(); } }, //CaptureAppView
{ "Replay", ui::Color::grey(), &bitmap_icon_replay, [&nav](){ nav.push<ReplayAppView>(); } },
{ "Code transmitters", ui::Color::green(), &bitmap_icon_codetx, [&nav](){ nav.push<TransmitterCodedMenuView>(); } },
{ "Audio transmitters", ui::Color::green(), &bitmap_icon_audiotx, [&nav](){ nav.push<TransmitterAudioMenuView>(); } },
{ "Close Call", ui::Color::orange(),&bitmap_icon_closecall, [&nav](){ nav.push<CloseCallView>(); } },

View File

@ -38,11 +38,12 @@ using namespace portapack;
namespace ui {
void SoundBoardView::do_random() {
uint16_t id;
uint32_t id;
chThdSleepMilliseconds(300); // 300ms
chThdSleepMilliseconds(500);
id = lfsr_iterate(lfsr_v) % max_sound;
lfsr_v = lfsr_iterate(lfsr_v);
id = lfsr_v % max_sound;
play_sound(id);
@ -67,6 +68,7 @@ void SoundBoardView::prepare_audio() {
} else {
pbar.set_value(0);
transmitter_model.disable();
if (check_loop.value())
do_random();
}
}
@ -98,7 +100,7 @@ void SoundBoardView::on_tuning_frequency_changed(rf::Frequency f) {
}
void SoundBoardView::play_sound(uint16_t id) {
uint32_t ctcss_option;
uint32_t ctcss_index;
bool ctcss_enabled;
uint32_t divider;
@ -122,9 +124,9 @@ void SoundBoardView::play_sound(uint16_t id) {
transmitter_model.set_baseband_bandwidth(1750000);
transmitter_model.enable();
ctcss_option = options_ctcss.selected_index();
ctcss_index = options_ctcss.selected_index();
if (ctcss_option)
if (ctcss_index)
ctcss_enabled = true;
else
ctcss_enabled = false;
@ -133,9 +135,9 @@ void SoundBoardView::play_sound(uint16_t id) {
baseband::set_audiotx_data(
divider,
number_bw.value(),
number_bw.value() * 1000,
ctcss_enabled,
(67109.0 * (float)_ctcss_freq)/1536000.0 // TODO: Might not be precise enough
(uint32_t)((ctcss_tones[ctcss_index].frequency / 1536000.0) * 0xFFFFFFFFULL)
);
}
@ -185,10 +187,6 @@ void SoundBoardView::change_page(Button& button, const KeyEvent key) {
}
}
void SoundBoardView::on_ctcss_changed(uint32_t v) {
_ctcss_freq = v;
}
SoundBoardView::SoundBoardView(
NavigationView& nav
) : nav_ (nav)
@ -249,9 +247,6 @@ SoundBoardView::SoundBoardView(
options_ctcss.set_options(ctcss_options);
options_ctcss.on_change = [this](size_t, OptionsField::value_t v) {
this->on_ctcss_changed(v);
};
options_ctcss.set_selected_index(0);
const auto button_fn = [this](Button& button) {

View File

@ -73,17 +73,15 @@ private:
uint32_t sample_duration { 0 };
uint8_t page = 0;
uint16_t lfsr_v = 0x1337;
uint32_t lfsr_v = 0x13377331;
std::unique_ptr<WAVFileReader> reader { };
sound sounds[105];
uint8_t max_sound { 0 };
uint8_t max_page { 0 };
uint32_t max_sound { };
uint8_t max_page { };
uint32_t _ctcss_freq { 0 };
int8_t audio_buffer[2048];
int8_t audio_buffer[1024];
Style style_a {
.font = font::fixed_8x16,

View File

@ -83,8 +83,8 @@ private:
NumberField field_bw {
{ 14 * 8, 0 * 16 },
2,
{ 1, 99 },
3,
{ 1, 150 },
1,
' '
};

View File

@ -37,18 +37,13 @@ void AudioTXProcessor::execute(const buffer_c8_t& buffer){
//ai = 0;
for (size_t i = 0; i<buffer.count; i++) {
// Audio preview sample generation: 1536000/divider = samplerate
// Audio preview sample generation @ 1536000/divider
if (!as) {
as = divider;
audio_fifo.out(sample);
audio_fifo.out(out_sample);
sample = (int32_t)out_sample;
//preview_audio_buffer.p[ai++] = sample << 8;
if (ctcss_enabled) {
ctcss_sample = sine_table_i8[(ctcss_phase & 0x03FC0000) >> 18];
int16_t mix = (sample * 218) + (ctcss_sample * 37); // ~15%
sample = mix >> 8;
}
if ((audio_fifo.len() < 1024) && (asked == false)) {
// Ask application to fill up fifo
sigmessage.signaltype = 1;
@ -59,36 +54,38 @@ void AudioTXProcessor::execute(const buffer_c8_t& buffer){
as--;
}
if (ctcss_enabled) {
ctcss_sample = sine_table_i8[(ctcss_phase & 0xFF000000U) >> 24];
sample_mixed = ((sample * 217) + (ctcss_sample * 38)) / 256; // ~15%
ctcss_phase += ctcss_phase_inc;
} else {
sample_mixed = sample;
}
// FM
frq = sample * bw;
delta = sample_mixed * fm_delta;
phase = (phase + frq);
sphase = phase + (64 << 18);
phase += delta;
sphase = phase + (64 << 24);
re = (sine_table_i8[(sphase & 0x03FC0000) >> 18]);
im = (sine_table_i8[(phase & 0x03FC0000) >> 18]);
re = (sine_table_i8[(sphase & 0xFF000000U) >> 24]);
im = (sine_table_i8[(phase & 0xFF000000U) >> 24]);
buffer.p[i] = {(int8_t)re, (int8_t)im};
buffer.p[i] = {re, im};
}
//AudioOutput::fill_audio_buffer(preview_audio_buffer, true);
}
void AudioTXProcessor::on_message(const Message* const msg) {
const auto message = static_cast<const AudioTXConfigMessage*>(msg);
const auto message = *reinterpret_cast<const AudioTXConfigMessage*>(msg);
switch(msg->id) {
case Message::ID::AudioTXConfig:
// 1<<18 = 262144
// m = (262144 * a) / 1536000
// a = 262144 / 1536000 (*1000 = 171)
bw = 171 * (message->bw);
divider = message->divider;
ctcss_phase_inc = message->ctcss_phase_inc;
ctcss_enabled = message->ctcss_enabled;
fm_delta = message.fm_delta * (0xFFFFFFULL / 1536000);
divider = message.divider;
ctcss_enabled = message.ctcss_enabled;
ctcss_phase_inc = message.ctcss_phase_inc;
as = 0;
configured = true;

View File

@ -38,19 +38,21 @@ private:
BasebandThread baseband_thread { 1536000, this, NORMALPRIO + 20, baseband::Direction::Transmit };
int8_t audio_fifo_data[2048];
int8_t audio_fifo_data[2048] { };
FIFO<int8_t> audio_fifo = { audio_fifo_data, 11 }; // 43ms @ 48000Hz
uint32_t bw;
uint32_t divider;
uint8_t as = 0;
uint32_t ctcss_phase_inc;
bool ctcss_enabled;
uint32_t fm_delta { 0 };
uint32_t divider { };
uint32_t as { 0 };
bool ctcss_enabled { false };
uint32_t ctcss_phase_inc { };
uint32_t ctcss_phase { 0 }, phase { 0 }, sphase { 0 };
int8_t out_sample { };
int32_t ctcss_sample { 0 }, sample { 0 }, sample_mixed { }, delta { };
int8_t re, im;
int8_t ctcss_sample, sample;
int8_t re { 0 }, im { 0 };
bool asked = false;
bool asked { false };
//int16_t audio_data[64];
/*const buffer_s16_t preview_audio_buffer {
@ -58,10 +60,7 @@ private:
sizeof(int16_t)*64
};*/
FIFOSignalMessage sigmessage;
uint32_t ctcss_phase, phase, sphase;
int32_t frq;
FIFOSignalMessage sigmessage { };
};
#endif

View File

@ -36,7 +36,7 @@ void JammerProcessor::execute(const buffer_c8_t& buffer) {
// Find next enabled range
do {
current_range++;
if (current_range == 9) current_range = 0;
if (current_range == 24) current_range = 0; // Warning ! Should match JAMMER_MAX_CH
} while (!jammer_channels[current_range].enabled);
jammer_duration = jammer_channels[current_range].duration;

View File

@ -36,7 +36,7 @@ public:
private:
bool configured = false;
BasebandThread baseband_thread { 1536000, this, NORMALPRIO + 20, baseband::Direction::Transmit };
BasebandThread baseband_thread { 3072000, this, NORMALPRIO + 20, baseband::Direction::Transmit };
JammerChannel * jammer_channels { };

View File

@ -630,19 +630,19 @@ class AudioTXConfigMessage : public Message {
public:
constexpr AudioTXConfigMessage(
const uint32_t divider,
const uint32_t bw,
const uint32_t fm_delta,
const uint32_t ctcss_phase_inc,
const bool ctcss_enabled
) : Message { ID::AudioTXConfig },
divider(divider),
bw(bw),
fm_delta(fm_delta),
ctcss_phase_inc(ctcss_phase_inc),
ctcss_enabled(ctcss_enabled)
{
}
const uint32_t divider;
const uint32_t bw;
const uint32_t fm_delta;
const uint32_t ctcss_phase_inc;
const bool ctcss_enabled;
};

View File

@ -62,9 +62,9 @@ struct SharedMemory {
union {
ToneData tones_data;
JammerChannel jammer_channels[9];
JammerChannel jammer_channels[24];
uint8_t data[512];
} bb_data { { { { 0, 0 } }, 0, { 0 } } };
} bb_data { { { { 0, 0 } }, 0, { 0 } } }; // { } ?
};
extern SharedMemory& shared_memory;

Binary file not shown.