mirror of
https://github.com/eried/portapack-mayhem.git
synced 2024-10-01 01:26:06 -04:00
# This is a combination of 2 commits.
# The first commit's message is: Updated RDS transmitter: flags, PI and date/time Merging baseband audio tone generators Merging DTMF baseband with "tones" baseband Added stealth transmit mode App flash section bumped to 512k RX and TX LEDs are now used Play dead should work again, added login option Morse frame gen. for letters and fox hunt codes Merged EPAR with Xylos Made EPAR use encoders for frame gen. Moved OOK encoders data in encoders.hpp Simplified about screen, ui_about_demo.* files are still there BHT city DB, keywords removed BHT cities DB, keywords removed Update README.md RDS radiotext and time group generators # This is the 2nd commit message: Update README.md
This commit is contained in:
parent
0b13283d5d
commit
6bcb7dc1b1
@ -1,26 +1,35 @@
|
||||
# CMAKE generated file: DO NOT EDIT!
|
||||
# Generated by "Unix Makefiles" Generator, CMake Version 2.8
|
||||
# Generated by "Unix Makefiles" Generator, CMake Version 3.5
|
||||
|
||||
# Default target executed when no arguments are given to make.
|
||||
default_target: all
|
||||
|
||||
.PHONY : default_target
|
||||
|
||||
# Allow only one "make -f Makefile2" at a time, but pass parallelism.
|
||||
.NOTPARALLEL:
|
||||
|
||||
|
||||
#=============================================================================
|
||||
# Special targets provided by cmake.
|
||||
|
||||
# Disable implicit rules so canonical targets will work.
|
||||
.SUFFIXES:
|
||||
|
||||
|
||||
# Remove some rules from gmake that .SUFFIXES does not remove.
|
||||
SUFFIXES =
|
||||
|
||||
.SUFFIXES: .hpux_make_needs_suffix_list
|
||||
|
||||
|
||||
# Suppress display of executed commands.
|
||||
$(VERBOSE).SILENT:
|
||||
|
||||
|
||||
# A target that is always out of date.
|
||||
cmake_force:
|
||||
|
||||
.PHONY : cmake_force
|
||||
|
||||
#=============================================================================
|
||||
@ -49,12 +58,13 @@ CMAKE_BINARY_DIR = /home/furrtek/portapack-hackrf
|
||||
|
||||
# Special rule for the target edit_cache
|
||||
edit_cache:
|
||||
@$(CMAKE_COMMAND) -E cmake_echo_color --switch=$(COLOR) --cyan "Running interactive CMake command-line interface..."
|
||||
/usr/bin/cmake -i .
|
||||
@$(CMAKE_COMMAND) -E cmake_echo_color --switch=$(COLOR) --cyan "No interactive CMake dialog available..."
|
||||
/usr/bin/cmake -E echo No\ interactive\ CMake\ dialog\ available.
|
||||
.PHONY : edit_cache
|
||||
|
||||
# Special rule for the target edit_cache
|
||||
edit_cache/fast: edit_cache
|
||||
|
||||
.PHONY : edit_cache/fast
|
||||
|
||||
# Special rule for the target rebuild_cache
|
||||
@ -65,6 +75,7 @@ rebuild_cache:
|
||||
|
||||
# Special rule for the target rebuild_cache
|
||||
rebuild_cache/fast: rebuild_cache
|
||||
|
||||
.PHONY : rebuild_cache/fast
|
||||
|
||||
# The main all target
|
||||
@ -81,6 +92,7 @@ clean:
|
||||
|
||||
# The main clean target
|
||||
clean/fast: clean
|
||||
|
||||
.PHONY : clean/fast
|
||||
|
||||
# Prepare targets for installation.
|
||||
@ -105,6 +117,7 @@ firmware/CMakeFiles/firmware.dir/rule:
|
||||
|
||||
# Convenience name for target.
|
||||
firmware: firmware/CMakeFiles/firmware.dir/rule
|
||||
|
||||
.PHONY : firmware
|
||||
|
||||
# fast build rule for target.
|
||||
@ -119,6 +132,7 @@ firmware/CMakeFiles/program.dir/rule:
|
||||
|
||||
# Convenience name for target.
|
||||
program: firmware/CMakeFiles/program.dir/rule
|
||||
|
||||
.PHONY : program
|
||||
|
||||
# fast build rule for target.
|
||||
@ -133,6 +147,7 @@ firmware/CMakeFiles/release.dir/rule:
|
||||
|
||||
# Convenience name for target.
|
||||
release: firmware/CMakeFiles/release.dir/rule
|
||||
|
||||
.PHONY : release
|
||||
|
||||
# fast build rule for target.
|
||||
@ -149,8 +164,8 @@ help:
|
||||
@echo "... edit_cache"
|
||||
@echo "... firmware"
|
||||
@echo "... program"
|
||||
@echo "... rebuild_cache"
|
||||
@echo "... release"
|
||||
@echo "... rebuild_cache"
|
||||
.PHONY : help
|
||||
|
||||
|
||||
|
@ -1,5 +1,6 @@
|
||||
#
|
||||
# Copyright (C) 2014 Jared Boone, ShareBrained Technology, Inc.
|
||||
# Copyright (C) 2016 Furrtek
|
||||
#
|
||||
# This file is part of PortaPack.
|
||||
#
|
||||
@ -152,7 +153,9 @@ set(CPPSRC
|
||||
ui_alphanum.cpp
|
||||
ui_audio.cpp
|
||||
ui_baseband_stats_view.cpp
|
||||
ui_bht_tx.cpp
|
||||
ui_channel.cpp
|
||||
ui_closecall.cpp
|
||||
ui_debug.cpp
|
||||
ui_encoders.cpp
|
||||
ui_font_fixed_8x16.cpp
|
||||
@ -161,6 +164,7 @@ set(CPPSRC
|
||||
ui_jammer.cpp
|
||||
ui_lcr.cpp
|
||||
ui_menu.cpp
|
||||
ui_morse.cpp
|
||||
ui_navigation.cpp
|
||||
ui_numbers.cpp
|
||||
ui_nuoptix.cpp
|
||||
@ -176,8 +180,7 @@ set(CPPSRC
|
||||
ui_textentry.cpp
|
||||
ui_touch_calibration.cpp
|
||||
ui_whipcalc.cpp
|
||||
ui_xylos.cpp
|
||||
ui_closecall.cpp
|
||||
ui_whistle.cpp
|
||||
# ui_loadmodule.cpp
|
||||
recent_entries.cpp
|
||||
receiver_model.cpp
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -79,10 +79,14 @@ void WFMConfig::apply() const {
|
||||
audio::set_rate(audio::Rate::Hz_48000);
|
||||
}
|
||||
|
||||
void set_ccir_data( const uint32_t samples_per_tone, const uint16_t tone_count) {
|
||||
const CCIRConfigureMessage message {
|
||||
samples_per_tone,
|
||||
tone_count
|
||||
void set_tones_data(const uint64_t bw, const uint32_t pre_silence, const uint16_t tone_count,
|
||||
const bool dual_tone, const bool audio_out) {
|
||||
const TonesConfigureMessage message {
|
||||
(uint32_t)(262144 * bw) / 1536000,
|
||||
pre_silence,
|
||||
tone_count,
|
||||
dual_tone,
|
||||
audio_out
|
||||
};
|
||||
send_message(&message);
|
||||
}
|
||||
@ -158,14 +162,14 @@ void set_rds_data(const uint16_t message_length) {
|
||||
send_message(&message);
|
||||
}
|
||||
|
||||
void set_dtmf_data(const uint32_t bw, const uint32_t tone_length, const uint32_t pause_length) {
|
||||
/*void set_dtmf_data(const uint32_t bw, const uint32_t tone_length, const uint32_t pause_length) {
|
||||
const DTMFTXConfigMessage message {
|
||||
bw,
|
||||
tone_length,
|
||||
pause_length
|
||||
};
|
||||
send_message(&message);
|
||||
}
|
||||
}*/
|
||||
|
||||
static bool baseband_image_running = false;
|
||||
|
||||
|
@ -53,7 +53,8 @@ struct WFMConfig {
|
||||
void apply() const;
|
||||
};
|
||||
|
||||
void set_ccir_data( const uint32_t samples_per_tone, const uint16_t tone_count);
|
||||
void set_tones_data(const uint64_t bw, const uint32_t pre_silence, const uint16_t tone_count,
|
||||
const bool dual_tone, const bool audio_out);
|
||||
void set_audiotx_data(const uint32_t divider, const uint32_t bw, const bool ctcss_enabled, const uint32_t ctcss_phase_inc);
|
||||
void set_fifo_data(const int8_t * data);
|
||||
void set_pwmrssi(int32_t avg, bool enabled);
|
||||
@ -64,7 +65,7 @@ void set_ook_data(const uint32_t stream_length, const uint32_t samples_per_bit,
|
||||
void set_pocsag();
|
||||
void set_adsb();
|
||||
void set_rds_data(const uint16_t message_length);
|
||||
void set_dtmf_data(const uint32_t bw, const uint32_t tone_length, const uint32_t pause_length);
|
||||
//void set_dtmf_data(const uint32_t bw, const uint32_t tone_length, const uint32_t pause_length);
|
||||
|
||||
void run_image(const portapack::spi_flash::image_tag_t image_tag);
|
||||
void shutdown();
|
||||
|
@ -27,6 +27,52 @@
|
||||
|
||||
namespace ui {
|
||||
|
||||
static constexpr uint8_t bitmap_stealth_data[] = {
|
||||
0x00, 0x00,
|
||||
0x00, 0x00,
|
||||
0xE0, 0x07,
|
||||
0xF8, 0x1F,
|
||||
0xFC, 0x3F,
|
||||
0xFC, 0x3F,
|
||||
0xE4, 0x27,
|
||||
0xC4, 0x23,
|
||||
0xEC, 0x37,
|
||||
0x7C, 0x3E,
|
||||
0x7C, 0x1E,
|
||||
0x2C, 0x0C,
|
||||
0x0C, 0x00,
|
||||
0x0C, 0x00,
|
||||
0x0C, 0x00,
|
||||
0x00, 0x00
|
||||
};
|
||||
|
||||
static constexpr Bitmap bitmap_stealth {
|
||||
{ 16, 16 }, bitmap_stealth_data
|
||||
};
|
||||
|
||||
static constexpr uint8_t bitmap_speaker_data[] = {
|
||||
0x00, 0x00,
|
||||
0x00, 0x20,
|
||||
0x00, 0x30,
|
||||
0x00, 0x38,
|
||||
0x00, 0x3C,
|
||||
0xDC, 0x3E,
|
||||
0xDC, 0x3F,
|
||||
0xDC, 0x3F,
|
||||
0xDC, 0x3F,
|
||||
0xDC, 0x3F,
|
||||
0xDC, 0x3E,
|
||||
0x00, 0x3C,
|
||||
0x00, 0x38,
|
||||
0x00, 0x30,
|
||||
0x00, 0x20,
|
||||
0x00, 0x00
|
||||
};
|
||||
|
||||
static constexpr Bitmap bitmap_speaker {
|
||||
{ 16, 16 }, bitmap_speaker_data
|
||||
};
|
||||
|
||||
static constexpr uint8_t bitmap_more_data[] = {
|
||||
0x00, 0x00,
|
||||
0xC0, 0x03,
|
||||
@ -78,15 +124,15 @@ static constexpr uint8_t bitmap_keyboard_data[] = {
|
||||
0x00, 0x00,
|
||||
0x00, 0x00,
|
||||
0x00, 0x00,
|
||||
0xF8, 0x0F,
|
||||
0x88, 0x08,
|
||||
0x88, 0x08,
|
||||
0x88, 0x08,
|
||||
0xFE, 0x3F,
|
||||
0x22, 0x22,
|
||||
0x22, 0x22,
|
||||
0x22, 0x22,
|
||||
0xFE, 0x3F,
|
||||
0xF0, 0x1F,
|
||||
0x10, 0x11,
|
||||
0x10, 0x11,
|
||||
0x10, 0x11,
|
||||
0xFC, 0x7F,
|
||||
0x44, 0x44,
|
||||
0x44, 0x44,
|
||||
0x44, 0x44,
|
||||
0xFC, 0x7F,
|
||||
0x00, 0x00,
|
||||
0x00, 0x00,
|
||||
0x00, 0x00,
|
||||
|
@ -1,4 +1,4 @@
|
||||
unsigned char demofont_bin[] = {
|
||||
const uint8_t demofont_bin[] = {
|
||||
0xff, 0xff, 0xff, 0xff, 0xc7, 0xe4, 0xeb, 0xa3, 0xc0, 0xef, 0x82, 0x65,
|
||||
0xcc, 0x6a, 0x50, 0xc8, 0x8c, 0x99, 0xa6, 0x78, 0x76, 0x8d, 0x57, 0x66,
|
||||
0xa5, 0x3f, 0x52, 0x7d, 0x2d, 0x66, 0x91, 0x19, 0x46, 0x72, 0x00, 0x5e,
|
||||
|
221
firmware/application/encoders.hpp
Normal file
221
firmware/application/encoders.hpp
Normal file
@ -0,0 +1,221 @@
|
||||
/*
|
||||
* Copyright (C) 2014 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 <cstring>
|
||||
#include <string>
|
||||
|
||||
#ifndef __ENCODERS_H__
|
||||
#define __ENCODERS_H__
|
||||
|
||||
namespace encoders {
|
||||
|
||||
#define ENC_TYPES_COUNT 14
|
||||
|
||||
struct encoder_def_t {
|
||||
std::string name; // Encoder chip ref/name
|
||||
std::string address_symbols; // "01", "01F"...
|
||||
std::string data_symbols; // Same
|
||||
uint16_t clk_per_symbol; // Oscillator periods per symbol
|
||||
uint16_t clk_per_fragment; // Oscillator periods per symbol fragment (state)
|
||||
std::vector<std::string> bit_format; // List of fragments for each symbol in previous *_symbols list order
|
||||
uint8_t word_length; // Total # of symbols (not counting sync)
|
||||
std::string word_format; // A for Address, D for Data, S for sync
|
||||
std::string sync; // Like bit_format
|
||||
uint32_t default_speed; // Default encoder clk frequency (often set by shitty resistor)
|
||||
uint8_t repeat_min; // Minimum repeat count
|
||||
uint16_t pause_symbols; // Length of pause between repeats in symbols
|
||||
};
|
||||
|
||||
// Warning ! If this is changed, make sure that the UM3750 index is still good in ui_bht_tx.cpp !
|
||||
const encoder_def_t encoder_defs[ENC_TYPES_COUNT] = {
|
||||
// PT2260-R2
|
||||
{
|
||||
"2260-R2",
|
||||
"01F", "01",
|
||||
1024, 128,
|
||||
{ "10001000", "11101110", "10001110" },
|
||||
12, "AAAAAAAAAADDS",
|
||||
"10000000000000000000000000000000",
|
||||
150000, 2,
|
||||
0
|
||||
},
|
||||
|
||||
// PT2260-R4
|
||||
{
|
||||
"2260-R4",
|
||||
"01F", "01",
|
||||
1024, 128,
|
||||
{ "10001000", "11101110", "10001110" },
|
||||
12, "AAAAAAAADDDDS",
|
||||
"10000000000000000000000000000000",
|
||||
150000, 2,
|
||||
0
|
||||
},
|
||||
|
||||
// PT2262
|
||||
{
|
||||
"2262 ",
|
||||
"01F", "01F",
|
||||
32, 4,
|
||||
{ "10001000", "11101110", "10001110" },
|
||||
12, "AAAAAAAAAAAAS",
|
||||
"10000000000000000000000000000000",
|
||||
20000, 4,
|
||||
0
|
||||
},
|
||||
|
||||
// 16-bit ?
|
||||
{
|
||||
"16-bit ",
|
||||
"01", "01",
|
||||
32, 8,
|
||||
{ "1110", "1000" }, // Opposite ?
|
||||
16, "AAAAAAAAAAAAAAAAS",
|
||||
"100000000000000000000",
|
||||
25000, 50,
|
||||
0 // ?
|
||||
},
|
||||
|
||||
// RT1527
|
||||
{
|
||||
"1527 ",
|
||||
"01", "01",
|
||||
128, 32,
|
||||
{ "1000", "1110" },
|
||||
24, "SAAAAAAAAAAAAAAAAAAAADDDD",
|
||||
"10000000000000000000000000000000",
|
||||
100000, 4,
|
||||
10 // ?
|
||||
},
|
||||
|
||||
// HK526E
|
||||
{
|
||||
"526 ",
|
||||
"01", "01",
|
||||
24, 8,
|
||||
{ "110", "100" },
|
||||
12, "AAAAAAAAAAAA",
|
||||
"",
|
||||
20000, 4,
|
||||
10 // ?
|
||||
},
|
||||
|
||||
// HT12E
|
||||
{
|
||||
"12E ",
|
||||
"01", "01",
|
||||
3, 1,
|
||||
{ "011", "001" },
|
||||
12, "SAAAAAAAADDDD",
|
||||
"0000000000000000000000000000000000001",
|
||||
3000, 4,
|
||||
10 // ?
|
||||
},
|
||||
|
||||
// VD5026 13 bits ?
|
||||
{
|
||||
"5026 ",
|
||||
"0123", "0123",
|
||||
128, 8,
|
||||
{ "1000000010000000", "1111111011111110", "1111111010000000", "1000000011111110" },
|
||||
12, "SAAAAAAAAAAAA",
|
||||
"000000000000000000000000000000000000000000000001", // ?
|
||||
100000, 4,
|
||||
10 // ?
|
||||
},
|
||||
|
||||
// UM3750
|
||||
{
|
||||
"UM3750 ",
|
||||
"01", "01",
|
||||
96, 32,
|
||||
{ "011", "001" },
|
||||
12, "SAAAAAAAAAAAA",
|
||||
"1",
|
||||
100000, 4,
|
||||
0 // ?
|
||||
},
|
||||
|
||||
// UM3758
|
||||
{
|
||||
"UM3758 ",
|
||||
"01F", "01",
|
||||
96, 16,
|
||||
{ "011011", "001001", "011001" },
|
||||
18, "SAAAAAAAAAADDDDDDDD",
|
||||
"1",
|
||||
160000, 4,
|
||||
10 // ?
|
||||
},
|
||||
|
||||
// BA5104
|
||||
{
|
||||
"BA5104 ",
|
||||
"01", "01",
|
||||
3072, 768,
|
||||
{ "1000", "1110" },
|
||||
9, "SDDAAAAAAA",
|
||||
"",
|
||||
455000, 4,
|
||||
10 // ?
|
||||
},
|
||||
|
||||
// MC145026
|
||||
{
|
||||
"145026 ",
|
||||
"01F", "01",
|
||||
16, 1,
|
||||
{ "0111111101111111", "0100000001000000", "0111111101000000" },
|
||||
9, "SAAAAADDDD",
|
||||
"000000000000000000",
|
||||
455000, 2,
|
||||
2
|
||||
},
|
||||
|
||||
// HT6*** TODO: Add individual variations
|
||||
{
|
||||
"HT6*** ",
|
||||
"01F", "01",
|
||||
198, 33,
|
||||
{ "011011", "001001", "001011" },
|
||||
18, "SAAAAAAAAAAAADDDDDD",
|
||||
"0000000000000000000000000000000000001011001011001",
|
||||
80000, 3,
|
||||
10 // ?
|
||||
},
|
||||
|
||||
// TC9148
|
||||
{
|
||||
"TC9148 ",
|
||||
"01", "01",
|
||||
48, 12,
|
||||
{ "1000", "1110", },
|
||||
12, "AAAAAAAAAAAA",
|
||||
"",
|
||||
455000, 3,
|
||||
10 // ?
|
||||
}
|
||||
};
|
||||
|
||||
} /* namespace encoders */
|
||||
|
||||
#endif/*__ENCODERS_H__*/
|
@ -92,6 +92,7 @@ private:
|
||||
static MessageHandlerMap message_map;
|
||||
Thread* EventDispatcher::thread_event_loop = nullptr;
|
||||
bool EventDispatcher::is_running = false;
|
||||
bool EventDispatcher::display_sleep = false;
|
||||
|
||||
EventDispatcher::EventDispatcher(
|
||||
ui::Widget* const top_widget,
|
||||
@ -130,7 +131,7 @@ void EventDispatcher::set_display_sleep(const bool sleep) {
|
||||
portapack::display.wake();
|
||||
portapack::io.lcd_backlight(true);
|
||||
}
|
||||
display_sleep = sleep;
|
||||
EventDispatcher::display_sleep = sleep;
|
||||
};
|
||||
|
||||
eventmask_t EventDispatcher::wait() {
|
||||
@ -187,8 +188,12 @@ void EventDispatcher::dispatch(const eventmask_t events) {
|
||||
if( events & EVT_MASK_SWITCHES ) {
|
||||
handle_switches();
|
||||
}
|
||||
|
||||
/*if( events & EVT_MASK_LCD_FRAME_SYNC ) {
|
||||
blink_timer();
|
||||
}*/
|
||||
|
||||
if( !display_sleep ) {
|
||||
if( !EventDispatcher::display_sleep ) {
|
||||
if( events & EVT_MASK_LCD_FRAME_SYNC ) {
|
||||
handle_lcd_frame_sync();
|
||||
}
|
||||
@ -286,7 +291,7 @@ void EventDispatcher::handle_switches() {
|
||||
|
||||
portapack::bl_tick_counter = 0;
|
||||
|
||||
if( display_sleep ) {
|
||||
if( EventDispatcher::display_sleep ) {
|
||||
// Swallow event, wake up display.
|
||||
if( switches_state.any() ) {
|
||||
set_display_sleep(false);
|
||||
@ -308,7 +313,7 @@ void EventDispatcher::handle_switches() {
|
||||
void EventDispatcher::handle_encoder() {
|
||||
portapack::bl_tick_counter = 0;
|
||||
|
||||
if( display_sleep ) {
|
||||
if( EventDispatcher::display_sleep ) {
|
||||
// Swallow event, wake up display.
|
||||
set_display_sleep(false);
|
||||
return;
|
||||
|
@ -52,7 +52,7 @@ public:
|
||||
void run();
|
||||
static void request_stop();
|
||||
|
||||
void set_display_sleep(const bool sleep);
|
||||
static void set_display_sleep(const bool sleep);
|
||||
|
||||
static inline void check_fifo_isr() {
|
||||
if( !shared_memory.application_queue.is_empty() ) {
|
||||
@ -101,7 +101,7 @@ private:
|
||||
uint32_t encoder_last = 0;
|
||||
static bool is_running;
|
||||
bool sd_card_present = false;
|
||||
bool display_sleep = false;
|
||||
static bool display_sleep;
|
||||
bool halt = false;
|
||||
|
||||
eventmask_t wait();
|
||||
@ -117,6 +117,7 @@ private:
|
||||
|
||||
void on_touch_event(ui::TouchEvent event);
|
||||
|
||||
//void blink_timer();
|
||||
void handle_lcd_frame_sync();
|
||||
void handle_switches();
|
||||
void handle_encoder();
|
||||
|
@ -23,38 +23,47 @@
|
||||
// Bitmaps generated with:
|
||||
// Gimp image > indexed colors (16), then "xxd -i *.bmp"
|
||||
|
||||
//TEST: RDS
|
||||
//TEST: Imperial in whipcalc
|
||||
//TEST: Numbers
|
||||
//TEST: Jammer
|
||||
//TODO: Morse coder/beacon
|
||||
|
||||
//BUG: Xylos doesn't play last tone ?
|
||||
//BUG (fixed ?): Soundboard crashes on exit if no wav files on sd card
|
||||
//BUG (fixed ?): No audio in about when shown second time
|
||||
//BUG: Unistroke text entry screen doesn't care about string max length parameter
|
||||
//BUG (fixed ?): Unistroke text entry screen doesn't care about string max length parameter
|
||||
//BUG: POCSAG RX sometimes misses the first codeword after SYNC
|
||||
//BUG: Check AFSK transmit end, skips last bits ?
|
||||
//BUG: RDS doesn't stop baseband when stopping tx ?
|
||||
|
||||
//TODO: Use ModalMessageView with yes/no for TX
|
||||
//TODO: Mousejack ?
|
||||
//TODO: Waveform widget ?
|
||||
//TODO: Frequency manager save/load (ui_freqman)
|
||||
//TODO: Move frequencykeypad from ui_receiver to ui_widget (used everywhere)
|
||||
//TODO: ADS-B draw trajectory + GPS coordinates + scale, and playback
|
||||
//TODO: Finish EPAR tx
|
||||
//TODO: Wav visualizer
|
||||
//TODO: Analog TV tx with camcorder font character generator
|
||||
//TODO: Test dual tone in proc_tones and remove proc_dtmf_tx
|
||||
//TODO: Morse coder for foxhunts
|
||||
//TODO: Make Morse coder and Whistle use proc_tones
|
||||
//TODO: RDS multiple groups (sequence)
|
||||
//TODO: Frequency manager
|
||||
//TODO: IQ replay
|
||||
//TODO: Use ModalMessageView confirmation for TX ?
|
||||
//TODO: Show address/data bit fields in OOK TX
|
||||
//TODO: Scan for OOK TX
|
||||
//TODO: Check more OOK encoders
|
||||
//TODO: POCSAG 512 and 2400 (all 3 at the same time, or parameter ?)
|
||||
//TODO: Check AFSK transmit end, skips last bits ?
|
||||
//TODO: Use msgpack for settings, lists... on sd card
|
||||
//TODO: Frequency manager
|
||||
//TODO: Replay
|
||||
|
||||
//Multimon-style stuff:
|
||||
//TODO: AFSK receiver
|
||||
//TODO: CTCSS detector
|
||||
//TODO: DMR detector
|
||||
|
||||
//TODO: Closecall wide range fix
|
||||
//TODO: SD card wiper
|
||||
//TODO: GSM channel detector
|
||||
//TODO: SIGFOX RX/TX
|
||||
//TODO: Bodet :)
|
||||
//TODO: Whistler
|
||||
|
||||
//TODO: LCR full message former (see norm)
|
||||
//TODO: AFSK NRZI
|
||||
@ -62,16 +71,15 @@
|
||||
|
||||
//TODO: Playdead amnesia and login
|
||||
//TODO: Setup: Play dead by default ? Enable/disable ?
|
||||
//TODO: Hide statusview when playing dead
|
||||
//TODO: Persistent playdead !
|
||||
|
||||
//In case of disaster:
|
||||
//BUG (fixed ?): No audio in about when shown second time
|
||||
|
||||
//TODO: Show MD5 mismatches for modules not found, etc...
|
||||
//TODO: Module name/filename in modules.hpp to indicate requirement in case it's not found ui_loadmodule
|
||||
//BUG: Description doesn't show up first time going to system>module info (UI drawn on top)
|
||||
|
||||
//TODO: Draw on touchscreen and transmit as spectrum paint
|
||||
//TODO: Two players tic-tac-toe
|
||||
//TODO: Analog TV pong game
|
||||
|
||||
#include "ch.h"
|
||||
|
||||
|
@ -82,9 +82,17 @@ void make_4A_group(uint32_t blocks[], const uint16_t PI_code, const bool TP, con
|
||||
uint32_t L = 0;
|
||||
uint32_t day_code;
|
||||
|
||||
<<<<<<< HEAD
|
||||
// 57723
|
||||
|
||||
if ((month == 1) || (month == 2)) L = 1;
|
||||
|
||||
day_code = 14956 + day + (uint32_t)((float)(year - 1900 - L) * 365.25) + uint16_t((float)((month + 1) + L * 12) * 30.6001);
|
||||
=======
|
||||
if ((month == 1) || (month == 2)) L = 1;
|
||||
|
||||
day_code = 14956 + day + (uint32_t)((float)(year - L) * 365.25) + uint16_t(((month + 1) * L * 12) * 30.6001);
|
||||
>>>>>>> d402a87... RDS radiotext and time group generators
|
||||
|
||||
blocks[0] = PI_code;
|
||||
blocks[1] = (0x4 << 12) | (0 << 11) | (b2b(TP) << 10) | ((PTY & 0x1F) << 5) | ((day_code & 0x18000) >> 15);
|
||||
@ -98,7 +106,11 @@ uint16_t gen_PSN(const char * psname, const RDS_flags * rds_flags) {
|
||||
|
||||
// 4 groups with 2 PSN characters in each
|
||||
for (c = 0; c < 4; c++)
|
||||
<<<<<<< HEAD
|
||||
make_0B_group(&group[c][0], rds_flags->PI_code, rds_flags->TP, rds_flags->PTY, rds_flags->TA, rds_flags->MS, rds_flags->DI, c, &psname[c * 2]);
|
||||
=======
|
||||
make_0B_group(&group[c][0], rds_flags->PI_code, rds_flags->TP, rds_flags->PTY, rds_flags->TA, rds_flags->MS, rds_flags->DI, c, &psname[c * 2]);
|
||||
>>>>>>> d402a87... RDS radiotext and time group generators
|
||||
|
||||
// Generate checkbits for each block of each group
|
||||
for (c = 0; c < 4; c++) {
|
||||
@ -108,7 +120,7 @@ uint16_t gen_PSN(const char * psname, const RDS_flags * rds_flags) {
|
||||
group[c][3] = makeblock(group[c][3], RDS_OFFSET_D);
|
||||
}
|
||||
|
||||
uint32_t * tx_data_u32 = (uint32_t*)shared_memory.tx_data;
|
||||
uint32_t * tx_data_u32 = (uint32_t*)shared_memory.bb_data.data;
|
||||
|
||||
// Copy to tx_data for baseband
|
||||
for (c = 0; c < 4 * 4; c++)
|
||||
@ -150,7 +162,7 @@ uint16_t gen_RadioText(const char * text, const bool AB, const RDS_flags * rds_
|
||||
group[i + 3] = makeblock(group[i + 3], RDS_OFFSET_D);
|
||||
}
|
||||
|
||||
uint32_t * tx_data_u32 = (uint32_t*)shared_memory.tx_data;
|
||||
uint32_t * tx_data_u32 = (uint32_t*)shared_memory.bb_data.data;
|
||||
|
||||
// Copy to tx_data for baseband
|
||||
for (c = 0; c < (groups * 4); c++)
|
||||
@ -175,7 +187,11 @@ uint16_t gen_ClockTime(const RDS_flags * rds_flags,
|
||||
group[2] = makeblock(group[2], RDS_OFFSET_C);
|
||||
group[3] = makeblock(group[3], RDS_OFFSET_D);
|
||||
|
||||
<<<<<<< HEAD
|
||||
uint32_t * tx_data_u32 = (uint32_t*)shared_memory.bb_data.data;
|
||||
=======
|
||||
uint32_t * tx_data_u32 = (uint32_t*)shared_memory.tx_data;
|
||||
>>>>>>> d402a87... RDS radiotext and time group generators
|
||||
|
||||
// Copy to tx_data for baseband
|
||||
for (c = 0; c < 4; c++)
|
||||
|
@ -37,11 +37,19 @@ namespace rds {
|
||||
|
||||
struct RDS_flags {
|
||||
uint16_t PI_code;
|
||||
<<<<<<< HEAD
|
||||
uint8_t PTY;
|
||||
uint8_t DI;
|
||||
bool TP;
|
||||
bool TA;
|
||||
bool MS;
|
||||
=======
|
||||
bool TP;
|
||||
uint8_t PTY;
|
||||
bool TA;
|
||||
bool MS;
|
||||
bool DI;
|
||||
>>>>>>> d402a87... RDS radiotext and time group generators
|
||||
};
|
||||
|
||||
uint32_t makeblock(uint32_t blockdata, uint16_t offset);
|
||||
|
@ -24,6 +24,8 @@
|
||||
#include "baseband_api.hpp"
|
||||
|
||||
#include "portapack_persistent_memory.hpp"
|
||||
#include "hackrf_gpio.hpp"
|
||||
using namespace hackrf::one;
|
||||
using namespace portapack;
|
||||
|
||||
#include "radio.hpp"
|
||||
@ -159,6 +161,7 @@ void ReceiverModel::enable() {
|
||||
update_sampling_rate();
|
||||
update_modulation();
|
||||
update_headphone_volume();
|
||||
led_rx.on();
|
||||
}
|
||||
|
||||
void ReceiverModel::disable() {
|
||||
@ -168,6 +171,7 @@ void ReceiverModel::disable() {
|
||||
// TODO: Responsibility for enabling/disabling the radio is muddy.
|
||||
// Some happens in ReceiverModel, some inside radio namespace.
|
||||
radio::disable();
|
||||
led_rx.off();
|
||||
}
|
||||
|
||||
int32_t ReceiverModel::tuning_offset() {
|
||||
|
@ -1,5 +1,6 @@
|
||||
/*
|
||||
* Copyright (C) 2014 Jared Boone, ShareBrained Technology, Inc.
|
||||
* Copyright (C) 2016 Furrtek
|
||||
*
|
||||
* This file is part of PortaPack.
|
||||
*
|
||||
@ -24,8 +25,12 @@
|
||||
#include "baseband_api.hpp"
|
||||
|
||||
#include "portapack_persistent_memory.hpp"
|
||||
#include "hackrf_gpio.hpp"
|
||||
using namespace hackrf::one;
|
||||
using namespace portapack;
|
||||
|
||||
#include "time.hpp"
|
||||
#include "event_m0.hpp"
|
||||
#include "radio.hpp"
|
||||
#include "audio.hpp"
|
||||
|
||||
@ -83,6 +88,11 @@ uint32_t TransmitterModel::baseband_oversampling() const {
|
||||
return baseband_configuration.decimation_factor;
|
||||
}
|
||||
|
||||
void TransmitterModel::on_tick_second() {
|
||||
if (portapack::persistent_memory::stealth_mode())
|
||||
led_tx.toggle();
|
||||
}
|
||||
|
||||
void TransmitterModel::enable() {
|
||||
enabled_ = true;
|
||||
radio::set_direction(rf::Direction::Transmit);
|
||||
@ -92,6 +102,13 @@ void TransmitterModel::enable() {
|
||||
update_vga();
|
||||
update_baseband_bandwidth();
|
||||
update_baseband_configuration();
|
||||
|
||||
led_tx.on();
|
||||
signal_token_tick_second = time::signal_tick_second += [this]() {
|
||||
this->on_tick_second();
|
||||
};
|
||||
if (portapack::persistent_memory::stealth_mode())
|
||||
EventDispatcher::set_display_sleep(true);
|
||||
}
|
||||
|
||||
void TransmitterModel::disable() {
|
||||
@ -100,6 +117,9 @@ void TransmitterModel::disable() {
|
||||
// TODO: Responsibility for enabling/disabling the radio is muddy.
|
||||
// Some happens in ReceiverModel, some inside radio namespace.
|
||||
radio::disable();
|
||||
|
||||
time::signal_tick_second -= signal_token_tick_second;
|
||||
led_tx.off();
|
||||
}
|
||||
|
||||
void TransmitterModel::update_tuning_frequency() {
|
||||
|
@ -1,5 +1,6 @@
|
||||
/*
|
||||
* Copyright (C) 2015 Jared Boone, ShareBrained Technology, Inc.
|
||||
* Copyright (C) 2016 Furrtek
|
||||
*
|
||||
* This file is part of PortaPack.
|
||||
*
|
||||
@ -30,6 +31,7 @@
|
||||
#include "rf_path.hpp"
|
||||
#include "max2837.hpp"
|
||||
#include "volume.hpp"
|
||||
#include "signal.hpp"
|
||||
|
||||
class TransmitterModel {
|
||||
public:
|
||||
@ -70,6 +72,7 @@ private:
|
||||
.sampling_rate = 3072000,
|
||||
.decimation_factor = 1,
|
||||
};
|
||||
SignalToken signal_token_tick_second;
|
||||
|
||||
void update_tuning_frequency();
|
||||
void update_rf_amp();
|
||||
@ -78,6 +81,7 @@ private:
|
||||
void update_vga();
|
||||
void update_modulation();
|
||||
void update_baseband_configuration();
|
||||
void on_tick_second();
|
||||
};
|
||||
|
||||
#endif/*__TRANSMITTER_MODEL_H__*/
|
||||
|
@ -20,19 +20,11 @@
|
||||
* Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
#include <ch.h>
|
||||
|
||||
#include "demofont.hpp"
|
||||
#include "ymdata.hpp"
|
||||
|
||||
#include "cpld_update.hpp"
|
||||
#include "portapack.hpp"
|
||||
#include "audio.hpp"
|
||||
#include "event_m0.hpp"
|
||||
|
||||
#include "ui_about.hpp"
|
||||
#include "touch.hpp"
|
||||
#include "sine_table.hpp"
|
||||
|
||||
#include "portapack_shared_memory.hpp"
|
||||
#include "portapack_persistent_memory.hpp"
|
||||
@ -45,371 +37,130 @@ using namespace lpc43xx;
|
||||
using namespace portapack;
|
||||
|
||||
namespace ui {
|
||||
|
||||
void AboutView::on_show() {
|
||||
transmitter_model.set_tuning_frequency(1337000000); // TODO: Change
|
||||
transmitter_model.set_baseband_configuration({
|
||||
.mode = 0,
|
||||
.sampling_rate = 1536000,
|
||||
.decimation_factor = 1,
|
||||
});
|
||||
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();
|
||||
|
||||
baseband::set_audiotx_data(32, 50, false, 0);
|
||||
|
||||
//audio::headphone::set_volume(volume_t::decibel(0 - 99) + audio::headphone::volume_range().max);
|
||||
}
|
||||
|
||||
void AboutView::render_video() {
|
||||
uint8_t p, r, luma, chroma, cy;
|
||||
ui::Color cc;
|
||||
char ch;
|
||||
float s;
|
||||
|
||||
// Send framebuffer to LCD. Gotta go fast !
|
||||
display.render_box({30, 112}, {180, 72}, framebuffer);
|
||||
|
||||
// Clear framebuffer to black
|
||||
memset(framebuffer, 0, 180 * 72 * sizeof(ui::Color));
|
||||
|
||||
// Drum hit palette animation
|
||||
if (drum > 1) drum--;
|
||||
|
||||
// Render copper bars from Y buffer
|
||||
for (p = 0; p < 72; p++) {
|
||||
luma = copperbuffer[p] & 0x0F; // 0 is transparent
|
||||
if (luma) {
|
||||
chroma = copperbuffer[p]>>4;
|
||||
cc = ui::Color(std::min((coppercolor[chroma][0]/luma)*drum,255), std::min((coppercolor[chroma][1]/luma)*drum,255), std::min((coppercolor[chroma][2]/luma)*drum,255));
|
||||
for (r = 0; r < 180; r++)
|
||||
framebuffer[(p*180)+r] = cc;
|
||||
}
|
||||
}
|
||||
|
||||
// Scroll in/out state machine
|
||||
if (anim_state == 0) {
|
||||
// Scroll in
|
||||
if (ofy < 8) {
|
||||
ofy++;
|
||||
anim_state = 0;
|
||||
} else {
|
||||
anim_state = 1;
|
||||
}
|
||||
if (ofx < (int16_t)(180 - (strlen(credits[credits_index].name) * 16) - 8)) {
|
||||
ofx += 8;
|
||||
anim_state = 0;
|
||||
}
|
||||
} else if (anim_state == 1) {
|
||||
// Just wait
|
||||
if (credits_timer == (30*3)) {
|
||||
credits_timer = 0;
|
||||
anim_state = 2;
|
||||
} else {
|
||||
credits_timer++;
|
||||
}
|
||||
} else {
|
||||
// Scroll out
|
||||
if (credits[credits_index].change == true) {
|
||||
if (ofy > -24) {
|
||||
ofy--;
|
||||
anim_state = 2;
|
||||
} else {
|
||||
anim_state = 0;
|
||||
}
|
||||
} else {
|
||||
anim_state = 0;
|
||||
}
|
||||
if (ofx < 180) {
|
||||
ofx += 8;
|
||||
anim_state = 2;
|
||||
}
|
||||
|
||||
// Switch to next text
|
||||
if (anim_state == 0) {
|
||||
if (credits_index == 9)
|
||||
credits_index = 0;
|
||||
else
|
||||
credits_index++;
|
||||
ofx = -(strlen(credits[credits_index].name) * 16) - 16;
|
||||
}
|
||||
}
|
||||
|
||||
// Sine text ("role")
|
||||
p = 0;
|
||||
while ((ch = credits[credits_index].role[p])) {
|
||||
draw_demoglyph({(ui::Coord)(8+(p*16)), (ui::Coord)(ofy+(sine_table_f32[((p*16)+(phase>>5))&0xFF] * 8))}, ch, paletteA);
|
||||
p++;
|
||||
}
|
||||
|
||||
// Scroll text (name)
|
||||
p = 0;
|
||||
while ((ch = credits[credits_index].name[p])) {
|
||||
draw_demoglyph({(ui::Coord)(ofx+(p*16)), 56}, ch, paletteB);
|
||||
p++;
|
||||
}
|
||||
|
||||
// Clear bars Y buffer
|
||||
memset(copperbuffer, 0, 72);
|
||||
|
||||
// Render bars to Y buffer
|
||||
for (p = 0; p < 5; p++) {
|
||||
cy = copperbars[p];
|
||||
for (r = 0; r < 16; r++)
|
||||
copperbuffer[cy+r] = copperluma[r] + (p<<4);
|
||||
}
|
||||
|
||||
// Animate bars positions
|
||||
for (p = 0; p < 5; p++) {
|
||||
s = sine_table_f32[((p*32)+(phase/24))&0xFF];
|
||||
s += sine_table_f32[((p*16)+(phase/35))&0xFF];
|
||||
copperbars[p] = 28+(uint8_t)(s * 14);
|
||||
}
|
||||
|
||||
phase += 128;
|
||||
}
|
||||
|
||||
void AboutView::draw_demoglyph(ui::Point p, char ch, ui::Color * pal) {
|
||||
uint8_t x, y, c, cl, cr;
|
||||
uint16_t che;
|
||||
int16_t lbx, il;
|
||||
|
||||
// Map ASCII to font bitmap
|
||||
if ((ch >= 32) || (ch < 96))
|
||||
che = char_map[ch-32];
|
||||
else
|
||||
che = 0xFF;
|
||||
|
||||
if (che < 0xFF) {
|
||||
che = (che * 128) + 48; // Start in bitmap
|
||||
|
||||
il = (180 * p.y) + p.x; // Start il framebuffer
|
||||
|
||||
for (y = 0; y < 16; y++) {
|
||||
if (p.y + y >= 72) break; // Over bottom of framebuffer, abort
|
||||
if (p.y + y >= 0) {
|
||||
for (x = 0; x < 8; x++) {
|
||||
c = demofont_bin[x+(y*8)+che]; // Split byte in 2 4BPP pixels
|
||||
cl = c >> 4;
|
||||
cr = c & 0x0F;
|
||||
lbx = p.x + (x*2);
|
||||
if (cl && (lbx < 180) && (lbx >= 0)) framebuffer[il] = pal[cl];
|
||||
lbx++;
|
||||
il++;
|
||||
if (cr && (lbx < 180) && (lbx >= 0)) framebuffer[il] = pal[cr];
|
||||
il++;
|
||||
}
|
||||
il += 180-16;
|
||||
} else {
|
||||
il += 180;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void AboutView::render_audio() {
|
||||
uint8_t i, ymdata;
|
||||
uint16_t ym_render_cnt;
|
||||
|
||||
// This is heavily inspired by MAME's ay8910.cpp and the YM2149's datasheet
|
||||
|
||||
// Render 1024 music samples
|
||||
for (ym_render_cnt = 0; ym_render_cnt < 1024; ym_render_cnt++) {
|
||||
|
||||
// Update registers at 48000/960 = 50Hz
|
||||
if (ym_sample_cnt == 0) {
|
||||
// "Decompress" on the fly and update YM registers
|
||||
for (i = 0; i < 14; i++) {
|
||||
if (!ym_regs[i].cnt) {
|
||||
// New run
|
||||
ymdata = ymdata_bin[ym_regs[i].ptr++];
|
||||
ym_regs[i].cnt = ymdata & 0x7F;
|
||||
if (ymdata & 0x80) {
|
||||
ym_regs[i].same = true;
|
||||
ym_regs[i].value = ymdata_bin[ym_regs[i].ptr++];
|
||||
} else {
|
||||
ym_regs[i].same = false;
|
||||
}
|
||||
// Detect drum on channel B
|
||||
if (i == 3)
|
||||
if (ym_regs[3].value > 2) drum = 4;
|
||||
}
|
||||
if (ym_regs[i].same == false) {
|
||||
ym_regs[i].value = ymdata_bin[ym_regs[i].ptr++];
|
||||
if (i == 13) {
|
||||
// Update envelope attributes
|
||||
ym_env_att = (ym_regs[13].value & 4) ? 0x1F : 0x00;
|
||||
if (!(ym_regs[13].value & 8)) {
|
||||
ym_env_hold = 1;
|
||||
ym_env_alt = ym_env_att;
|
||||
} else {
|
||||
ym_env_hold = ym_regs[13].value & 1;
|
||||
ym_env_alt = ym_regs[13].value & 2;
|
||||
}
|
||||
// Reset envelope counter
|
||||
ym_env_step = 0x1F;
|
||||
ym_env_holding = 0;
|
||||
ym_env_vol = (ym_env_step ^ ym_env_att);
|
||||
}
|
||||
}
|
||||
ym_regs[i].cnt--;
|
||||
}
|
||||
ym_frame++;
|
||||
}
|
||||
|
||||
// Square wave oscillators
|
||||
// 2457600/16/48000 = 3.2, but 4 sounds better than 3...
|
||||
for (i = 0; i < 3; i++) {
|
||||
ym_osc_cnt[i] += 4;
|
||||
if (ym_osc_cnt[i] >= (ym_regs[i*2].value | ((ym_regs[(i*2)+1].value & 0x0f) << 8))) {
|
||||
ym_osc_cnt[i] = 0;
|
||||
ym_osc_out[i] ^= 1;
|
||||
}
|
||||
}
|
||||
|
||||
// Noise generator
|
||||
ym_noise_cnt += 4;
|
||||
if (ym_noise_cnt >= ((ym_regs[6].value & 0x1F) * 2)) {
|
||||
ym_noise_cnt = 0;
|
||||
ym_rng ^= (((ym_rng & 1) ^ ((ym_rng >> 3) & 1)) << 17);
|
||||
ym_rng >>= 1;
|
||||
}
|
||||
|
||||
// Mix tones and noise
|
||||
for (i = 0; i < 3; i++)
|
||||
ym_ch[i] = (ym_osc_out[i] | ((ym_regs[7].value >> i) & 1)) & ((ym_rng & 1) | ((ym_regs[7].value >> (i + 3)) & 1));
|
||||
|
||||
// Envelope generator
|
||||
if (!ym_env_holding) {
|
||||
ym_env_cnt += 8;
|
||||
if (ym_env_cnt >= (ym_regs[11].value | (ym_regs[12].value<<8))) {
|
||||
ym_env_cnt = 0;
|
||||
ym_env_step--;
|
||||
if (ym_env_step < 0) {
|
||||
if (ym_env_hold) {
|
||||
if (ym_env_alt)
|
||||
ym_env_att ^= 0x1F;
|
||||
ym_env_holding = 1;
|
||||
ym_env_step = 0;
|
||||
} else {
|
||||
if (ym_env_alt && (ym_env_step & 0x20))
|
||||
ym_env_att ^= 0x1F;
|
||||
ym_env_step &= 0x1F;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
ym_env_vol = (ym_env_step ^ ym_env_att);
|
||||
|
||||
ym_out = 0;
|
||||
for (i = 0; i < 3; i++) {
|
||||
if (ym_regs[i + 8].value & 0x10) {
|
||||
// Envelope mode
|
||||
ym_out += (ym_ch[i] ? ym_env_vol : 0);
|
||||
} else {
|
||||
// Fixed mode
|
||||
ym_out += (ym_ch[i] ? (ym_regs[i + 8].value & 0x0F) : 0);
|
||||
}
|
||||
}
|
||||
|
||||
ym_buffer[ym_render_cnt] = (ym_out * 2) - 45;
|
||||
|
||||
if (ym_sample_cnt < 960) {
|
||||
ym_sample_cnt++;
|
||||
} else {
|
||||
ym_sample_cnt = 0;
|
||||
}
|
||||
|
||||
// Loop
|
||||
if (ym_frame == ym_frames) ym_init();
|
||||
}
|
||||
}
|
||||
|
||||
void AboutView::update() {
|
||||
if (framebuffer) {
|
||||
// Update 1 out of 2 frames, 60Hz is very laggy
|
||||
if (refresh_cnt & 1) render_video();
|
||||
refresh_cnt++;
|
||||
}
|
||||
size_t c;
|
||||
int32_t n;
|
||||
std::string text;
|
||||
Coord y_val, x_pos = 0;
|
||||
uint32_t flag;
|
||||
|
||||
// Slowly increase volume to avoid jumpscare
|
||||
if (headphone_vol < (70 << 2)) {
|
||||
audio::headphone::set_volume(volume_t::decibel((headphone_vol/4) - 99) + audio::headphone::volume_range().max);
|
||||
headphone_vol++;
|
||||
}
|
||||
}
|
||||
if (scroll & 1) {
|
||||
if (!((scroll >> 1) & 15)) {
|
||||
if (line_feed) {
|
||||
line_feed = false;
|
||||
} else {
|
||||
// Find a free text widget
|
||||
for (c = 0; c < 10; c++)
|
||||
if (text_line[c].screen_pos().y >= 200) break;
|
||||
|
||||
if (c < 10) {
|
||||
flag = credits[credits_index].flag & 0x3F;
|
||||
line_feed = (credits[credits_index].flag & 0x40) ? true : false;
|
||||
|
||||
if (flag == SECTION) {
|
||||
if (!second) {
|
||||
text = credits[credits_index].role;
|
||||
if (credits[credits_index].name != "")
|
||||
second = true;
|
||||
else {
|
||||
credits_index++;
|
||||
line_feed = true;
|
||||
}
|
||||
} else {
|
||||
text = credits[credits_index].name;
|
||||
second = false;
|
||||
line_feed = true;
|
||||
credits_index++;
|
||||
}
|
||||
x_pos = (240 - (text.size() * 8)) / 2;
|
||||
} else if (flag == TITLE) {
|
||||
text = credits[credits_index].role;
|
||||
x_pos = 120 - (text.size() * 8);
|
||||
if (credits[credits_index].name != "")
|
||||
text += " " + credits[credits_index].name;
|
||||
credits_index++;
|
||||
} else if (flag == MEMBER) {
|
||||
if (!second) {
|
||||
text = credits[credits_index].role;
|
||||
if (credits[credits_index].name != "")
|
||||
second = true;
|
||||
else
|
||||
credits_index++;
|
||||
} else {
|
||||
text = credits[credits_index].name;
|
||||
second = false;
|
||||
credits_index++;
|
||||
}
|
||||
x_pos = 136;
|
||||
}
|
||||
|
||||
if (!(flag & 0x80)) {
|
||||
text_line[c].set_parent_rect({{ x_pos, 200 - 16 }, { (Dim)text.size() * 8, 17 }});
|
||||
text_line[c].set(text);
|
||||
text_line[c].hidden(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void AboutView::ym_init() {
|
||||
uint8_t reg;
|
||||
|
||||
for (reg = 0; reg < 14; reg++) {
|
||||
ym_regs[reg].cnt = 0;
|
||||
// Pick up start pointers for each YM registers RLE blocks
|
||||
ym_regs[reg].ptr = ((uint16_t)(ymdata_bin[(reg*2)+3])<<8) + ymdata_bin[(reg*2)+2];
|
||||
ym_regs[reg].same = false; // Useless ?
|
||||
ym_regs[reg].value = 0; // Useless ?
|
||||
// Scroll text lines
|
||||
for (c = 0; c < 10; c++) {
|
||||
y_val = text_line[c].screen_pos().y - 16;
|
||||
if (y_val < 32) {
|
||||
text_line[c].set_parent_rect({{ text_line[c].screen_pos().x, 200 }, { text_line[c].size() }});
|
||||
text_line[c].hidden(true);
|
||||
} else {
|
||||
if (y_val < 200) {
|
||||
text_line[c].set_parent_rect({{ text_line[c].screen_pos().x, y_val - 1 }, { text_line[c].size() }});
|
||||
n = (y_val - 32) >> 2;
|
||||
if (n > 19)
|
||||
n = (38 - n);
|
||||
else
|
||||
n -= 3;
|
||||
if (n > 3) n = 3;
|
||||
if (n < 0) n = 0;
|
||||
text_line[c].set_style(&styles[n]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ym_frame = 0;
|
||||
scroll++;
|
||||
}
|
||||
|
||||
AboutView::AboutView(
|
||||
NavigationView& nav
|
||||
)
|
||||
{
|
||||
uint8_t p, c;
|
||||
|
||||
baseband::run_image(portapack::spi_flash::image_tag_audio_tx);
|
||||
//uint8_t p, c;
|
||||
|
||||
add_children({ {
|
||||
&text_title,
|
||||
&text_firmware,
|
||||
&text_cpld_hackrf,
|
||||
&text_cpld_hackrf_status,
|
||||
&button_ok,
|
||||
} });
|
||||
|
||||
for (auto& text : text_line) {
|
||||
text.set("");
|
||||
text.set_parent_rect({
|
||||
static_cast<Coord>(0),
|
||||
static_cast<Coord>(200),
|
||||
0, 0
|
||||
});
|
||||
add_child(&text);
|
||||
}
|
||||
|
||||
if( cpld_hackrf_verify_eeprom() ) {
|
||||
text_cpld_hackrf_status.set(" OK");
|
||||
} else {
|
||||
text_cpld_hackrf_status.set("BAD");
|
||||
}
|
||||
|
||||
// Politely ask for about 26kB
|
||||
framebuffer = (ui::Color *)chHeapAlloc(0x0, 180 * 72 * sizeof(ui::Color));
|
||||
|
||||
if (framebuffer) {
|
||||
memset(framebuffer, 0, 180 * 72 * sizeof(ui::Color));
|
||||
|
||||
// Copy original font palette
|
||||
c = 0;
|
||||
for (p = 0; p < 48; p+=3)
|
||||
paletteA[c++] = ui::Color(demofont_bin[p], demofont_bin[p+1], demofont_bin[p+2]);
|
||||
|
||||
// Increase red in another one
|
||||
c = 0;
|
||||
for (p = 0; p < 48; p+=3)
|
||||
paletteB[c++] = ui::Color(std::min(demofont_bin[p]+64, 255), demofont_bin[p+1], demofont_bin[p+2]);
|
||||
}
|
||||
|
||||
// Init YM synth
|
||||
ym_frames = ((uint16_t)(ymdata_bin[1])<<8) + ymdata_bin[0];
|
||||
ym_init();
|
||||
|
||||
button_ok.on_select = [this,&nav](Button&){
|
||||
if (framebuffer) chHeapFree(framebuffer); // Do NOT forget this
|
||||
nav.pop();
|
||||
};
|
||||
}
|
||||
|
||||
AboutView::~AboutView() {
|
||||
transmitter_model.disable();
|
||||
baseband::shutdown();
|
||||
}
|
||||
|
||||
void AboutView::focus() {
|
||||
button_ok.focus();
|
||||
}
|
||||
|
@ -26,8 +26,7 @@
|
||||
#include "ui_widget.hpp"
|
||||
#include "ui_menu.hpp"
|
||||
#include "ui_navigation.hpp"
|
||||
#include "transmitter_model.hpp"
|
||||
#include "baseband_api.hpp"
|
||||
#include "ui_font_fixed_8x16.hpp"
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
@ -36,103 +35,73 @@ namespace ui {
|
||||
class AboutView : public View {
|
||||
public:
|
||||
AboutView(NavigationView& nav);
|
||||
~AboutView();
|
||||
|
||||
void on_show() override;
|
||||
void focus() override;
|
||||
|
||||
std::string title() const override { return "About"; };
|
||||
|
||||
private:
|
||||
void ym_init();
|
||||
void update();
|
||||
void render_video();
|
||||
void render_audio();
|
||||
void draw_demoglyph(ui::Point p, char ch, ui::Color * pal);
|
||||
uint16_t debug_cnt = 0;
|
||||
|
||||
typedef struct ymreg_t {
|
||||
uint8_t value;
|
||||
uint8_t cnt;
|
||||
uint16_t ptr;
|
||||
bool same;
|
||||
} ymreg_t;
|
||||
|
||||
uint16_t headphone_vol = 5 << 2;
|
||||
|
||||
ymreg_t ym_regs[14];
|
||||
uint16_t ym_frames;
|
||||
uint16_t ym_frame;
|
||||
uint8_t drum = 0;
|
||||
uint16_t ym_osc_cnt[3];
|
||||
uint32_t ym_rng = 1;
|
||||
uint16_t ym_noise_cnt;
|
||||
uint8_t ym_env_att, ym_env_hold, ym_env_alt, ym_env_holding, ym_env_vol;
|
||||
int8_t ym_env_step;
|
||||
uint16_t ym_env_cnt;
|
||||
uint8_t ym_osc_out[3];
|
||||
uint8_t ym_ch[3];
|
||||
uint8_t ym_out;
|
||||
uint16_t ym_sample_cnt = 0;
|
||||
|
||||
int8_t ym_buffer[1024];
|
||||
|
||||
uint8_t refresh_cnt;
|
||||
ui::Color paletteA[16];
|
||||
ui::Color paletteB[16];
|
||||
ui::Color * framebuffer;
|
||||
uint32_t phase = 0;
|
||||
uint8_t copperbars[5] = { 0 };
|
||||
uint8_t copperbuffer[72] = { 0 };
|
||||
|
||||
uint8_t anim_state = 0;
|
||||
uint8_t credits_index = 0;
|
||||
uint16_t credits_timer = 0;
|
||||
uint32_t scroll = 0;
|
||||
bool second = false;
|
||||
bool line_feed = false;
|
||||
|
||||
int16_t ofx = -180, ofy = -24;
|
||||
|
||||
const uint8_t char_map[64] = { 0xFF, 27, 46, 0xFF, 0xFF, 0xFF, 28, 45,
|
||||
58, 59, 0xFF, 43, 40, 57, 26, 42,
|
||||
29, 30, 31, 32, 33, 34, 35, 36,
|
||||
37, 38, 41, 0xFF, 0xFF, 0xFF, 0xFF, 44,
|
||||
0xFF, 0, 1, 2, 3, 4, 5, 6,
|
||||
7, 8, 9, 10, 11, 12, 13, 14,
|
||||
15, 16, 17, 18, 19, 20, 21, 22,
|
||||
23, 24, 25, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF };
|
||||
|
||||
const uint8_t copperluma[16] = { 8,7,6,5,4,3,2,1,1,2,3,4,5,6,7,8 };
|
||||
const uint8_t coppercolor[5][3] = { { 255,0,0 },
|
||||
{ 0,255,0 },
|
||||
{ 0,0,255 },
|
||||
{ 255,0,255 },
|
||||
{ 255,255,0 } };
|
||||
Style styles[4] = { style_black, style_dark_grey, style_light_grey, style_white };
|
||||
|
||||
static constexpr Style style_white {
|
||||
.font = font::fixed_8x16,
|
||||
.background = Color::black(),
|
||||
.foreground = Color(255, 255, 255)
|
||||
};
|
||||
static constexpr Style style_light_grey {
|
||||
.font = font::fixed_8x16,
|
||||
.background = Color::black(),
|
||||
.foreground = Color(170, 170, 170)
|
||||
};
|
||||
static constexpr Style style_dark_grey {
|
||||
.font = font::fixed_8x16,
|
||||
.background = Color::black(),
|
||||
.foreground = Color(85, 85, 85)
|
||||
};
|
||||
static constexpr Style style_black {
|
||||
.font = font::fixed_8x16,
|
||||
.background = Color::black(),
|
||||
.foreground = Color(0, 0, 0)
|
||||
};
|
||||
|
||||
enum flags {
|
||||
SECTION = 0,
|
||||
MEMBER = 1,
|
||||
MEMBER_LF = 0x41,
|
||||
TITLE = 2,
|
||||
TITLE_LF = 0x42,
|
||||
END = 0x80
|
||||
};
|
||||
|
||||
typedef struct credits_t {
|
||||
char role[12];
|
||||
char name[12];
|
||||
bool change;
|
||||
std::string role;
|
||||
std::string name;
|
||||
flags flag;
|
||||
} credits_t;
|
||||
|
||||
// 0123456789A 0123456789A
|
||||
const credits_t credits[10] = { {"GURUS", "J. BOONE", false},
|
||||
{"GURUS", "M. OSSMANN", true},
|
||||
{"BUGS", "FURRTEK", true},
|
||||
{"RDS WAVE", "C. JACQUET", true},
|
||||
{"POCSAG RX", "T. SAILER", false},
|
||||
{"POCSAG RX", "E. OENAL", true},
|
||||
{"XYLOS DATA", "CLX", true},
|
||||
{"GREETS TO", "SIGMOUNTE", false},
|
||||
{"GREETS TO", "WINDYOONA", true},
|
||||
{"THIS MUSIC", "BIG ALEC", true}
|
||||
const credits_t credits[13] = { {"Portapack|HAVOC", "Git hash " GIT_REVISION, SECTION},
|
||||
{"Gurus", "J. Boone", TITLE},
|
||||
{"M. Ossmann", "", MEMBER_LF},
|
||||
{"Immaturity", "Furrtek", TITLE_LF},
|
||||
{"POCSAG rx", "T. Sailer", TITLE},
|
||||
{"E. Oenal", "", MEMBER_LF},
|
||||
{"RDS waveform", "C. Jacquet", TITLE_LF},
|
||||
{"Xy. infos", "cLx", TITLE_LF},
|
||||
{"Thanks", "", SECTION},
|
||||
{"Rainer Matla", "Keld Norman", TITLE},
|
||||
{"Sigmounte", "Waax", TITLE},
|
||||
{"Windyoona", "Channels", TITLE_LF},
|
||||
{"", "MMXVI", END}
|
||||
};
|
||||
|
||||
Text text_title {
|
||||
{ 100, 32, 40, 16 },
|
||||
"About",
|
||||
};
|
||||
|
||||
Text text_firmware {
|
||||
{ 0, 236, 240, 16 },
|
||||
"Git Commit Hash " GIT_REVISION,
|
||||
};
|
||||
std::array<Text, 10> text_line;
|
||||
|
||||
Text text_cpld_hackrf {
|
||||
{ 0, 252, 11*8, 16 },
|
||||
@ -156,17 +125,6 @@ private:
|
||||
}
|
||||
};
|
||||
|
||||
MessageHandlerRegistration message_handler_fifo_signal {
|
||||
Message::ID::FIFOSignal,
|
||||
[this](const Message* const p) {
|
||||
const auto message = static_cast<const FIFOSignalMessage*>(p);
|
||||
if (message->signaltype == 1) {
|
||||
this->render_audio();
|
||||
baseband::set_fifo_data(ym_buffer);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
};
|
||||
|
||||
} /* namespace ui */
|
||||
|
418
firmware/application/ui_about_demo.cpp
Normal file
418
firmware/application/ui_about_demo.cpp
Normal file
@ -0,0 +1,418 @@
|
||||
/*
|
||||
* 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 <ch.h>
|
||||
|
||||
#include "demofont.hpp"
|
||||
#include "ymdata.hpp"
|
||||
|
||||
#include "cpld_update.hpp"
|
||||
#include "portapack.hpp"
|
||||
#include "audio.hpp"
|
||||
#include "event_m0.hpp"
|
||||
|
||||
#include "ui_about.hpp"
|
||||
#include "touch.hpp"
|
||||
#include "sine_table.hpp"
|
||||
|
||||
#include "portapack_shared_memory.hpp"
|
||||
#include "portapack_persistent_memory.hpp"
|
||||
#include "lpc43xx_cpp.hpp"
|
||||
|
||||
#include <math.h>
|
||||
#include <cstring>
|
||||
|
||||
using namespace lpc43xx;
|
||||
using namespace portapack;
|
||||
|
||||
namespace ui {
|
||||
|
||||
void AboutView::on_show() {
|
||||
transmitter_model.set_tuning_frequency(1337000000); // TODO: Change
|
||||
transmitter_model.set_baseband_configuration({
|
||||
.mode = 0,
|
||||
.sampling_rate = 1536000,
|
||||
.decimation_factor = 1,
|
||||
});
|
||||
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();
|
||||
|
||||
baseband::set_audiotx_data(32, 50, false, 0);
|
||||
|
||||
//audio::headphone::set_volume(volume_t::decibel(0 - 99) + audio::headphone::volume_range().max);
|
||||
}
|
||||
|
||||
void AboutView::render_video() {
|
||||
uint8_t p, r, luma, chroma, cy;
|
||||
ui::Color cc;
|
||||
char ch;
|
||||
float s;
|
||||
|
||||
// Send framebuffer to LCD. Gotta go fast !
|
||||
display.render_box({30, 112}, {180, 72}, framebuffer);
|
||||
|
||||
// Clear framebuffer to black
|
||||
memset(framebuffer, 0, 180 * 72 * sizeof(ui::Color));
|
||||
|
||||
// Drum hit palette animation
|
||||
if (drum > 1) drum--;
|
||||
|
||||
// Render copper bars from Y buffer
|
||||
for (p = 0; p < 72; p++) {
|
||||
luma = copperbuffer[p] & 0x0F; // 0 is transparent
|
||||
if (luma) {
|
||||
chroma = copperbuffer[p]>>4;
|
||||
cc = ui::Color(std::min((coppercolor[chroma][0]/luma)*drum,255), std::min((coppercolor[chroma][1]/luma)*drum,255), std::min((coppercolor[chroma][2]/luma)*drum,255));
|
||||
for (r = 0; r < 180; r++)
|
||||
framebuffer[(p*180)+r] = cc;
|
||||
}
|
||||
}
|
||||
|
||||
// Scroll in/out state machine
|
||||
if (anim_state == 0) {
|
||||
// Scroll in
|
||||
if (ofy < 8) {
|
||||
ofy++;
|
||||
anim_state = 0;
|
||||
} else {
|
||||
anim_state = 1;
|
||||
}
|
||||
if (ofx < (int16_t)(180 - (strlen(credits[credits_index].name) * 16) - 8)) {
|
||||
ofx += 8;
|
||||
anim_state = 0;
|
||||
}
|
||||
} else if (anim_state == 1) {
|
||||
// Just wait
|
||||
if (credits_timer == (30*3)) {
|
||||
credits_timer = 0;
|
||||
anim_state = 2;
|
||||
} else {
|
||||
credits_timer++;
|
||||
}
|
||||
} else {
|
||||
// Scroll out
|
||||
if (credits[credits_index].change == true) {
|
||||
if (ofy > -24) {
|
||||
ofy--;
|
||||
anim_state = 2;
|
||||
} else {
|
||||
anim_state = 0;
|
||||
}
|
||||
} else {
|
||||
anim_state = 0;
|
||||
}
|
||||
if (ofx < 180) {
|
||||
ofx += 8;
|
||||
anim_state = 2;
|
||||
}
|
||||
|
||||
// Switch to next text
|
||||
if (anim_state == 0) {
|
||||
if (credits_index == 9)
|
||||
credits_index = 0;
|
||||
else
|
||||
credits_index++;
|
||||
ofx = -(strlen(credits[credits_index].name) * 16) - 16;
|
||||
}
|
||||
}
|
||||
|
||||
// Sine text ("role")
|
||||
p = 0;
|
||||
while ((ch = credits[credits_index].role[p])) {
|
||||
draw_demoglyph({(ui::Coord)(8+(p*16)), (ui::Coord)(ofy+(sine_table_f32[((p*16)+(phase>>5))&0xFF] * 8))}, ch, paletteA);
|
||||
p++;
|
||||
}
|
||||
|
||||
// Scroll text (name)
|
||||
p = 0;
|
||||
while ((ch = credits[credits_index].name[p])) {
|
||||
draw_demoglyph({(ui::Coord)(ofx+(p*16)), 56}, ch, paletteB);
|
||||
p++;
|
||||
}
|
||||
|
||||
// Clear bars Y buffer
|
||||
memset(copperbuffer, 0, 72);
|
||||
|
||||
// Render bars to Y buffer
|
||||
for (p = 0; p < 5; p++) {
|
||||
cy = copperbars[p];
|
||||
for (r = 0; r < 16; r++)
|
||||
copperbuffer[cy+r] = copperluma[r] + (p<<4);
|
||||
}
|
||||
|
||||
// Animate bars positions
|
||||
for (p = 0; p < 5; p++) {
|
||||
s = sine_table_f32[((p*32)+(phase/24))&0xFF];
|
||||
s += sine_table_f32[((p*16)+(phase/35))&0xFF];
|
||||
copperbars[p] = 28+(uint8_t)(s * 14);
|
||||
}
|
||||
|
||||
phase += 128;
|
||||
}
|
||||
|
||||
void AboutView::draw_demoglyph(ui::Point p, char ch, ui::Color * pal) {
|
||||
uint8_t x, y, c, cl, cr;
|
||||
uint16_t che;
|
||||
int16_t lbx, il;
|
||||
|
||||
// Map ASCII to font bitmap
|
||||
if ((ch >= 32) || (ch < 96))
|
||||
che = char_map[ch-32];
|
||||
else
|
||||
che = 0xFF;
|
||||
|
||||
if (che < 0xFF) {
|
||||
che = (che * 128) + 48; // Start in bitmap
|
||||
|
||||
il = (180 * p.y) + p.x; // Start il framebuffer
|
||||
|
||||
for (y = 0; y < 16; y++) {
|
||||
if (p.y + y >= 72) break; // Over bottom of framebuffer, abort
|
||||
if (p.y + y >= 0) {
|
||||
for (x = 0; x < 8; x++) {
|
||||
c = demofont_bin[x+(y*8)+che]; // Split byte in 2 4BPP pixels
|
||||
cl = c >> 4;
|
||||
cr = c & 0x0F;
|
||||
lbx = p.x + (x*2);
|
||||
if (cl && (lbx < 180) && (lbx >= 0)) framebuffer[il] = pal[cl];
|
||||
lbx++;
|
||||
il++;
|
||||
if (cr && (lbx < 180) && (lbx >= 0)) framebuffer[il] = pal[cr];
|
||||
il++;
|
||||
}
|
||||
il += 180-16;
|
||||
} else {
|
||||
il += 180;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void AboutView::render_audio() {
|
||||
uint8_t i, ymdata;
|
||||
uint16_t ym_render_cnt;
|
||||
|
||||
// This is heavily inspired by MAME's ay8910.cpp and the YM2149's datasheet
|
||||
|
||||
// Render 1024 music samples
|
||||
for (ym_render_cnt = 0; ym_render_cnt < 1024; ym_render_cnt++) {
|
||||
|
||||
// Update registers at 48000/960 = 50Hz
|
||||
if (ym_sample_cnt == 0) {
|
||||
// "Decompress" on the fly and update YM registers
|
||||
for (i = 0; i < 14; i++) {
|
||||
if (!ym_regs[i].cnt) {
|
||||
// New run
|
||||
ymdata = ymdata_bin[ym_regs[i].ptr++];
|
||||
ym_regs[i].cnt = ymdata & 0x7F;
|
||||
if (ymdata & 0x80) {
|
||||
ym_regs[i].same = true;
|
||||
ym_regs[i].value = ymdata_bin[ym_regs[i].ptr++];
|
||||
} else {
|
||||
ym_regs[i].same = false;
|
||||
}
|
||||
// Detect drum on channel B
|
||||
if (i == 3)
|
||||
if (ym_regs[3].value > 2) drum = 4;
|
||||
}
|
||||
if (ym_regs[i].same == false) {
|
||||
ym_regs[i].value = ymdata_bin[ym_regs[i].ptr++];
|
||||
if (i == 13) {
|
||||
// Update envelope attributes
|
||||
ym_env_att = (ym_regs[13].value & 4) ? 0x1F : 0x00;
|
||||
if (!(ym_regs[13].value & 8)) {
|
||||
ym_env_hold = 1;
|
||||
ym_env_alt = ym_env_att;
|
||||
} else {
|
||||
ym_env_hold = ym_regs[13].value & 1;
|
||||
ym_env_alt = ym_regs[13].value & 2;
|
||||
}
|
||||
// Reset envelope counter
|
||||
ym_env_step = 0x1F;
|
||||
ym_env_holding = 0;
|
||||
ym_env_vol = (ym_env_step ^ ym_env_att);
|
||||
}
|
||||
}
|
||||
ym_regs[i].cnt--;
|
||||
}
|
||||
ym_frame++;
|
||||
}
|
||||
|
||||
// Square wave oscillators
|
||||
// 2457600/16/48000 = 3.2, but 4 sounds better than 3...
|
||||
for (i = 0; i < 3; i++) {
|
||||
ym_osc_cnt[i] += 4;
|
||||
if (ym_osc_cnt[i] >= (ym_regs[i*2].value | ((ym_regs[(i*2)+1].value & 0x0f) << 8))) {
|
||||
ym_osc_cnt[i] = 0;
|
||||
ym_osc_out[i] ^= 1;
|
||||
}
|
||||
}
|
||||
|
||||
// Noise generator
|
||||
ym_noise_cnt += 4;
|
||||
if (ym_noise_cnt >= ((ym_regs[6].value & 0x1F) * 2)) {
|
||||
ym_noise_cnt = 0;
|
||||
ym_rng ^= (((ym_rng & 1) ^ ((ym_rng >> 3) & 1)) << 17);
|
||||
ym_rng >>= 1;
|
||||
}
|
||||
|
||||
// Mix tones and noise
|
||||
for (i = 0; i < 3; i++)
|
||||
ym_ch[i] = (ym_osc_out[i] | ((ym_regs[7].value >> i) & 1)) & ((ym_rng & 1) | ((ym_regs[7].value >> (i + 3)) & 1));
|
||||
|
||||
// Envelope generator
|
||||
if (!ym_env_holding) {
|
||||
ym_env_cnt += 8;
|
||||
if (ym_env_cnt >= (ym_regs[11].value | (ym_regs[12].value<<8))) {
|
||||
ym_env_cnt = 0;
|
||||
ym_env_step--;
|
||||
if (ym_env_step < 0) {
|
||||
if (ym_env_hold) {
|
||||
if (ym_env_alt)
|
||||
ym_env_att ^= 0x1F;
|
||||
ym_env_holding = 1;
|
||||
ym_env_step = 0;
|
||||
} else {
|
||||
if (ym_env_alt && (ym_env_step & 0x20))
|
||||
ym_env_att ^= 0x1F;
|
||||
ym_env_step &= 0x1F;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
ym_env_vol = (ym_env_step ^ ym_env_att);
|
||||
|
||||
ym_out = 0;
|
||||
for (i = 0; i < 3; i++) {
|
||||
if (ym_regs[i + 8].value & 0x10) {
|
||||
// Envelope mode
|
||||
ym_out += (ym_ch[i] ? ym_env_vol : 0);
|
||||
} else {
|
||||
// Fixed mode
|
||||
ym_out += (ym_ch[i] ? (ym_regs[i + 8].value & 0x0F) : 0);
|
||||
}
|
||||
}
|
||||
|
||||
ym_buffer[ym_render_cnt] = (ym_out * 2) - 45;
|
||||
|
||||
if (ym_sample_cnt < 960) {
|
||||
ym_sample_cnt++;
|
||||
} else {
|
||||
ym_sample_cnt = 0;
|
||||
}
|
||||
|
||||
// Loop
|
||||
if (ym_frame == ym_frames) ym_init();
|
||||
}
|
||||
}
|
||||
|
||||
void AboutView::update() {
|
||||
if (framebuffer) {
|
||||
// Update 1 out of 2 frames, 60Hz is very laggy
|
||||
if (refresh_cnt & 1) render_video();
|
||||
refresh_cnt++;
|
||||
}
|
||||
|
||||
// Slowly increase volume to avoid jumpscare
|
||||
if (headphone_vol < (70 << 2)) {
|
||||
audio::headphone::set_volume(volume_t::decibel((headphone_vol/4) - 99) + audio::headphone::volume_range().max);
|
||||
headphone_vol++;
|
||||
}
|
||||
}
|
||||
|
||||
void AboutView::ym_init() {
|
||||
uint8_t reg;
|
||||
|
||||
for (reg = 0; reg < 14; reg++) {
|
||||
ym_regs[reg].cnt = 0;
|
||||
// Pick up start pointers for each YM registers RLE blocks
|
||||
ym_regs[reg].ptr = ((uint16_t)(ymdata_bin[(reg*2)+3])<<8) + ymdata_bin[(reg*2)+2];
|
||||
ym_regs[reg].same = false; // Useless ?
|
||||
ym_regs[reg].value = 0; // Useless ?
|
||||
}
|
||||
|
||||
ym_frame = 0;
|
||||
}
|
||||
|
||||
AboutView::AboutView(
|
||||
NavigationView& nav
|
||||
)
|
||||
{
|
||||
uint8_t p, c;
|
||||
|
||||
baseband::run_image(portapack::spi_flash::image_tag_audio_tx);
|
||||
|
||||
add_children({ {
|
||||
&text_title,
|
||||
&text_firmware,
|
||||
&text_cpld_hackrf,
|
||||
&text_cpld_hackrf_status,
|
||||
&button_ok,
|
||||
} });
|
||||
|
||||
if( cpld_hackrf_verify_eeprom() ) {
|
||||
text_cpld_hackrf_status.set(" OK");
|
||||
} else {
|
||||
text_cpld_hackrf_status.set("BAD");
|
||||
}
|
||||
|
||||
// Politely ask for about 26kB
|
||||
framebuffer = (ui::Color *)chHeapAlloc(0x0, 180 * 72 * sizeof(ui::Color));
|
||||
|
||||
if (framebuffer) {
|
||||
memset(framebuffer, 0, 180 * 72 * sizeof(ui::Color));
|
||||
|
||||
// Copy original font palette
|
||||
c = 0;
|
||||
for (p = 0; p < 48; p+=3)
|
||||
paletteA[c++] = ui::Color(demofont_bin[p], demofont_bin[p+1], demofont_bin[p+2]);
|
||||
|
||||
// Increase red in another one
|
||||
c = 0;
|
||||
for (p = 0; p < 48; p+=3)
|
||||
paletteB[c++] = ui::Color(std::min(demofont_bin[p]+64, 255), demofont_bin[p+1], demofont_bin[p+2]);
|
||||
}
|
||||
|
||||
// Init YM synth
|
||||
ym_frames = ((uint16_t)(ymdata_bin[1])<<8) + ymdata_bin[0];
|
||||
ym_init();
|
||||
|
||||
|
||||
button_ok.on_select = [this,&nav](Button&){
|
||||
if (framebuffer) chHeapFree(framebuffer); // Do NOT forget this
|
||||
nav.pop();
|
||||
};
|
||||
}
|
||||
|
||||
AboutView::~AboutView() {
|
||||
transmitter_model.disable();
|
||||
baseband::shutdown();
|
||||
}
|
||||
|
||||
void AboutView::focus() {
|
||||
button_ok.focus();
|
||||
}
|
||||
|
||||
} /* namespace ui */
|
174
firmware/application/ui_about_demo.hpp
Normal file
174
firmware/application/ui_about_demo.hpp
Normal file
@ -0,0 +1,174 @@
|
||||
/*
|
||||
* 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 __UI_ABOUT_H__
|
||||
#define __UI_ABOUT_H__
|
||||
|
||||
#include "ui_widget.hpp"
|
||||
#include "ui_menu.hpp"
|
||||
#include "ui_navigation.hpp"
|
||||
#include "transmitter_model.hpp"
|
||||
#include "baseband_api.hpp"
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
namespace ui {
|
||||
|
||||
class AboutView : public View {
|
||||
public:
|
||||
AboutView(NavigationView& nav);
|
||||
~AboutView();
|
||||
|
||||
void on_show() override;
|
||||
void focus() override;
|
||||
|
||||
private:
|
||||
void ym_init();
|
||||
void update();
|
||||
void render_video();
|
||||
void render_audio();
|
||||
void draw_demoglyph(ui::Point p, char ch, ui::Color * pal);
|
||||
uint16_t debug_cnt = 0;
|
||||
|
||||
typedef struct ymreg_t {
|
||||
uint8_t value;
|
||||
uint8_t cnt;
|
||||
uint16_t ptr;
|
||||
bool same;
|
||||
} ymreg_t;
|
||||
|
||||
uint16_t headphone_vol = 5 << 2;
|
||||
|
||||
ymreg_t ym_regs[14];
|
||||
uint16_t ym_frames;
|
||||
uint16_t ym_frame;
|
||||
uint8_t drum = 0;
|
||||
uint16_t ym_osc_cnt[3];
|
||||
uint32_t ym_rng = 1;
|
||||
uint16_t ym_noise_cnt;
|
||||
uint8_t ym_env_att, ym_env_hold, ym_env_alt, ym_env_holding, ym_env_vol;
|
||||
int8_t ym_env_step;
|
||||
uint16_t ym_env_cnt;
|
||||
uint8_t ym_osc_out[3];
|
||||
uint8_t ym_ch[3];
|
||||
uint8_t ym_out;
|
||||
uint16_t ym_sample_cnt = 0;
|
||||
|
||||
int8_t ym_buffer[1024];
|
||||
|
||||
uint8_t refresh_cnt;
|
||||
ui::Color paletteA[16];
|
||||
ui::Color paletteB[16];
|
||||
ui::Color * framebuffer;
|
||||
uint32_t phase = 0;
|
||||
uint8_t copperbars[5] = { 0 };
|
||||
uint8_t copperbuffer[72] = { 0 };
|
||||
|
||||
uint8_t anim_state = 0;
|
||||
uint8_t credits_index = 0;
|
||||
uint16_t credits_timer = 0;
|
||||
|
||||
int16_t ofx = -180, ofy = -24;
|
||||
|
||||
const uint8_t char_map[64] = { 0xFF, 27, 46, 0xFF, 0xFF, 0xFF, 28, 45,
|
||||
58, 59, 0xFF, 43, 40, 57, 26, 42,
|
||||
29, 30, 31, 32, 33, 34, 35, 36,
|
||||
37, 38, 41, 0xFF, 0xFF, 0xFF, 0xFF, 44,
|
||||
0xFF, 0, 1, 2, 3, 4, 5, 6,
|
||||
7, 8, 9, 10, 11, 12, 13, 14,
|
||||
15, 16, 17, 18, 19, 20, 21, 22,
|
||||
23, 24, 25, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF };
|
||||
|
||||
const uint8_t copperluma[16] = { 8,7,6,5,4,3,2,1,1,2,3,4,5,6,7,8 };
|
||||
const uint8_t coppercolor[5][3] = { { 255,0,0 },
|
||||
{ 0,255,0 },
|
||||
{ 0,0,255 },
|
||||
{ 255,0,255 },
|
||||
{ 255,255,0 } };
|
||||
|
||||
typedef struct credits_t {
|
||||
char role[12];
|
||||
char name[12];
|
||||
bool change;
|
||||
} credits_t;
|
||||
|
||||
// 0123456789A 0123456789A
|
||||
const credits_t credits[10] = { {"GURUS", "J. BOONE", false},
|
||||
{"GURUS", "M. OSSMANN", true},
|
||||
{"BUGS", "FURRTEK", true},
|
||||
{"RDS WAVE", "C. JACQUET", true},
|
||||
{"POCSAG RX", "T. SAILER", false},
|
||||
{"POCSAG RX", "E. OENAL", true},
|
||||
{"XYLOS DATA", "CLX", true},
|
||||
{"GREETS TO", "SIGMOUNTE", false},
|
||||
{"GREETS TO", "WINDYOONA", true},
|
||||
{"THIS MUSIC", "BIG ALEC", true}
|
||||
};
|
||||
|
||||
Text text_title {
|
||||
{ 100, 32, 40, 16 },
|
||||
"About",
|
||||
};
|
||||
|
||||
Text text_firmware {
|
||||
{ 0, 236, 240, 16 },
|
||||
"Git Commit Hash " GIT_REVISION,
|
||||
};
|
||||
|
||||
Text text_cpld_hackrf {
|
||||
{ 0, 252, 11*8, 16 },
|
||||
"HackRF CPLD",
|
||||
};
|
||||
|
||||
Text text_cpld_hackrf_status {
|
||||
{ 240 - 3*8, 252, 3*8, 16 },
|
||||
"???"
|
||||
};
|
||||
|
||||
Button button_ok {
|
||||
{ 72, 272, 96, 24 },
|
||||
"OK"
|
||||
};
|
||||
|
||||
MessageHandlerRegistration message_handler_update {
|
||||
Message::ID::DisplayFrameSync,
|
||||
[this](const Message* const) {
|
||||
this->update();
|
||||
}
|
||||
};
|
||||
|
||||
MessageHandlerRegistration message_handler_fifo_signal {
|
||||
Message::ID::FIFOSignal,
|
||||
[this](const Message* const p) {
|
||||
const auto message = static_cast<const FIFOSignalMessage*>(p);
|
||||
if (message->signaltype == 1) {
|
||||
this->render_audio();
|
||||
baseband::set_fifo_data(ym_buffer);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
};
|
||||
|
||||
} /* namespace ui */
|
||||
|
||||
#endif/*__UI_ABOUT_H__*/
|
@ -95,7 +95,7 @@ void ADSBTxView::start_tx() {
|
||||
transmitter_model.set_baseband_bandwidth(1750000);
|
||||
transmitter_model.enable();
|
||||
|
||||
memcpy(shared_memory.tx_data, adsb_bin, 112);
|
||||
memcpy(shared_memory.bb_data.data, adsb_bin, 112);
|
||||
baseband::set_adsb();
|
||||
}
|
||||
|
||||
|
@ -163,7 +163,7 @@ private:
|
||||
Message::ID::TXDone,
|
||||
[this](const Message* const p) {
|
||||
const auto message = *reinterpret_cast<const TXDoneMessage*>(p);
|
||||
this->on_txdone(message.n);
|
||||
this->on_txdone(message.progress);
|
||||
}
|
||||
};
|
||||
};
|
||||
|
@ -22,12 +22,7 @@
|
||||
|
||||
#include "ui_alphanum.hpp"
|
||||
|
||||
#include "ch.h"
|
||||
|
||||
#include "ff.h"
|
||||
#include "portapack.hpp"
|
||||
#include "radio.hpp"
|
||||
|
||||
#include "hackrf_hal.hpp"
|
||||
#include "portapack_shared_memory.hpp"
|
||||
|
||||
@ -45,9 +40,9 @@ void AlphanumView::paint(Painter& painter) {
|
||||
AlphanumView::AlphanumView(
|
||||
NavigationView& nav,
|
||||
char txt[],
|
||||
uint8_t max_len
|
||||
) {
|
||||
_max_len = max_len;
|
||||
size_t max_length
|
||||
) : _max_length(max_length)
|
||||
{
|
||||
_lowercase = false;
|
||||
size_t n;
|
||||
|
||||
@ -64,14 +59,19 @@ AlphanumView::AlphanumView(
|
||||
};
|
||||
|
||||
txtidx = strlen(txt);
|
||||
memcpy(txtinput, txt, max_len + 1);
|
||||
memcpy(txtinput, txt, _max_length + 1);
|
||||
n = txtidx;
|
||||
while (n && (txtinput[n - 1] == ' ')) {
|
||||
txtinput[--n] = 0;
|
||||
txtidx--;
|
||||
}
|
||||
|
||||
add_child(&text_input);
|
||||
add_children({ {
|
||||
&text_input,
|
||||
&button_lowercase,
|
||||
&raw_char,
|
||||
&button_ok
|
||||
} });
|
||||
|
||||
const auto button_fn = [this](Button& button) {
|
||||
this->on_button(button);
|
||||
@ -81,9 +81,9 @@ AlphanumView::AlphanumView(
|
||||
for (auto& button : buttons) {
|
||||
button.on_select = button_fn;
|
||||
button.set_parent_rect({
|
||||
static_cast<Coord>((n % 5) * button_w),
|
||||
static_cast<Coord>((n / 5) * button_h + 24),
|
||||
button_w, button_h
|
||||
static_cast<Coord>((n % 5) * (240 / 5)),
|
||||
static_cast<Coord>((n / 5) * 28 + 24),
|
||||
240 / 5, 28
|
||||
});
|
||||
if ((n < 10) || (n == 39))
|
||||
button.set_style(&style_num);
|
||||
@ -92,31 +92,28 @@ AlphanumView::AlphanumView(
|
||||
add_child(&button);
|
||||
n++;
|
||||
}
|
||||
set_uppercase();
|
||||
set_keys(keys_upper);
|
||||
|
||||
add_child(&button_lowercase);
|
||||
button_lowercase.on_select = [this, &nav, max_len](Button&) {
|
||||
button_lowercase.on_select = [this, &nav](Button&) {
|
||||
if (_lowercase == true) {
|
||||
_lowercase = false;
|
||||
button_lowercase.set_text("UC");
|
||||
set_uppercase();
|
||||
set_keys(keys_upper);
|
||||
} else {
|
||||
_lowercase = true;
|
||||
button_lowercase.set_text("LC");
|
||||
set_lowercase();
|
||||
set_keys(keys_lower);
|
||||
}
|
||||
};
|
||||
|
||||
add_child(&raw_char);
|
||||
raw_char.set_value(0x30);
|
||||
raw_char.on_select = [this, &nav, txt, max_len](NumberField&) {
|
||||
raw_char.set_value('0');
|
||||
raw_char.on_select = [this, &nav](NumberField&) {
|
||||
char_add(raw_char.value());
|
||||
update_text();
|
||||
};
|
||||
|
||||
add_child(&button_done);
|
||||
button_done.on_select = [this, &nav, txt, max_len](Button&) {
|
||||
memcpy(txt, txtinput, max_len + 1);
|
||||
button_ok.on_select = [this, &nav, txt, max_length](Button&) {
|
||||
memcpy(txt, txtinput, max_length + 1);
|
||||
if (on_changed) on_changed(this->value());
|
||||
nav.pop();
|
||||
};
|
||||
@ -140,29 +137,12 @@ void AlphanumView::move_cursor() {
|
||||
);
|
||||
}
|
||||
|
||||
void AlphanumView::set_uppercase() {
|
||||
const char* const key_caps = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ. !<";
|
||||
|
||||
void AlphanumView::set_keys(const char * const key_list) {
|
||||
size_t n = 0;
|
||||
for(auto& button : buttons) {
|
||||
//add_child(&button);
|
||||
|
||||
for (auto& button : buttons) {
|
||||
const std::string label {
|
||||
key_caps[n]
|
||||
};
|
||||
button.set_text(label);
|
||||
n++;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void AlphanumView::set_lowercase() {
|
||||
const char* const key_caps = "0123456789abcdefghijklmnopqrstuvwxyz:=?<";
|
||||
|
||||
size_t n = 0;
|
||||
for(auto& button : buttons) {
|
||||
//add_child(&button);
|
||||
const std::string label {
|
||||
key_caps[n]
|
||||
key_list[n]
|
||||
};
|
||||
button.set_text(label);
|
||||
n++;
|
||||
@ -170,7 +150,7 @@ void AlphanumView::set_lowercase() {
|
||||
}
|
||||
|
||||
void AlphanumView::focus() {
|
||||
button_done.focus();
|
||||
button_ok.focus();
|
||||
}
|
||||
|
||||
char * AlphanumView::value() {
|
||||
@ -180,30 +160,31 @@ char * AlphanumView::value() {
|
||||
|
||||
void AlphanumView::on_button(Button& button) {
|
||||
const auto s = button.text();
|
||||
if( s == "<" ) {
|
||||
|
||||
if (s == "<")
|
||||
char_delete();
|
||||
} else {
|
||||
else
|
||||
char_add(s[0]);
|
||||
}
|
||||
|
||||
update_text();
|
||||
}
|
||||
|
||||
void AlphanumView::char_add(const char c) {
|
||||
if (txtidx < _max_len) {
|
||||
txtinput[txtidx] = c;
|
||||
txtidx++;
|
||||
}
|
||||
if (txtidx >= _max_length) return;
|
||||
|
||||
txtinput[txtidx] = c;
|
||||
txtidx++;
|
||||
}
|
||||
|
||||
void AlphanumView::char_delete() {
|
||||
if (txtidx) {
|
||||
txtidx--;
|
||||
txtinput[txtidx] = 0;
|
||||
}
|
||||
if (!txtidx) return;
|
||||
|
||||
txtidx--;
|
||||
txtinput[txtidx] = 0;
|
||||
}
|
||||
|
||||
void AlphanumView::update_text() {
|
||||
text_input.set(std::string(txtinput) + std::string(_max_len - strlen(txtinput), ' '));
|
||||
text_input.set(std::string(txtinput) + std::string(_max_length - strlen(txtinput), ' '));
|
||||
move_cursor();
|
||||
}
|
||||
|
||||
|
@ -29,12 +29,6 @@
|
||||
#include "ui_menu.hpp"
|
||||
#include "ui_navigation.hpp"
|
||||
#include "ui_font_fixed_8x16.hpp"
|
||||
#include "clock_manager.hpp"
|
||||
#include "message.hpp"
|
||||
#include "rf_path.hpp"
|
||||
#include "max2837.hpp"
|
||||
#include "volume.hpp"
|
||||
#include "transmitter_model.hpp"
|
||||
|
||||
namespace ui {
|
||||
|
||||
@ -42,26 +36,30 @@ class AlphanumView : public View {
|
||||
public:
|
||||
std::function<void(char *)> on_changed;
|
||||
|
||||
AlphanumView(NavigationView& nav, char txt[], uint8_t max_len);
|
||||
AlphanumView(NavigationView& nav, char txt[], size_t max_length);
|
||||
|
||||
void paint(Painter& painter) override;
|
||||
void focus() override;
|
||||
|
||||
char * value();
|
||||
|
||||
std::string title() const override { return "Text entry"; };
|
||||
|
||||
private:
|
||||
uint8_t _max_len;
|
||||
const char * const keys_upper = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ. !<";
|
||||
const char * const keys_lower = "0123456789abcdefghijklmnopqrstuvwxyz:=?<";
|
||||
|
||||
size_t _max_length;
|
||||
uint8_t txtidx;
|
||||
bool _lowercase = false;
|
||||
static constexpr size_t button_w = 240 / 5;
|
||||
static constexpr size_t button_h = 28;
|
||||
char txtinput[29] = { 0 }; // 28 chars max
|
||||
|
||||
void char_add(const char c);
|
||||
void char_delete();
|
||||
void set_lowercase();
|
||||
void set_uppercase();
|
||||
void set_keys(const char * const key_list);
|
||||
void move_cursor();
|
||||
void on_button(Button& button);
|
||||
void update_text();
|
||||
|
||||
Text text_input {
|
||||
{ 8, 0, 224, 16 }
|
||||
@ -70,7 +68,7 @@ private:
|
||||
std::array<Button, 40> buttons;
|
||||
|
||||
Button button_lowercase {
|
||||
{ 88+64+16, 270, 32, 24 },
|
||||
{ 21 * 8, 270, 32, 24 },
|
||||
"UC"
|
||||
};
|
||||
|
||||
@ -82,14 +80,10 @@ private:
|
||||
'0'
|
||||
};
|
||||
|
||||
Button button_done {
|
||||
Button button_ok {
|
||||
{ 88, 270, 64, 24 },
|
||||
"Done"
|
||||
"OK"
|
||||
};
|
||||
|
||||
void on_button(Button& button);
|
||||
|
||||
void update_text();
|
||||
};
|
||||
|
||||
} /* namespace ui */
|
||||
|
@ -28,8 +28,6 @@
|
||||
|
||||
#include "event_m0.hpp"
|
||||
|
||||
#include "message.hpp"
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
namespace ui {
|
||||
|
409
firmware/application/ui_bht_tx.cpp
Normal file
409
firmware/application/ui_bht_tx.cpp
Normal file
@ -0,0 +1,409 @@
|
||||
/*
|
||||
* 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 "ui_bht_tx.hpp"
|
||||
|
||||
#include "portapack.hpp"
|
||||
#include "baseband_api.hpp"
|
||||
#include "portapack_persistent_memory.hpp"
|
||||
|
||||
#include <cstring>
|
||||
#include <stdio.h>
|
||||
|
||||
using namespace portapack;
|
||||
|
||||
namespace ui {
|
||||
|
||||
void BHTView::focus() {
|
||||
relay_states[0].focus();
|
||||
}
|
||||
|
||||
BHTView::~BHTView() {
|
||||
transmitter_model.disable();
|
||||
baseband::shutdown();
|
||||
}
|
||||
|
||||
void BHTView::generate_message() {
|
||||
size_t c;
|
||||
const encoder_def_t * um3750_def;
|
||||
uint8_t bit[12];
|
||||
uint8_t city_code;
|
||||
std::string ep_symbols;
|
||||
char ook_bitstream[256];
|
||||
char ep_message[13] = { 0 };
|
||||
|
||||
if (!_mode) {
|
||||
// Xy CCIR frame
|
||||
|
||||
// Header
|
||||
ccir_message[0] = (header_code_a.value() / 10) + '0';
|
||||
ccir_message[1] = (header_code_a.value() % 10) + '0';
|
||||
ccir_message[2] = (header_code_b.value() / 10) + '0';
|
||||
ccir_message[3] = (header_code_b.value() % 10) + '0';
|
||||
|
||||
// Addresses
|
||||
ccir_message[4] = (city_code_xy.value() / 10) + '0';
|
||||
ccir_message[5] = (city_code_xy.value() % 10) + '0';
|
||||
ccir_message[6] = family_code_xy.value() + '0';
|
||||
|
||||
if (!checkbox_wcsubfamily.value())
|
||||
ccir_message[7] = subfamily_code.value() + '0';
|
||||
else
|
||||
ccir_message[7] = 'A'; // Wildcard
|
||||
|
||||
if (!checkbox_wcid.value()) {
|
||||
ccir_message[8] = (receiver_code.value() / 10) + '0';
|
||||
ccir_message[9] = (receiver_code.value() % 10) + '0';
|
||||
} else {
|
||||
ccir_message[8] = 'A'; // Wildcard
|
||||
ccir_message[9] = 'A'; // Wildcard
|
||||
}
|
||||
|
||||
ccir_message[10] = 'B';
|
||||
|
||||
// Relay states
|
||||
for (c = 0; c < 4; c++)
|
||||
ccir_message[c + 11] = relay_states[c].selected_index() + '0';
|
||||
|
||||
ccir_message[15] = 'B';
|
||||
|
||||
// End
|
||||
for (c = 16; c < 20; c++)
|
||||
ccir_message[c] = '0';
|
||||
|
||||
ccir_message[20] = 0;
|
||||
|
||||
// Replace repeats with E code
|
||||
for (c = 1; c < 20; c++)
|
||||
if (ccir_message[c] == ccir_message[c - 1]) ccir_message[c] = 'E';
|
||||
|
||||
// Display as text
|
||||
text_message.set(ccir_message);
|
||||
|
||||
ascii_to_ccir(ccir_message);
|
||||
} else {
|
||||
// EP frame
|
||||
// Repeated 2x 26 times
|
||||
// Whole frame + space = 128ms, data only = 64ms
|
||||
|
||||
um3750_def = &encoder_defs[8];
|
||||
|
||||
city_code = city_code_ep.value();
|
||||
|
||||
for (c = 0; c < 8; c++)
|
||||
bit[c] = (city_code >> c) & 1;
|
||||
|
||||
bit[8] = family_code_ep.selected_index_value() >> 1;
|
||||
bit[9] = family_code_ep.selected_index_value() & 1;
|
||||
bit[10] = 0; // R1 first
|
||||
if (relay_states[0].selected_index())
|
||||
bit[11] = relay_states[0].selected_index() - 1;
|
||||
else
|
||||
bit[11] = 0;
|
||||
|
||||
for (c = 0; c < 12; c++)
|
||||
ep_message[c] = bit[c] + '0';
|
||||
|
||||
text_message.set(ep_message);
|
||||
|
||||
c = 0;
|
||||
for (auto ch : um3750_def->word_format) {
|
||||
if (ch == 'S')
|
||||
ep_symbols += um3750_def->sync;
|
||||
else
|
||||
ep_symbols += um3750_def->bit_format[bit[c++]];
|
||||
}
|
||||
|
||||
c = 0;
|
||||
for (auto ch : ep_symbols) {
|
||||
if (ch != '0')
|
||||
ook_bitstream[c >> 3] |= (1 << (7 - (c & 7)));
|
||||
c++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void BHTView::start_tx() {
|
||||
|
||||
if (speaker_enabled && !_mode)
|
||||
audio::headphone::set_volume(volume_t::decibel(90 - 99) + audio::headphone::volume_range().max);
|
||||
|
||||
transmitter_model.set_tuning_frequency(bht_freqs[options_freq.selected_index()]);
|
||||
transmitter_model.set_baseband_configuration({
|
||||
.mode = 0,
|
||||
.sampling_rate = 1536000U,
|
||||
.decimation_factor = 1,
|
||||
});
|
||||
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.tones_data.message, ccir_message, 20);
|
||||
|
||||
for (uint8_t c = 0; c < 16; c++) {
|
||||
shared_memory.bb_data.tones_data.tone_defs[c].delta = ccir_deltas[c];
|
||||
shared_memory.bb_data.tones_data.tone_defs[c].duration = CCIR_TONE_LENGTH;
|
||||
}
|
||||
|
||||
audio::set_rate(audio::Rate::Hz_24000);
|
||||
baseband::set_tones_data(10000, CCIR_SILENCE, 20, false, checkbox_speaker.value());
|
||||
}
|
||||
|
||||
// ASCII to frequency LUT index
|
||||
void BHTView::ascii_to_ccir(char * ascii) {
|
||||
for (size_t c = 0; c < 20; c++) {
|
||||
if (ascii[c] > '9')
|
||||
ascii[c] -= 0x37;
|
||||
else
|
||||
ascii[c] -= '0';
|
||||
}
|
||||
}
|
||||
|
||||
void BHTView::on_tx_progress(const int progress, const bool done) {
|
||||
size_t c;
|
||||
uint8_t sr;
|
||||
|
||||
if (tx_mode == SINGLE) {
|
||||
if (done) {
|
||||
audio::headphone::set_volume(volume_t::decibel(0 - 99) + audio::headphone::volume_range().max);
|
||||
|
||||
transmitter_model.disable();
|
||||
progressbar.set_value(0);
|
||||
|
||||
if (!checkbox_cligno.value()) {
|
||||
tx_mode = IDLE;
|
||||
button_transmit.set_style(&style_val);
|
||||
button_transmit.set_text("START");
|
||||
} else {
|
||||
chThdSleepMilliseconds(tempo_cligno.value() * 1000); // Dirty :(
|
||||
|
||||
// Invert all relay states
|
||||
for (c = 0; c < 4; c++) {
|
||||
sr = relay_states[c].selected_index();
|
||||
if (sr > 0) relay_states[c].set_selected_index(sr ^ 3);
|
||||
}
|
||||
|
||||
generate_message();
|
||||
start_tx();
|
||||
}
|
||||
} else {
|
||||
progressbar.set_value(progress);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
BHTView::BHTView(NavigationView& nav) {
|
||||
(void)nav;
|
||||
size_t n;
|
||||
|
||||
baseband::run_image(portapack::spi_flash::image_tag_tones);
|
||||
//baseband::run_image(portapack::spi_flash::image_tag_encoders);
|
||||
|
||||
add_children({ {
|
||||
&options_mode,
|
||||
&text_header,
|
||||
&header_code_a,
|
||||
&header_code_b,
|
||||
&checkbox_speaker,
|
||||
&bmp_speaker,
|
||||
&text_city,
|
||||
&city_code_xy,
|
||||
&text_family,
|
||||
&family_code_xy,
|
||||
&text_subfamily,
|
||||
&subfamily_code,
|
||||
&checkbox_wcsubfamily,
|
||||
&text_receiver,
|
||||
&receiver_code,
|
||||
&checkbox_wcid,
|
||||
&text_freq,
|
||||
&options_freq,
|
||||
&text_relais,
|
||||
&progressbar,
|
||||
&text_message,
|
||||
&button_transmit,
|
||||
&checkbox_cligno,
|
||||
&tempo_cligno,
|
||||
&text_cligno
|
||||
} });
|
||||
|
||||
options_mode.set_selected_index(0); // Xy
|
||||
header_code_a.set_value(0);
|
||||
header_code_b.set_value(0);
|
||||
city_code_xy.set_value(18);
|
||||
family_code_xy.set_value(1);
|
||||
subfamily_code.set_value(1);
|
||||
receiver_code.set_value(1);
|
||||
options_freq.set_selected_index(0);
|
||||
tempo_cligno.set_value(1);
|
||||
progressbar.set_max(20);
|
||||
relay_states[0].set_selected_index(1); // R1 OFF
|
||||
|
||||
options_mode.on_change = [this](size_t mode, OptionsField::value_t) {
|
||||
_mode = mode;
|
||||
|
||||
if (_mode) {
|
||||
// EP layout
|
||||
remove_children({ {
|
||||
&text_header,
|
||||
&header_code_a,
|
||||
&header_code_b,
|
||||
&checkbox_speaker,
|
||||
&bmp_speaker,
|
||||
&city_code_xs,
|
||||
&family_code_xy,
|
||||
&text_subfamily,
|
||||
&subfamily_code,
|
||||
&checkbox_wcsubfamily,
|
||||
&text_receiver,
|
||||
&receiver_code,
|
||||
&checkbox_wcid,
|
||||
&relay_states[2],
|
||||
&relay_states[3]
|
||||
} });
|
||||
add_children({ {
|
||||
&city_code_ep,
|
||||
&family_code_ep
|
||||
} });
|
||||
set_dirty();
|
||||
} else {
|
||||
// Xy layout
|
||||
remove_children({ {
|
||||
&city_code_ep,
|
||||
&family_code_ep
|
||||
} });
|
||||
add_children({ {
|
||||
&text_header,
|
||||
&header_code_a,
|
||||
&header_code_b,
|
||||
&checkbox_speaker,
|
||||
&bmp_speaker,
|
||||
&city_code_xy,
|
||||
&family_code_xy,
|
||||
&text_subfamily,
|
||||
&subfamily_code,
|
||||
&checkbox_wcsubfamily,
|
||||
&text_receiver,
|
||||
&receiver_code,
|
||||
&checkbox_wcid,
|
||||
&relay_states[2],
|
||||
&relay_states[3]
|
||||
} });
|
||||
set_dirty();
|
||||
};
|
||||
generate_message();
|
||||
};
|
||||
|
||||
checkbox_speaker.on_select = [this](Checkbox&) {
|
||||
speaker_enabled = checkbox_speaker.value();
|
||||
};
|
||||
|
||||
header_code_a.on_change = [this](int32_t) {
|
||||
generate_message();
|
||||
};
|
||||
header_code_b.on_change = [this](int32_t) {
|
||||
generate_message();
|
||||
};
|
||||
city_code_xy.on_change = [this](int32_t) {
|
||||
generate_message();
|
||||
};
|
||||
family_code_xy.on_change = [this](int32_t) {
|
||||
generate_message();
|
||||
};
|
||||
subfamily_code.on_change = [this](int32_t) {
|
||||
generate_message();
|
||||
};
|
||||
receiver_code.on_change = [this](int32_t) {
|
||||
generate_message();
|
||||
};
|
||||
|
||||
checkbox_wcsubfamily.on_select = [this](Checkbox&) {
|
||||
if (checkbox_wcsubfamily.value()) {
|
||||
subfamily_code.set_focusable(false);
|
||||
subfamily_code.set_style(&style_grey);
|
||||
text_subfamily.set_style(&style_grey);
|
||||
} else {
|
||||
subfamily_code.set_focusable(true);
|
||||
subfamily_code.set_style(&style());
|
||||
text_subfamily.set_style(&style());
|
||||
}
|
||||
generate_message();
|
||||
};
|
||||
|
||||
checkbox_wcid.on_select = [this](Checkbox&) {
|
||||
if (checkbox_wcid.value()) {
|
||||
receiver_code.set_focusable(false);
|
||||
receiver_code.set_style(&style_grey);
|
||||
text_receiver.set_style(&style_grey);
|
||||
} else {
|
||||
receiver_code.set_focusable(true);
|
||||
receiver_code.set_style(&style());
|
||||
text_receiver.set_style(&style());
|
||||
}
|
||||
generate_message();
|
||||
};
|
||||
|
||||
checkbox_wcsubfamily.set_value(true);
|
||||
checkbox_wcid.set_value(true);
|
||||
|
||||
const auto relay_state_fn = [this](size_t, OptionsField::value_t) {
|
||||
this->generate_message();
|
||||
};
|
||||
|
||||
n = 0;
|
||||
for (auto& relay_state : relay_states) {
|
||||
relay_state.on_change = relay_state_fn;
|
||||
relay_state.set_parent_rect({
|
||||
static_cast<Coord>(26 + (n * 53)),
|
||||
174,
|
||||
24, 24
|
||||
});
|
||||
relay_state.set_options(relay_options);
|
||||
add_child(&relay_state);
|
||||
n++;
|
||||
}
|
||||
|
||||
button_transmit.set_style(&style_val);
|
||||
|
||||
generate_message();
|
||||
|
||||
button_transmit.on_select = [this, &nav](Button&) {
|
||||
if (tx_mode == IDLE) {
|
||||
//auto modal_view = nav.push<ModalMessageView>("TX", "TX ?", true);
|
||||
//modal_view->on_choice = [this](bool choice) {
|
||||
// if (choice) {
|
||||
if (speaker_enabled && _mode)
|
||||
audio::headphone::set_volume(volume_t::decibel(90 - 99) + audio::headphone::volume_range().max);
|
||||
tx_mode = SINGLE;
|
||||
button_transmit.set_style(&style_cancel);
|
||||
button_transmit.set_text("Wait");
|
||||
generate_message();
|
||||
start_tx();
|
||||
// }
|
||||
//};
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
} /* namespace ui */
|
429
firmware/application/ui_bht_tx.hpp
Normal file
429
firmware/application/ui_bht_tx.hpp
Normal file
@ -0,0 +1,429 @@
|
||||
/*
|
||||
* 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 "ui.hpp"
|
||||
#include "ui_widget.hpp"
|
||||
#include "ui_navigation.hpp"
|
||||
#include "ui_font_fixed_8x16.hpp"
|
||||
|
||||
#include "bmp_bulb_on.hpp"
|
||||
#include "bmp_bulb_off.hpp"
|
||||
#include "bmp_bulb_ignore.hpp"
|
||||
|
||||
#include "message.hpp"
|
||||
#include "volume.hpp"
|
||||
#include "audio.hpp"
|
||||
#include "transmitter_model.hpp"
|
||||
#include "encoders.hpp"
|
||||
//#include "receiver_model.hpp"
|
||||
#include "portapack.hpp"
|
||||
|
||||
using namespace encoders;
|
||||
|
||||
#define CCIR_TONE_LENGTH (153600-1) // 1536000*0.1
|
||||
#define CCIR_DELTA_COEF (43.691) // (65536*1024)/1536000
|
||||
#define CCIR_SILENCE (614400-1) // 400ms
|
||||
|
||||
namespace ui {
|
||||
|
||||
class BHTView : public View {
|
||||
public:
|
||||
BHTView(NavigationView& nav);
|
||||
~BHTView();
|
||||
|
||||
void focus() override;
|
||||
|
||||
std::string title() const override { return "BHT transmit"; };
|
||||
|
||||
private:
|
||||
const uint32_t ccir_deltas[16] = {
|
||||
(uint32_t)(1981 * CCIR_DELTA_COEF),
|
||||
(uint32_t)(1124 * CCIR_DELTA_COEF),
|
||||
(uint32_t)(1197 * CCIR_DELTA_COEF),
|
||||
(uint32_t)(1275 * CCIR_DELTA_COEF),
|
||||
(uint32_t)(1358 * CCIR_DELTA_COEF),
|
||||
(uint32_t)(1446 * CCIR_DELTA_COEF),
|
||||
(uint32_t)(1540 * CCIR_DELTA_COEF),
|
||||
(uint32_t)(1640 * CCIR_DELTA_COEF),
|
||||
(uint32_t)(1747 * CCIR_DELTA_COEF),
|
||||
(uint32_t)(1860 * CCIR_DELTA_COEF),
|
||||
(uint32_t)(2400 * CCIR_DELTA_COEF),
|
||||
(uint32_t)(930 * CCIR_DELTA_COEF),
|
||||
(uint32_t)(2247 * CCIR_DELTA_COEF),
|
||||
(uint32_t)(991 * CCIR_DELTA_COEF),
|
||||
(uint32_t)(2110 * CCIR_DELTA_COEF),
|
||||
(uint32_t)(1055 * CCIR_DELTA_COEF)
|
||||
};
|
||||
|
||||
enum tx_modes {
|
||||
IDLE = 0,
|
||||
SINGLE,
|
||||
SEQUENCE
|
||||
};
|
||||
|
||||
tx_modes tx_mode = IDLE;
|
||||
|
||||
struct bht_city {
|
||||
std::string name;
|
||||
uint8_t freq_index;
|
||||
bool recent;
|
||||
};
|
||||
|
||||
const bht_city bht_cities[122] = {
|
||||
{ "Aizenay", 0, false },
|
||||
{ "Albertville", 3, false },
|
||||
{ "Ales", 3, false },
|
||||
{ "Artannes/Indre", 5, false },
|
||||
{ "Avignon", 3, true },
|
||||
{ "Azay-le-Rideau", 5, false },
|
||||
{ "Baux Ste. Croix", 0, false },
|
||||
{ "Beaugency", 4, false },
|
||||
{ "Beaune", 4, false },
|
||||
{ "Betton", 2, false },
|
||||
{ "Bihorel", 0, true },
|
||||
{ "Blanquefort", 4, true },
|
||||
{ "Bobigny", 5, false },
|
||||
{ "Bouffere", 4, true },
|
||||
{ "Boulogne/Mer", 0, true },
|
||||
{ "Bourg-en-Bresse", 3, false },
|
||||
{ "Bourges", 0, false },
|
||||
{ "Bouscat", 0, false },
|
||||
{ "Carquefou", 5, false },
|
||||
{ "St. Cast", 0, false },
|
||||
{ "Caudebec/Caux", 3, true },
|
||||
{ "Cercy-la-Tour", 5, false },
|
||||
{ "Chamalieres", 5, false },
|
||||
{ "St. Chamond", 5, false },
|
||||
{ "Chapelle/Fgrtz", 2, false },
|
||||
{ "Charite/Loire", 3, false },
|
||||
{ "Charleville-Mzr", 1, false },
|
||||
{ "Chilly Mazarin", 5, false },
|
||||
{ "Clermont Frrd.", 5, false },
|
||||
{ "Cluses", 2, false },
|
||||
{ "Compiegne", 4, false },
|
||||
{ "Coulanges/Nevers", 5, false },
|
||||
{ "Cour Cheverny", 5, false },
|
||||
{ "Cournon Auvergne", 5, false },
|
||||
{ "Crolles", 5, true },
|
||||
{ "Cublize", 4, true },
|
||||
{ "Donges", 5, false },
|
||||
{ "Emalleville", 0, false },
|
||||
{ "Etrepagny", 0, false },
|
||||
{ "Fecamp", 0, false },
|
||||
{ "Ferriere", 0, false },
|
||||
{ "Ferte Imbault", 5, false },
|
||||
{ "Fontaine", 5, true },
|
||||
{ "Forbach", 3, false },
|
||||
{ "Fourchambault", 5, false },
|
||||
{ "Fresnay/Sarthe", 3, false },
|
||||
{ "St Fulgent", 5, true },
|
||||
{ "Gaillac", 3, true },
|
||||
{ "St. Georges/Grs", 0, false },
|
||||
{ "St. Gervais/Frt", 5, false },
|
||||
{ "Givors", 5, false },
|
||||
{ "Guichen", 2, false },
|
||||
{ "Guildo", 0, false },
|
||||
{ "Guipry", 2, false },
|
||||
{ "St Hilaire/Riez", 0, false },
|
||||
{ "Hossegor/Capbrtn", 0, true },
|
||||
{ "Houlbec-Cocherel", 0, false },
|
||||
{ "Huisseau/Cosson", 5, false },
|
||||
{ "Huningue", 5, false },
|
||||
{ "Iffendic", 2, false },
|
||||
{ "La Croix St. Ouen", 4, false },
|
||||
{ "Langrune/Mer", 0, false },
|
||||
{ "Le Neubourg", 2, true },
|
||||
{ "St Leger/Vignes", 5, false },
|
||||
{ "Levallois-Perret", 5, false },
|
||||
{ "Lille", 5, true },
|
||||
{ "Limoges", 5, false },
|
||||
{ "Longueil-Anel", 4, false },
|
||||
{ "Lormont", 5, true },
|
||||
{ "Mantes-la-Jolie", 5, false },
|
||||
{ "Martigues", 0, true },
|
||||
{ "Marzy", 5, false },
|
||||
{ "Ste. Memmie", 3, false },
|
||||
{ "Menton", 0, true },
|
||||
{ "Metz", 3, false },
|
||||
{ "Mezidon Canon", 1, false },
|
||||
{ "Millau", 5, false },
|
||||
{ "Miniac-Morvan", 2, false },
|
||||
{ "Mt. Pres Chambord", 5, false },
|
||||
{ "Montesson", 5, false },
|
||||
{ "Monts", 5, false },
|
||||
{ "Noisy-le-Grand", 4, true },
|
||||
{ "St Ouen", 5, false },
|
||||
{ "Ozoir/Ferriere", 5, false },
|
||||
{ "Pace", 2, false },
|
||||
{ "Pelussin", 5, false },
|
||||
{ "Petite Foret", 1, false },
|
||||
{ "Plestin/Greves", 0, false },
|
||||
{ "Pleumeur Bodou", 5, true },
|
||||
{ "Pont Audemer", 0, false },
|
||||
{ "Pontcharra", 5, true },
|
||||
{ "Pontchateau", 5, false },
|
||||
{ "Pressagny L'Org.", 0, false },
|
||||
{ "Remiremont", 4, true },
|
||||
{ "Ribeauville", 5, false },
|
||||
{ "La Roche sur Yon", 0, false },
|
||||
{ "Romorantin-Lant.", 5, false },
|
||||
{ "Rueil Malmaison", 5, false },
|
||||
{ "Sault-les-Rethel", 3, false },
|
||||
{ "Selles-St-Denis", 5, false },
|
||||
{ "Selles/Cher", 5, false },
|
||||
{ "Sens", 4, false },
|
||||
{ "Sezanne", 3, false },
|
||||
{ "Sommesous", 3, false },
|
||||
{ "Ste. Suzanne", 2, true },
|
||||
{ "Talence", 3, true },
|
||||
{ "Thionville", 3, false },
|
||||
{ "Thonon-les-Bains", 2, false },
|
||||
{ "Tours en Sologne", 5, true },
|
||||
{ "Trelaze", 5, true },
|
||||
{ "Trouville/Mer", 0, false },
|
||||
{ "Tulle", 2, false },
|
||||
{ "Ussel", 2, false },
|
||||
{ "Valberg", 5, true },
|
||||
{ "Valence", 5, false },
|
||||
{ "Velizy", 5, false },
|
||||
{ "Vesoul", 5, false },
|
||||
{ "Ville S. la Ferte", 0, false },
|
||||
{ "Villefrance/Saone", 5, false },
|
||||
{ "Villers Cotterets", 3, false },
|
||||
{ "Vitre", 2, false },
|
||||
{ "Vitry-le-Francois", 4, true }
|
||||
};
|
||||
|
||||
const rf::Frequency bht_freqs[7] = { 31325000, 31387500, 31437500, 31475000, 31687500, 31975000, 88000000 };
|
||||
|
||||
char ccir_message[21];
|
||||
bool speaker_enabled = false;
|
||||
size_t _mode = 0;
|
||||
|
||||
void ascii_to_ccir(char * ascii);
|
||||
void start_tx();
|
||||
void generate_message();
|
||||
void on_tx_progress(const int progress, const bool done);
|
||||
|
||||
const Style style_val {
|
||||
.font = font::fixed_8x16,
|
||||
.background = Color::black(),
|
||||
.foreground = Color::green(),
|
||||
};
|
||||
const Style style_cancel {
|
||||
.font = font::fixed_8x16,
|
||||
.background = Color::black(),
|
||||
.foreground = Color::red(),
|
||||
};
|
||||
const Style style_grey {
|
||||
.font = font::fixed_8x16,
|
||||
.background = Color::black(),
|
||||
.foreground = Color::grey(),
|
||||
};
|
||||
|
||||
|
||||
OptionsField options_mode {
|
||||
{ 10 * 8, 4 },
|
||||
10,
|
||||
{
|
||||
{ "Mode Xy.", 0 },
|
||||
{ "Mode EP.", 1 }
|
||||
}
|
||||
};
|
||||
|
||||
Checkbox checkbox_speaker {
|
||||
{ 22 * 8, 4 },
|
||||
0,
|
||||
""
|
||||
};
|
||||
Image bmp_speaker {
|
||||
{ 204, 8, 16, 16 },
|
||||
&bitmap_speaker,
|
||||
ui::Color::white(),
|
||||
ui::Color::black()
|
||||
};
|
||||
|
||||
Text text_header {
|
||||
{ 8 * 8, 3 * 8, 7 * 8, 16 },
|
||||
"Header:"
|
||||
};
|
||||
NumberField header_code_a {
|
||||
{ 16 * 8, 3 * 8 },
|
||||
2,
|
||||
{ 0, 99 },
|
||||
1,
|
||||
'0'
|
||||
};
|
||||
NumberField header_code_b {
|
||||
{ 18 * 8, 3 * 8 },
|
||||
2,
|
||||
{ 0, 99 },
|
||||
1,
|
||||
'0'
|
||||
};
|
||||
|
||||
Text text_city {
|
||||
{ 4 * 8, 5 * 8, 11 * 8, 16 },
|
||||
"Code ville:"
|
||||
};
|
||||
NumberField city_code_xy {
|
||||
{ 16 * 8, 5 * 8 },
|
||||
2,
|
||||
{ 0, 99 },
|
||||
1,
|
||||
' '
|
||||
};
|
||||
NumberField city_code_ep {
|
||||
{ 16 * 8, 5 * 8 },
|
||||
3,
|
||||
{ 0, 255 },
|
||||
1,
|
||||
' '
|
||||
};
|
||||
|
||||
Text text_family {
|
||||
{ 7 * 8, 7 * 8, 8 * 8, 16 },
|
||||
"Famille:"
|
||||
};
|
||||
NumberField family_code_xy {
|
||||
{ 16 * 8, 7 * 8 },
|
||||
1,
|
||||
{ 0, 9 },
|
||||
1,
|
||||
' '
|
||||
};
|
||||
OptionsField family_code_ep {
|
||||
{ 16 * 8, 7 * 8 },
|
||||
2,
|
||||
{
|
||||
{ "A ", 2 }, // See receiver PCB
|
||||
{ "B ", 1 },
|
||||
{ "C ", 0 },
|
||||
{ "TP", 3 }
|
||||
}
|
||||
};
|
||||
|
||||
Text text_subfamily {
|
||||
{ 2 * 8, 9 * 8 + 2, 13 * 8, 16 },
|
||||
"Sous-famille:"
|
||||
};
|
||||
NumberField subfamily_code {
|
||||
{ 16 * 8, 9 * 8 + 2 },
|
||||
1,
|
||||
{ 0, 9 },
|
||||
1,
|
||||
' '
|
||||
};
|
||||
Checkbox checkbox_wcsubfamily {
|
||||
{ 20 * 8, 8 * 8 + 6 },
|
||||
6,
|
||||
"Toutes"
|
||||
};
|
||||
|
||||
Text text_receiver {
|
||||
{ 2 * 8, 13 * 8, 13 * 8, 16 },
|
||||
"ID recepteur:"
|
||||
};
|
||||
NumberField receiver_code {
|
||||
{ 16 * 8, 13 * 8 },
|
||||
2,
|
||||
{ 0, 99 },
|
||||
1,
|
||||
'0'
|
||||
};
|
||||
Checkbox checkbox_wcid {
|
||||
{ 20 * 8, 12 * 8 + 4 },
|
||||
4,
|
||||
"Tous"
|
||||
};
|
||||
|
||||
Text text_freq {
|
||||
{ 6 * 8, 8 * 16, 10 * 8, 16 },
|
||||
"Frequence:"
|
||||
};
|
||||
OptionsField options_freq {
|
||||
{ 17 * 8, 8 * 16},
|
||||
7,
|
||||
{
|
||||
{ "31.3250", 0 },
|
||||
{ "31.3875", 1 },
|
||||
{ "31.4375", 2 },
|
||||
{ "31.4750", 3 },
|
||||
{ "31.6875", 4 },
|
||||
{ "31.9750", 5 },
|
||||
{ "TEST 88", 6 }
|
||||
}
|
||||
};
|
||||
|
||||
Text text_relais {
|
||||
{ 8, 19 * 8, 13 * 8, 16 },
|
||||
"Etats relais:"
|
||||
};
|
||||
|
||||
std::array<ImageOptionsField, 4> relay_states;
|
||||
|
||||
ImageOptionsField::options_t relay_options = {
|
||||
{ &bulb_ignore_bmp[0], 0 },
|
||||
{ &bulb_off_bmp[0], 1 },
|
||||
{ &bulb_on_bmp[0], 2 }
|
||||
};
|
||||
|
||||
ProgressBar progressbar {
|
||||
{ 5 * 8, 27 * 8, 20 * 8, 16 },
|
||||
};
|
||||
Text text_message {
|
||||
{ 5 * 8, 29 * 8, 20 * 8, 16 },
|
||||
""
|
||||
};
|
||||
|
||||
Button button_transmit {
|
||||
{ 2 * 8, 16 * 16, 12 * 8, 32 },
|
||||
"START"
|
||||
};
|
||||
|
||||
Checkbox checkbox_cligno {
|
||||
{ 16 * 8, 16 * 16 + 4},
|
||||
3,
|
||||
"J/N"
|
||||
};
|
||||
NumberField tempo_cligno {
|
||||
{ 24 * 8, 16 * 16 + 8},
|
||||
2,
|
||||
{ 1, 99 },
|
||||
1,
|
||||
' '
|
||||
};
|
||||
Text text_cligno {
|
||||
{ 26 * 8, 16 * 16 + 8, 2 * 8, 16 },
|
||||
"s."
|
||||
};
|
||||
|
||||
MessageHandlerRegistration message_handler_tx_done {
|
||||
Message::ID::TXDone,
|
||||
[this](const Message* const p) {
|
||||
const auto message = *reinterpret_cast<const TXDoneMessage*>(p);
|
||||
this->on_tx_progress(message.progress, message.done);
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
} /* namespace ui */
|
@ -23,13 +23,9 @@
|
||||
#include "ui_closecall.hpp"
|
||||
#include "msgpack.hpp"
|
||||
|
||||
#include "ch.h"
|
||||
#include "time.hpp"
|
||||
|
||||
#include "event_m0.hpp"
|
||||
#include "hackrf_gpio.hpp"
|
||||
#include "portapack.hpp"
|
||||
#include "radio.hpp"
|
||||
#include "baseband_api.hpp"
|
||||
#include "string_format.hpp"
|
||||
|
||||
|
@ -25,8 +25,6 @@
|
||||
#include "spectrum_color_lut.hpp"
|
||||
|
||||
#include "ui_receiver.hpp"
|
||||
#include "ui_spectrum.hpp"
|
||||
#include "ui_record_view.hpp"
|
||||
#include "ui_font_fixed_8x16.hpp"
|
||||
|
||||
namespace ui {
|
||||
|
@ -208,36 +208,6 @@ private:
|
||||
};
|
||||
};
|
||||
|
||||
class DebugSDView : public View {
|
||||
public:
|
||||
DebugSDView(NavigationView& nav);
|
||||
|
||||
void focus() override;
|
||||
|
||||
void paint(Painter& painter) override;
|
||||
|
||||
private:
|
||||
Text text_title {
|
||||
{ 32, 16, 128, 16 },
|
||||
"SD card debug"
|
||||
};
|
||||
|
||||
Text text_modules {
|
||||
{ 8, 32, 28 * 8, 16 },
|
||||
"TESTTESTTESTTESTTESTTESTTEST"
|
||||
};
|
||||
|
||||
Button button_makefile {
|
||||
{ 72, 192, 96, 24 },
|
||||
"Play file"
|
||||
};
|
||||
|
||||
Button button_done {
|
||||
{ 72, 240, 96, 24 },
|
||||
"Done"
|
||||
};
|
||||
};
|
||||
|
||||
class DebugLCRView : public View {
|
||||
public:
|
||||
DebugLCRView(NavigationView& nav, std::string lcrstring, uint8_t checksum);
|
||||
|
@ -210,7 +210,7 @@ void EncodersView::start_tx(const bool scan) {
|
||||
transmitter_model.set_baseband_bandwidth(1750000);
|
||||
transmitter_model.enable();
|
||||
|
||||
memcpy(shared_memory.tx_data, ook_bitstream, 256);
|
||||
memcpy(shared_memory.bb_data.data, ook_bitstream, 256);
|
||||
|
||||
baseband::set_ook_data(
|
||||
ook_bitstream_length,
|
||||
@ -233,7 +233,7 @@ void EncodersView::on_type_change(size_t index) {
|
||||
|
||||
encoder_def = &encoder_defs[enc_type];
|
||||
|
||||
numberfield_clk.set_value(encoder_def->default_frequency / 1000);
|
||||
numberfield_clk.set_value(encoder_def->default_speed / 1000);
|
||||
|
||||
// SymField setup
|
||||
word_length = encoder_def->word_length;
|
||||
|
@ -24,12 +24,13 @@
|
||||
#include "ui_widget.hpp"
|
||||
#include "ui_navigation.hpp"
|
||||
#include "ui_font_fixed_8x16.hpp"
|
||||
#include "encoders.hpp"
|
||||
#include "message.hpp"
|
||||
#include "transmitter_model.hpp"
|
||||
|
||||
namespace ui {
|
||||
using namespace encoders;
|
||||
|
||||
#define ENC_TYPES_COUNT 14
|
||||
namespace ui {
|
||||
|
||||
class EncodersView : public View {
|
||||
public:
|
||||
@ -43,191 +44,6 @@ public:
|
||||
std::string title() const override { return "Encoders TX"; };
|
||||
|
||||
private:
|
||||
struct encoder_def_t {
|
||||
std::string name; // Encoder chip ref/name
|
||||
std::string address_symbols; // "01", "01F"...
|
||||
std::string data_symbols; // Same
|
||||
uint16_t clk_per_symbol; // Oscillator periods per symbol
|
||||
uint16_t clk_per_fragment; // Oscillator periods per symbol fragment (state)
|
||||
std::vector<std::string> bit_format; // List of fragments for each symbol in previous *_symbols list order
|
||||
uint8_t word_length; // Total # of symbols (not counting sync)
|
||||
std::string word_format; // A for Address, D for Data, S for sync
|
||||
std::string sync; // Like bit_format
|
||||
uint32_t default_frequency; // Default encoder clk frequency (often set by shitty resistor)
|
||||
uint8_t repeat_min; // Minimum repeat count
|
||||
uint16_t pause_symbols; // Length of pause between repeats in symbols
|
||||
};
|
||||
|
||||
const encoder_def_t encoder_defs[ENC_TYPES_COUNT] = {
|
||||
// PT2260-R2
|
||||
{
|
||||
"2260-R2",
|
||||
"01F", "01",
|
||||
1024, 128,
|
||||
{ "10001000", "11101110", "10001110" },
|
||||
12, "AAAAAAAAAADDS",
|
||||
"10000000000000000000000000000000",
|
||||
150000, 2,
|
||||
0
|
||||
},
|
||||
|
||||
// PT2260-R4
|
||||
{
|
||||
"2260-R4",
|
||||
"01F", "01",
|
||||
1024, 128,
|
||||
{ "10001000", "11101110", "10001110" },
|
||||
12, "AAAAAAAADDDDS",
|
||||
"10000000000000000000000000000000",
|
||||
150000, 2,
|
||||
0
|
||||
},
|
||||
|
||||
// PT2262
|
||||
{
|
||||
"2262 ",
|
||||
"01F", "01F",
|
||||
32, 4,
|
||||
{ "10001000", "11101110", "10001110" },
|
||||
12, "AAAAAAAAAAAAS",
|
||||
"10000000000000000000000000000000",
|
||||
20000, 4,
|
||||
0
|
||||
},
|
||||
|
||||
// 16-bit ?
|
||||
{
|
||||
"16-bit ",
|
||||
"01", "01",
|
||||
32, 8,
|
||||
{ "1110", "1000" }, // Opposite ?
|
||||
16, "AAAAAAAAAAAAAAAAS",
|
||||
"100000000000000000000",
|
||||
25000, 50,
|
||||
0 // ?
|
||||
},
|
||||
|
||||
// RT1527
|
||||
{
|
||||
"1527 ",
|
||||
"01", "01",
|
||||
128, 32,
|
||||
{ "1000", "1110" },
|
||||
24, "SAAAAAAAAAAAAAAAAAAAADDDD",
|
||||
"10000000000000000000000000000000",
|
||||
100000, 4,
|
||||
10 // ?
|
||||
},
|
||||
|
||||
// HK526E
|
||||
{
|
||||
"526 ",
|
||||
"01", "01",
|
||||
24, 8,
|
||||
{ "110", "100" },
|
||||
12, "AAAAAAAAAAAA",
|
||||
"",
|
||||
20000, 4,
|
||||
10 // ?
|
||||
},
|
||||
|
||||
// HT12E
|
||||
{
|
||||
"12E ",
|
||||
"01", "01",
|
||||
3, 1,
|
||||
{ "011", "001" },
|
||||
12, "SAAAAAAAADDDD",
|
||||
"0000000000000000000000000000000000001",
|
||||
3000, 4,
|
||||
10 // ?
|
||||
},
|
||||
|
||||
// VD5026 13 bits ?
|
||||
{
|
||||
"5026 ",
|
||||
"0123", "0123",
|
||||
128, 8,
|
||||
{ "1000000010000000", "1111111011111110", "1111111010000000", "1000000011111110" },
|
||||
12, "SAAAAAAAAAAAA",
|
||||
"000000000000000000000000000000000000000000000001", // ?
|
||||
100000, 4,
|
||||
10 // ?
|
||||
},
|
||||
|
||||
// UM3750
|
||||
{
|
||||
"UM3750 ",
|
||||
"01", "01",
|
||||
96, 32,
|
||||
{ "011", "001" },
|
||||
12, "SAAAAAAAAAAAA",
|
||||
"1",
|
||||
100000, 4,
|
||||
0 // ?
|
||||
},
|
||||
|
||||
// UM3758
|
||||
{
|
||||
"UM3758 ",
|
||||
"01F", "01",
|
||||
96, 16,
|
||||
{ "011011", "001001", "011001" },
|
||||
18, "SAAAAAAAAAADDDDDDDD",
|
||||
"1",
|
||||
160000, 4,
|
||||
10 // ?
|
||||
},
|
||||
|
||||
// BA5104
|
||||
{
|
||||
"BA5104 ",
|
||||
"01", "01",
|
||||
3072, 768,
|
||||
{ "1000", "1110" },
|
||||
9, "SDDAAAAAAA",
|
||||
"",
|
||||
455000, 4,
|
||||
10 // ?
|
||||
},
|
||||
|
||||
// MC145026
|
||||
{
|
||||
"145026 ",
|
||||
"01F", "01",
|
||||
16, 1,
|
||||
{ "0111111101111111", "0100000001000000", "0111111101000000" },
|
||||
9, "SAAAAADDDD",
|
||||
"000000000000000000",
|
||||
455000, 2,
|
||||
2
|
||||
},
|
||||
|
||||
// HT6*** TODO: Add individual variations
|
||||
{
|
||||
"HT6*** ",
|
||||
"01F", "01",
|
||||
198, 33,
|
||||
{ "011011", "001001", "001011" },
|
||||
18, "SAAAAAAAAAAAADDDDDD",
|
||||
"0000000000000000000000000000000000001011001011001",
|
||||
80000, 3,
|
||||
10 // ?
|
||||
},
|
||||
|
||||
// TC9148
|
||||
{
|
||||
"TC9148 ",
|
||||
"01", "01",
|
||||
48, 12,
|
||||
{ "1000", "1110", },
|
||||
12, "AAAAAAAAAAAA",
|
||||
"",
|
||||
455000, 3,
|
||||
10 // ?
|
||||
}
|
||||
};
|
||||
|
||||
enum tx_modes {
|
||||
IDLE = 0,
|
||||
SINGLE,
|
||||
@ -373,7 +189,7 @@ private:
|
||||
Message::ID::TXDone,
|
||||
[this](const Message* const p) {
|
||||
const auto message = *reinterpret_cast<const TXDoneMessage*>(p);
|
||||
this->on_txdone(message.n);
|
||||
this->on_txdone(message.progress);
|
||||
}
|
||||
};
|
||||
};
|
||||
|
@ -1,235 +0,0 @@
|
||||
/*
|
||||
* 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 "ui_epar.hpp"
|
||||
|
||||
#include "ch.h"
|
||||
#include "hackrf_hal.hpp"
|
||||
|
||||
#include "event_m0.hpp"
|
||||
#include "ff.h"
|
||||
#include "hackrf_gpio.hpp"
|
||||
#include "portapack.hpp"
|
||||
#include "radio.hpp"
|
||||
|
||||
#include "hackrf_hal.hpp"
|
||||
#include "portapack_shared_memory.hpp"
|
||||
#include "portapack_persistent_memory.hpp"
|
||||
|
||||
#include <cstring>
|
||||
#include <stdio.h>
|
||||
|
||||
using namespace portapack;
|
||||
|
||||
namespace ui {
|
||||
|
||||
void EPARView::focus() {
|
||||
city_code.focus();
|
||||
}
|
||||
|
||||
EPARView::~EPARView() {
|
||||
transmitter_model.disable();
|
||||
}
|
||||
|
||||
void EPARView::update_message() {
|
||||
uint8_t c;
|
||||
|
||||
// Start bit
|
||||
epar_bits[0] = 1;
|
||||
|
||||
// To binary
|
||||
for (c = 0; c < 8; c++)
|
||||
epar_bits[c + 1] = ((city_code.value() >> c) & 1);
|
||||
|
||||
epar_bits[9] = options_group.selected_index_value() & 1;
|
||||
epar_bits[10] = (options_group.selected_index_value() >> 1) & 1;
|
||||
|
||||
if (checkbox_ra.value())
|
||||
epar_bits[11] = 1; // Bit 11 is relay 1 state
|
||||
else
|
||||
epar_bits[11] = 0;
|
||||
|
||||
if (checkbox_rb.value())
|
||||
epar_bits[12] = 1; // Bit 12 is relay 2 state
|
||||
else
|
||||
epar_bits[12] = 0;
|
||||
|
||||
// DEBUG
|
||||
char debug_binary[14];
|
||||
for (c = 0; c < 13; c++)
|
||||
debug_binary[c] = 0x30 + epar_bits[c];
|
||||
debug_binary[13] = 0;
|
||||
|
||||
text_debug.set(debug_binary);
|
||||
}
|
||||
|
||||
void EPARView::journuit() {
|
||||
chThdSleepMilliseconds(1000);
|
||||
|
||||
// Invert relay states
|
||||
checkbox_ra.set_value(~checkbox_ra.value());
|
||||
checkbox_rb.set_value(~checkbox_rb.value());
|
||||
|
||||
update_message();
|
||||
|
||||
shared_memory.transmit_done = false;
|
||||
memcpy(shared_memory.epardata, epar_bits, 13);
|
||||
transmitter_model.enable();
|
||||
}
|
||||
|
||||
void EPARView::on_tuning_frequency_changed(rf::Frequency f) {
|
||||
receiver_model.set_tuning_frequency(f);
|
||||
}
|
||||
|
||||
EPARView::EPARView(
|
||||
NavigationView& nav
|
||||
)
|
||||
{
|
||||
static constexpr Style style_val {
|
||||
.font = font::fixed_8x16,
|
||||
.background = Color::green(),
|
||||
.foreground = Color::black(),
|
||||
};
|
||||
|
||||
static constexpr Style style_cancel {
|
||||
.font = font::fixed_8x16,
|
||||
.background = Color::red(),
|
||||
.foreground = Color::black(),
|
||||
};
|
||||
|
||||
transmitter_model.set_baseband_configuration({
|
||||
.mode = 4,
|
||||
.sampling_rate = 1536000,
|
||||
.decimation_factor = 1,
|
||||
});
|
||||
|
||||
add_children({ {
|
||||
&text_city,
|
||||
&city_code,
|
||||
&text_group,
|
||||
&options_group,
|
||||
&checkbox_ra,
|
||||
&checkbox_rb,
|
||||
&excur,
|
||||
&text_freq,
|
||||
//&options_freq,
|
||||
&field_frequency,
|
||||
&progressbar,
|
||||
&text_debug,
|
||||
&button_transmit,
|
||||
&checkbox_cligno,
|
||||
&button_exit
|
||||
} });
|
||||
|
||||
city_code.set_value(220);
|
||||
options_group.set_selected_index(3); // TP
|
||||
//options_freq.set_selected_index(6); // 5 ! DEBUG
|
||||
|
||||
checkbox_ra.set_value(true);
|
||||
checkbox_rb.set_value(true);
|
||||
|
||||
excur.set_value(500);
|
||||
shared_memory.excursion = 500;
|
||||
excur.on_change = [this](int32_t v) {
|
||||
(void)v;
|
||||
shared_memory.excursion = excur.value();
|
||||
};
|
||||
|
||||
field_frequency.set_value(31387500); // 31.3805 receiver_model.tuning_frequency()
|
||||
field_frequency.set_step(500);
|
||||
field_frequency.on_change = [this](rf::Frequency f) {
|
||||
this->on_tuning_frequency_changed(f);
|
||||
};
|
||||
field_frequency.on_edit = [this, &nav]() {
|
||||
// TODO: Provide separate modal method/scheme?
|
||||
auto new_view = nav.push<FrequencyKeypadView>(receiver_model.tuning_frequency());
|
||||
new_view->on_changed = [this](rf::Frequency f) {
|
||||
this->on_tuning_frequency_changed(f);
|
||||
this->field_frequency.set_value(f);
|
||||
};
|
||||
};
|
||||
|
||||
city_code.on_change = [this](int32_t v) {
|
||||
(void)v;
|
||||
EPARView::update_message();
|
||||
};
|
||||
options_group.on_change = [this](size_t n, OptionsField::value_t v) {
|
||||
(void)n;
|
||||
(void)v;
|
||||
EPARView::update_message();
|
||||
};
|
||||
checkbox_ra.on_select = [this](Checkbox&) {
|
||||
EPARView::update_message();
|
||||
};
|
||||
checkbox_rb.on_select = [this](Checkbox&) {
|
||||
EPARView::update_message();
|
||||
};
|
||||
|
||||
button_transmit.set_style(&style_val);
|
||||
|
||||
EPARView::update_message();
|
||||
|
||||
button_transmit.on_select = [this](Button&) {
|
||||
if (txing == false) {
|
||||
update_message();
|
||||
|
||||
EventDispatcher::message_map().unregister_handler(Message::ID::TXDone);
|
||||
|
||||
EventDispatcher::message_map().register_handler(Message::ID::TXDone,
|
||||
[this](Message* const p) {
|
||||
const auto message = static_cast<const TXDoneMessage*>(p);
|
||||
if (message->n == 100) {
|
||||
transmitter_model.disable();
|
||||
progressbar.set_value(0);
|
||||
if (checkbox_cligno.value() == false) {
|
||||
txing = false;
|
||||
button_transmit.set_style(&style_val);
|
||||
button_transmit.set_text("START");
|
||||
} else {
|
||||
journuit();
|
||||
}
|
||||
} else {
|
||||
progressbar.set_value(message->n * 2); // 100/52
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
shared_memory.transmit_done = false;
|
||||
memcpy(shared_memory.epardata, epar_bits, 13);
|
||||
|
||||
transmitter_model.set_tuning_frequency(field_frequency.value());
|
||||
//transmitter_model.set_tuning_frequency(epar_freqs[options_freq.selected_index()]);
|
||||
|
||||
txing = true;
|
||||
button_transmit.set_style(&style_cancel);
|
||||
button_transmit.set_text("Wait");
|
||||
transmitter_model.enable();
|
||||
}
|
||||
};
|
||||
|
||||
button_exit.on_select = [&nav](Button&){
|
||||
nav.pop();
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
} /* namespace ui */
|
@ -1,155 +0,0 @@
|
||||
/*
|
||||
* 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 "ui.hpp"
|
||||
#include "ui_widget.hpp"
|
||||
#include "ui_painter.hpp"
|
||||
#include "ui_menu.hpp"
|
||||
#include "ui_navigation.hpp"
|
||||
#include "ui_font_fixed_8x16.hpp"
|
||||
#include "ui_receiver.hpp"
|
||||
#include "clock_manager.hpp"
|
||||
#include "message.hpp"
|
||||
#include "rf_path.hpp"
|
||||
#include "max2837.hpp"
|
||||
#include "volume.hpp"
|
||||
#include "transmitter_model.hpp"
|
||||
#include "receiver_model.hpp"
|
||||
|
||||
namespace ui {
|
||||
|
||||
class EPARView : public View {
|
||||
public:
|
||||
EPARView(NavigationView& nav);
|
||||
~EPARView();
|
||||
std::string title() const override { return "EPAR transmit"; };
|
||||
void journuit();
|
||||
|
||||
void talk();
|
||||
void update_message();
|
||||
void focus() override;
|
||||
|
||||
private:
|
||||
bool txing = false;
|
||||
const rf::Frequency epar_freqs[7] = { 31325000, 31387500, 31437500, 31475000, 31687500, 31975000, 88000000 };
|
||||
char epar_bits[13];
|
||||
void on_tuning_frequency_changed(rf::Frequency f);
|
||||
|
||||
/* |012345678901234567890123456789|
|
||||
* | Code ville: 000 |
|
||||
* | Groupe: 00 |
|
||||
* */
|
||||
|
||||
Text text_city {
|
||||
{ 6 * 8, 1 * 16, 11 * 8, 16 },
|
||||
"Code ville:"
|
||||
};
|
||||
NumberField city_code {
|
||||
{ 18 * 8, 1 * 16 },
|
||||
3,
|
||||
{ 0, 255 },
|
||||
0,
|
||||
' '
|
||||
};
|
||||
|
||||
Text text_group {
|
||||
{ 10 * 8, 2 * 16, 7 * 8, 16 },
|
||||
"Groupe:"
|
||||
};
|
||||
OptionsField options_group {
|
||||
{ 18 * 8, 2 * 16 },
|
||||
4,
|
||||
{
|
||||
{ "A ", 2 }, // See receiver PCB
|
||||
{ "B ", 1 },
|
||||
{ "C ", 0 },
|
||||
{ "TP", 3 }
|
||||
}
|
||||
};
|
||||
|
||||
Text text_freq {
|
||||
{ 5 * 8, 4 * 16, 10 * 8, 16 },
|
||||
"Frequence:"
|
||||
};
|
||||
FrequencyField field_frequency {
|
||||
{ 16 * 8, 4 * 16 },
|
||||
};
|
||||
/*OptionsField options_freq {
|
||||
{ 16 * 8, 4 * 16},
|
||||
7,
|
||||
{
|
||||
{ "31.3250", 0 },
|
||||
{ "31.3875", 1 },
|
||||
{ "31.4375", 2 },
|
||||
{ "31.4750", 3 },
|
||||
{ "31.6875", 4 },
|
||||
{ "31.9750", 5 },
|
||||
{ "TEST 88", 6 }
|
||||
}
|
||||
};*/
|
||||
|
||||
Checkbox checkbox_ra {
|
||||
{ 7 * 8, 6 * 16 },
|
||||
8,
|
||||
"Relais 1"
|
||||
};
|
||||
Checkbox checkbox_rb {
|
||||
{ 7 * 8, 8 * 16 },
|
||||
8,
|
||||
"Relais 2"
|
||||
};
|
||||
|
||||
NumberField excur {
|
||||
{ 12 * 8, 10 * 16 },
|
||||
4,
|
||||
{ 0, 5000 },
|
||||
20,
|
||||
' '
|
||||
};
|
||||
|
||||
ProgressBar progressbar {
|
||||
{ 2 * 8, 12 * 16, 26 * 8, 20 },
|
||||
};
|
||||
|
||||
Text text_debug {
|
||||
{ 5 * 8, 14 * 16, 13 * 8, 16 },
|
||||
"-------------"
|
||||
};
|
||||
|
||||
Button button_transmit {
|
||||
{ 2 * 8, 16 * 16, 64, 32 },
|
||||
"START"
|
||||
};
|
||||
|
||||
Checkbox checkbox_cligno {
|
||||
{ 96, 16 * 16 + 4},
|
||||
3,
|
||||
"J/N"
|
||||
};
|
||||
|
||||
Button button_exit {
|
||||
{ 21 * 8, 16 * 16, 64, 32 },
|
||||
"Exit"
|
||||
};
|
||||
};
|
||||
|
||||
} /* namespace ui */
|
@ -22,8 +22,6 @@
|
||||
|
||||
#include "ui_freqman.hpp"
|
||||
|
||||
#include "ch.h"
|
||||
#include "ff.h"
|
||||
#include "portapack.hpp"
|
||||
#include "event_m0.hpp"
|
||||
#include "portapack_shared_memory.hpp"
|
||||
@ -35,13 +33,36 @@ using namespace portapack;
|
||||
namespace ui {
|
||||
|
||||
void FrequencySaveView::focus() {
|
||||
button_exit.focus();
|
||||
button_save_timestamp.focus();
|
||||
}
|
||||
|
||||
FrequencySaveView::FrequencySaveView(
|
||||
NavigationView& nav,
|
||||
const rf::Frequency value
|
||||
) {
|
||||
add_children({ {
|
||||
&big_display,
|
||||
&text_save,
|
||||
&button_save_name,
|
||||
&button_save_timestamp,
|
||||
&button_cancel
|
||||
} });
|
||||
|
||||
big_display.set(value);
|
||||
|
||||
button_cancel.on_select = [this, &nav](Button&) {
|
||||
nav.pop();
|
||||
};
|
||||
}
|
||||
|
||||
void FrequencyLoadView::focus() {
|
||||
button_exit.focus();
|
||||
}
|
||||
|
||||
FrequencyLoadView::FrequencyLoadView(
|
||||
NavigationView& nav,
|
||||
const rf::Frequency value
|
||||
) {
|
||||
|
||||
add_children({ {
|
||||
&button_exit
|
||||
|
@ -37,6 +37,40 @@ public:
|
||||
|
||||
std::string title() const override { return "Save frequency"; };
|
||||
|
||||
private:
|
||||
BigFrequency big_display {
|
||||
{ 4, 2 * 16, 28 * 8, 32 },
|
||||
0
|
||||
};
|
||||
|
||||
Text text_save {
|
||||
{ 72, 124, 10 * 8, 16 },
|
||||
"Save with:",
|
||||
};
|
||||
Button button_save_name {
|
||||
{ 72, 144, 96, 32 },
|
||||
"Name"
|
||||
};
|
||||
Button button_save_timestamp {
|
||||
{ 72, 184, 96, 32 },
|
||||
"Timestamp"
|
||||
};
|
||||
|
||||
Button button_cancel {
|
||||
{ 72, 264, 96, 32 },
|
||||
"Cancel"
|
||||
};
|
||||
};
|
||||
|
||||
class FrequencyLoadView : public View {
|
||||
public:
|
||||
FrequencyLoadView(NavigationView& nav, const rf::Frequency value);
|
||||
//~FrequencySaveView();
|
||||
|
||||
void focus() override;
|
||||
|
||||
std::string title() const override { return "Load frequency"; };
|
||||
|
||||
private:
|
||||
Button button_exit {
|
||||
{ 72, 264, 96, 32 },
|
||||
|
@ -22,11 +22,7 @@
|
||||
|
||||
#include "ui_handwrite.hpp"
|
||||
|
||||
#include "ch.h"
|
||||
|
||||
#include "ff.h"
|
||||
#include "portapack.hpp"
|
||||
#include "event_m0.hpp"
|
||||
#include "hackrf_hal.hpp"
|
||||
#include "portapack_shared_memory.hpp"
|
||||
|
||||
@ -43,17 +39,17 @@ void HandWriteView::paint(Painter& painter) {
|
||||
HandWriteView::HandWriteView(
|
||||
NavigationView& nav,
|
||||
char txt[],
|
||||
uint8_t max_len
|
||||
) {
|
||||
size_t max_length
|
||||
) : _max_length(max_length)
|
||||
{
|
||||
const char special_chars[5] = {'\'', '.', '?', '!', '='};
|
||||
_max_len = max_len;
|
||||
size_t n;
|
||||
|
||||
// Handwriting alphabet definition here
|
||||
handwriting = &handwriting_unistroke;
|
||||
|
||||
txtidx = strlen(txt);
|
||||
memcpy(txtinput, txt, max_len + 1);
|
||||
memcpy(txtinput, txt, _max_length + 1);
|
||||
n = txtidx;
|
||||
while (n && (txtinput[n - 1] == ' ')) {
|
||||
txtinput[--n] = 0;
|
||||
@ -71,7 +67,7 @@ HandWriteView::HandWriteView(
|
||||
};
|
||||
|
||||
n = 0;
|
||||
for(auto& button : num_buttons) {
|
||||
for (auto& button : num_buttons) {
|
||||
add_child(&button);
|
||||
button.on_select = button_fn;
|
||||
button.set_parent_rect({
|
||||
@ -80,15 +76,15 @@ HandWriteView::HandWriteView(
|
||||
24, 28
|
||||
});
|
||||
const std::string label {
|
||||
(char)(n + 0x30)
|
||||
(char)(n + '0')
|
||||
};
|
||||
button.set_text(label);
|
||||
button.id = n + 0x30;
|
||||
button.id = n + '0';
|
||||
n++;
|
||||
}
|
||||
|
||||
n = 0;
|
||||
for(auto& button : special_buttons) {
|
||||
for (auto& button : special_buttons) {
|
||||
add_child(&button);
|
||||
button.on_select = button_fn;
|
||||
button.set_parent_rect({
|
||||
@ -114,9 +110,9 @@ HandWriteView::HandWriteView(
|
||||
}
|
||||
};
|
||||
|
||||
button_ok.on_select = [this, &nav, txt, max_len](Button&) {
|
||||
memcpy(txt, txtinput, max_len + 1);
|
||||
on_changed(this->value());
|
||||
button_ok.on_select = [this, &nav, txt, max_length](Button&) {
|
||||
memcpy(txt, txtinput, max_length + 1);
|
||||
if (on_changed) on_changed(this->value());
|
||||
nav.pop();
|
||||
};
|
||||
|
||||
@ -134,14 +130,13 @@ bool HandWriteView::on_touch(const TouchEvent event) {
|
||||
guess_letter();
|
||||
}
|
||||
if (event.type == ui::TouchEvent::Type::Move) {
|
||||
if (tracing) {
|
||||
if (tracing)
|
||||
current_pos = event.point;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void HandWriteView::clear_zone(Color color, bool flash) {
|
||||
void HandWriteView::clear_zone(const Color color, const bool flash) {
|
||||
display.fill_rectangle(
|
||||
{{0, 32}, {240, 216}},
|
||||
color
|
||||
@ -149,6 +144,7 @@ void HandWriteView::clear_zone(Color color, bool flash) {
|
||||
if (flash) {
|
||||
flash_timer = 4;
|
||||
} else {
|
||||
// Draw grid
|
||||
_painter->draw_rectangle(
|
||||
{{0, 32}, {80, 216}},
|
||||
Color::grey()
|
||||
@ -169,7 +165,7 @@ void HandWriteView::clear_zone(Color color, bool flash) {
|
||||
}
|
||||
|
||||
void HandWriteView::guess_letter() {
|
||||
uint8_t symbol, match, count, stroke_idx, stroke_data;
|
||||
uint32_t symbol, match, count, stroke_idx, stroke_data;
|
||||
Condition cond;
|
||||
Direction dir;
|
||||
bool matched;
|
||||
@ -212,7 +208,7 @@ void HandWriteView::guess_letter() {
|
||||
} else if ((dir & 0x0F) == 0x0F) {
|
||||
if ((dir & 0xF0) != (stroke_data & 0xF0)) break;
|
||||
} else {
|
||||
if (dir != stroke_data) break;
|
||||
if (dir != (int32_t)stroke_data) break;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -231,7 +227,8 @@ void HandWriteView::guess_letter() {
|
||||
clear_zone(Color::green(), true); // Green flash
|
||||
} else {
|
||||
if (txtidx) {
|
||||
txtinput[--txtidx] = 0; // Erase
|
||||
txtidx--;
|
||||
txtinput[txtidx] = 0; // Erase
|
||||
clear_zone(Color::yellow(), true); // Yellow flash
|
||||
} else {
|
||||
clear_zone(Color::red(), true); // Red flash
|
||||
@ -386,6 +383,7 @@ void HandWriteView::on_show() {
|
||||
}
|
||||
|
||||
char * HandWriteView::value() {
|
||||
txtinput[txtidx] = 0;
|
||||
return txtinput;
|
||||
}
|
||||
|
||||
@ -395,10 +393,10 @@ void HandWriteView::on_button(Button& button) {
|
||||
}
|
||||
|
||||
void HandWriteView::char_add(const char c) {
|
||||
if (txtidx < _max_len) {
|
||||
txtinput[txtidx] = c;
|
||||
txtidx++;
|
||||
}
|
||||
if (txtidx >= _max_length) return;
|
||||
|
||||
txtinput[txtidx] = c;
|
||||
txtidx++;
|
||||
}
|
||||
|
||||
void HandWriteView::update_text() {
|
||||
|
@ -26,10 +26,8 @@
|
||||
#include "ui.hpp"
|
||||
#include "ui_widget.hpp"
|
||||
#include "ui_painter.hpp"
|
||||
#include "ui_menu.hpp"
|
||||
#include "ui_navigation.hpp"
|
||||
#include "unistroke.hpp"
|
||||
#include "message.hpp"
|
||||
|
||||
namespace ui {
|
||||
|
||||
@ -37,20 +35,20 @@ class HandWriteView : public View {
|
||||
public:
|
||||
std::function<void(char *)> on_changed;
|
||||
|
||||
HandWriteView(NavigationView& nav, char txt[], uint8_t max_len);
|
||||
HandWriteView(NavigationView& nav, char txt[], size_t max_length);
|
||||
|
||||
void paint(Painter& painter) override;
|
||||
void on_show() override;
|
||||
bool on_touch(const TouchEvent event) override;
|
||||
|
||||
char * value();
|
||||
|
||||
void char_add(const char c);
|
||||
|
||||
std::string title() const override { return "Text entry"; };
|
||||
|
||||
private:
|
||||
const HandWriting * handwriting;
|
||||
Painter * _painter;
|
||||
uint8_t _max_len;
|
||||
size_t _max_length;
|
||||
uint8_t dir_cnt = 0;
|
||||
uint8_t dir_prev;
|
||||
uint8_t flash_timer = 0;
|
||||
@ -61,12 +59,16 @@ private:
|
||||
uint8_t sample_skip, move_wait;
|
||||
uint8_t stroke_list[8];
|
||||
Point start_pos, current_pos, last_pos;
|
||||
bool _lowercase = true;
|
||||
char txtinput[25] = {0};
|
||||
bool _lowercase = false;
|
||||
char txtinput[29] = { 0 }; // 28 chars max
|
||||
|
||||
void sample_pen();
|
||||
void add_stroke(uint8_t dir);
|
||||
void guess_letter();
|
||||
void clear_zone(Color color, bool flash);
|
||||
void clear_zone(const Color color, const bool flash);
|
||||
void char_add(const char c);
|
||||
void on_button(Button& button);
|
||||
void update_text();
|
||||
|
||||
Text text_input {
|
||||
{ 8, 0, 224, 16 }
|
||||
@ -84,10 +86,6 @@ private:
|
||||
{ 190, 270, 40, 28 },
|
||||
"OK"
|
||||
};
|
||||
|
||||
void on_button(Button& button);
|
||||
|
||||
void update_text();
|
||||
|
||||
MessageHandlerRegistration message_handler_sample {
|
||||
Message::ID::DisplayFrameSync,
|
||||
|
@ -118,7 +118,7 @@ JammerView::JammerView(NavigationView& nav) {
|
||||
.foreground = Color::grey(),
|
||||
};
|
||||
|
||||
JammerRange * jammer_ranges = (JammerRange*)shared_memory.tx_data;
|
||||
JammerRange * jammer_ranges = (JammerRange*)shared_memory.bb_data.data;
|
||||
|
||||
add_children({ {
|
||||
&text_type,
|
||||
|
@ -259,7 +259,7 @@ void LCRView::start_tx(const bool scan) {
|
||||
transmitter_model.set_baseband_bandwidth(1750000);
|
||||
transmitter_model.enable();
|
||||
|
||||
memcpy(shared_memory.tx_data, lcr_message_data, 300);
|
||||
memcpy(shared_memory.bb_data.data, lcr_message_data, 300);
|
||||
|
||||
baseband::set_afsk_data(
|
||||
(153600 * 5) / portapack::persistent_memory::afsk_bitrate(),
|
||||
|
@ -179,7 +179,7 @@ private:
|
||||
Message::ID::TXDone,
|
||||
[this](const Message* const p) {
|
||||
const auto message = *reinterpret_cast<const TXDoneMessage*>(p);
|
||||
this->on_txdone(message.n);
|
||||
this->on_txdone(message.progress);
|
||||
}
|
||||
};
|
||||
};
|
||||
|
@ -49,19 +49,20 @@ void MenuItemView::paint(Painter& painter) {
|
||||
const auto paint_style = (highlighted() && parent()->has_focus()) ? style().invert() : style();
|
||||
|
||||
const auto font_height = paint_style.font.line_height();
|
||||
|
||||
ui::Color final_item_color = (highlighted() && parent()->has_focus()) ? paint_style.foreground : item.color;
|
||||
ui::Color final_bg_color = (highlighted() && parent()->has_focus()) ? item.color : paint_style.background;
|
||||
|
||||
if (final_item_color.v == final_bg_color.v) final_item_color = paint_style.foreground;
|
||||
|
||||
painter.fill_rectangle(
|
||||
r,
|
||||
paint_style.background
|
||||
final_bg_color
|
||||
);
|
||||
|
||||
ui::Color final_item_color = (highlighted() && parent()->has_focus()) ? ui::Color::black() : item.color;
|
||||
|
||||
if (final_item_color.v == paint_style.background.v) final_item_color = paint_style.foreground;
|
||||
|
||||
Style text_style {
|
||||
.font = paint_style.font,
|
||||
.background = paint_style.background,
|
||||
.background = final_bg_color,
|
||||
.foreground = final_item_color
|
||||
};
|
||||
|
||||
@ -84,6 +85,7 @@ MenuView::MenuView() {
|
||||
|
||||
arrow_more.set_parent_rect( { 216, 320 - 16 - 24, 16, 16 } );
|
||||
arrow_more.set_focusable(false);
|
||||
arrow_more.set_foreground(Color::black());
|
||||
}
|
||||
|
||||
MenuView::~MenuView() {
|
||||
@ -115,7 +117,7 @@ void MenuView::update_items() {
|
||||
size_t i = 0;
|
||||
Coord y_pos;
|
||||
|
||||
if (MENU_MAX + offset_ < (children_.size() - 1))
|
||||
if ((children_.size() - 1) > MENU_MAX + offset_)
|
||||
more_ = true;
|
||||
else
|
||||
more_ = false;
|
||||
@ -148,9 +150,8 @@ size_t MenuView::highlighted() const {
|
||||
}
|
||||
|
||||
bool MenuView::set_highlighted(const size_t new_value) {
|
||||
if( new_value >= (children_.size() - 1) ) { // Skip arrow widget
|
||||
if( new_value >= (children_.size() - 1) ) // Skip arrow widget
|
||||
return false;
|
||||
}
|
||||
|
||||
if ((new_value > offset_) && ((new_value - offset_ + 1) >= MENU_MAX)) {
|
||||
// Shift MenuView up
|
||||
|
@ -1,5 +1,6 @@
|
||||
/*
|
||||
* Copyright (C) 2014 Jared Boone, ShareBrained Technology, Inc.
|
||||
* Copyright (C) 2016 Furrtek
|
||||
*
|
||||
* This file is part of PortaPack.
|
||||
*
|
||||
|
143
firmware/application/ui_morse.cpp
Normal file
143
firmware/application/ui_morse.cpp
Normal file
@ -0,0 +1,143 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
/*
|
||||
Keying speed: 60 or 75 PARIS
|
||||
Continuous (Fox-oring)
|
||||
12s transmit, 48s space (Sprint 1/5th)
|
||||
60s transmit, 240s space (Classic 1/5 min)
|
||||
60s transmit, 360s space (Classic 1/7 min)
|
||||
*/
|
||||
|
||||
#include "ui_morse.hpp"
|
||||
|
||||
#include "portapack.hpp"
|
||||
#include "baseband_api.hpp"
|
||||
#include "portapack_persistent_memory.hpp"
|
||||
|
||||
#include <cstring>
|
||||
#include <stdio.h>
|
||||
|
||||
using namespace portapack;
|
||||
|
||||
// TODO: TX power setting
|
||||
// TODO: Live keying mode: Dit on left key, dah on right ?
|
||||
|
||||
namespace ui {
|
||||
|
||||
void MorseView::focus() {
|
||||
button_transmit.focus();
|
||||
}
|
||||
|
||||
MorseView::~MorseView() {
|
||||
transmitter_model.disable();
|
||||
baseband::shutdown();
|
||||
}
|
||||
|
||||
void MorseView::paint(Painter& painter) {
|
||||
(void)painter;
|
||||
}
|
||||
|
||||
void MorseView::generate_message(char * text) {
|
||||
char ch;
|
||||
size_t i, c;
|
||||
uint8_t code;
|
||||
uint8_t morse_message[256];
|
||||
|
||||
ToneDef * tone_defs = shared_memory.bb_data.tones_data.tone_defs;
|
||||
|
||||
// TODO: OOB check on morse_message[]
|
||||
|
||||
i = 0;
|
||||
while ((ch = (*text++))) {
|
||||
if ((ch >= 'a') && (ch <= 'z')) // Make uppercase
|
||||
ch -= 32;
|
||||
|
||||
if ((ch >= 'A') && (ch <= 'Z')) {
|
||||
code = morse_ITU[ch - 'A' + 10];
|
||||
for (c = 0; c < (code & 7); c++) {
|
||||
morse_message[i++] = (code << c) >> 7; // Dot/dash
|
||||
morse_message[i++] = 2; // Silence
|
||||
}
|
||||
morse_message[i - 1] = 3; // Letter silence
|
||||
} else if (ch == ' ') {
|
||||
morse_message[i++] = 4; // Word silence
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
memcpy(shared_memory.bb_data.tones_data.message, morse_message, i);
|
||||
|
||||
(*tone_defs).delta = MORSE_TONE_DELTA; // Dot
|
||||
(*tone_defs++).duration = MORSE_DOT;
|
||||
(*tone_defs).delta = MORSE_TONE_DELTA; // Dash
|
||||
(*tone_defs++).duration = MORSE_DASH;
|
||||
(*tone_defs).delta = 0; // 1 unit silence
|
||||
(*tone_defs++).duration = MORSE_SPACE;
|
||||
(*tone_defs).delta = 0; // 3 unit silence
|
||||
(*tone_defs++).duration = MORSE_LETTER_SPACE;
|
||||
(*tone_defs).delta = 0; // 7 unit silence
|
||||
(*tone_defs++).duration = MORSE_WORD_SPACE;
|
||||
|
||||
audio::set_rate(audio::Rate::Hz_24000);
|
||||
baseband::set_tones_data(5000, 0, i, false, false);
|
||||
}
|
||||
|
||||
MorseView::MorseView(
|
||||
NavigationView& nav
|
||||
)
|
||||
{
|
||||
add_children({ {
|
||||
&options_foxhunt,
|
||||
&button_transmit,
|
||||
&button_exit
|
||||
} });
|
||||
|
||||
button_transmit.on_select = [this](Button&){
|
||||
/*uint16_t c;
|
||||
ui::Context context;
|
||||
|
||||
make_frame();
|
||||
|
||||
shared_memory.afsk_samples_per_bit = 228000/persistent_memory::afsk_bitrate();
|
||||
shared_memory.afsk_phase_inc_mark = persistent_memory::afsk_mark_freq()*(65536*1024)/2280;
|
||||
shared_memory.afsk_phase_inc_space = persistent_memory::afsk_space_freq()*(65536*1024)/2280;
|
||||
|
||||
for (c = 0; c < 256; c++) {
|
||||
shared_memory.lcrdata[c] = this->lcrframe[c];
|
||||
}
|
||||
|
||||
shared_memory.afsk_transmit_done = false;
|
||||
shared_memory.afsk_repeat = 5; // DEFAULT
|
||||
|
||||
text_status.set("Send...");*/
|
||||
|
||||
//transmitter_model.enable();
|
||||
};
|
||||
|
||||
button_exit.on_select = [&nav](Button&){
|
||||
nav.pop();
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
} /* namespace ui */
|
198
firmware/application/ui_morse.hpp
Normal file
198
firmware/application/ui_morse.hpp
Normal file
@ -0,0 +1,198 @@
|
||||
/*
|
||||
* 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 "ui.hpp"
|
||||
#include "ui_widget.hpp"
|
||||
#include "ui_navigation.hpp"
|
||||
#include "ui_font_fixed_8x16.hpp"
|
||||
|
||||
#include "message.hpp"
|
||||
#include "volume.hpp"
|
||||
#include "audio.hpp"
|
||||
#include "transmitter_model.hpp"
|
||||
//#include "receiver_model.hpp"
|
||||
#include "portapack.hpp"
|
||||
|
||||
#define MORSE_TONE_DELTA ((1536000 / 800) - 1) // 1536000/800
|
||||
#define MORSE_UNIT (76800 - 1) // 1536000*0.05 (50ms)
|
||||
#define MORSE_DOT 1 * MORSE_UNIT
|
||||
#define MORSE_DASH 3 * MORSE_UNIT
|
||||
#define MORSE_SPACE 1 * MORSE_UNIT
|
||||
#define MORSE_LETTER_SPACE 3 * MORSE_UNIT
|
||||
#define MORSE_WORD_SPACE 7 * MORSE_UNIT
|
||||
|
||||
namespace ui {
|
||||
|
||||
class MorseView : public View {
|
||||
public:
|
||||
MorseView(NavigationView& nav);
|
||||
~MorseView();
|
||||
|
||||
void focus() override;
|
||||
void paint(Painter& painter) override;
|
||||
|
||||
private:
|
||||
//rf::Frequency f;
|
||||
|
||||
void generate_message(char * text);
|
||||
|
||||
const char foxhunt_codes[11][3] = {
|
||||
{ 'M', 'O', 'E' },
|
||||
{ 'M', 'O', 'I' },
|
||||
{ 'M', 'O', 'S' },
|
||||
{ 'M', 'O', 'H' },
|
||||
{ 'M', 'O', '5' },
|
||||
{ 'M', 'O', 'N' },
|
||||
{ 'M', 'O', 'D' },
|
||||
{ 'M', 'O', 'B' },
|
||||
{ 'M', 'O', '6' },
|
||||
{ 'M', 'O', 0 },
|
||||
{ 'S', 0, 0 }
|
||||
};
|
||||
|
||||
// 0=dot 1=dash
|
||||
const uint8_t morse_ITU[36] = {
|
||||
// Code Size
|
||||
0b11111101, // 0: 11111 101
|
||||
0b01111101, // 1: 01111 101
|
||||
0b00111101, // 2: 00111 101
|
||||
0b00011101, // 3: 00011 101
|
||||
0b00001101, // 4: 00001 101
|
||||
0b00000101, // 5: 00000 101
|
||||
0b10000101, // 6: 10000 101
|
||||
0b11000101, // 7: 11000 101
|
||||
0b11100101, // 8: 11100 101
|
||||
0b11110101, // 9: 11110 101
|
||||
0b01000010, // A: 01--- 010
|
||||
0b10000100, // B: 1000- 100
|
||||
0b10100100, // C: 1010- 100
|
||||
0b10000011, // D: 100-- 011
|
||||
0b00000001, // E: 0---- 001
|
||||
0b00100100, // F: 0010- 100
|
||||
0b11000011, // G: 110-- 011
|
||||
0b00000100, // H: 0000- 100
|
||||
0b00000010, // I: 00--- 010
|
||||
0b01110100, // J: 0111- 100
|
||||
0b10100011, // K: 101-- 011
|
||||
0b01000100, // L: 0100- 100
|
||||
0b11000010, // M: 11--- 010
|
||||
0b10000010, // N: 10--- 010
|
||||
0b11100011, // O: 111-- 011
|
||||
0b01100100, // P: 0110- 100 .#-###-###.# ##.#-### ##.#-###.# ##.#.# ##.#.#.# = 48 units
|
||||
0b11010100, // Q: 1101- 100 60s: 1.25s/unit
|
||||
0b01000011, // R: 010-- 011 75s: 1.5625s/unit
|
||||
0b00000011, // S: 000-- 011
|
||||
0b10000001, // T: 1---- 001
|
||||
0b00100011, // U: 001-- 011
|
||||
0b00010100, // V: 0001- 100
|
||||
0b01100011, // W: 011-- 011
|
||||
0b10010100, // X: 1001- 100
|
||||
0b10110100, // Y: 1011- 100
|
||||
0b11000100 // Z: 1100- 100
|
||||
};
|
||||
|
||||
const uint16_t morse_special[23] = {
|
||||
// Code Size
|
||||
0b1010110000000110, // !: 101011- 110
|
||||
0b0100100000000110, // ": 010010- 110
|
||||
0, // #
|
||||
0b0001001000000111, // $: 0001001 111
|
||||
0, // %
|
||||
0b0100000000000101, // &: 01000-- 101
|
||||
0b0111100000000110, // ': 011110- 110
|
||||
0b1011000000000101, // (: 10110-- 101
|
||||
0b1011010000000110, // ): 101101- 110
|
||||
0, // *
|
||||
0b0101000000000101, // +: 01010-- 101
|
||||
0b1100110000000110, // ,: 110011- 110
|
||||
0b1000010000000110, // -: 100001- 110
|
||||
0b0101010000000110, // .: 010101- 110
|
||||
0b1001000000000101, // /: 10010-- 101
|
||||
|
||||
0b1110000000000110, // :: 111000- 110
|
||||
0b1010100000000110, // ;: 101010- 110
|
||||
0, // <
|
||||
0b1000100000000101, // =: 10001-- 101
|
||||
0, // >
|
||||
0b0011000000000110, // ?: 001100- 110
|
||||
0b0110100000000110, // @: 011010- 110
|
||||
|
||||
0b0011010000000110 // _: 001101- 110
|
||||
};
|
||||
|
||||
const uint16_t prosigns[12] = {
|
||||
// Code Size
|
||||
0b0001110000001001, // <SOS>: 000111000 1001
|
||||
0b0101000000000100, // <AA>: 0101----- 0100
|
||||
0b0101000000000101, // <AR>: 01010---- 0101
|
||||
0b0100000000000101, // <AS>: 01000---- 0101
|
||||
0b1000100000000101, // <BT>: 10001---- 0101
|
||||
0b1010100000000101, // <CT>: 10101---- 0101
|
||||
0b0000000000001000, // <HH>: 00000000- 1000
|
||||
0b1010000000000011, // <K>: 101------ 0011
|
||||
0b1011000000000101, // <KN>: 10110---- 0101
|
||||
0b1001110000000110, // <NJ>: 100111--- 0110
|
||||
0b0001010000000110, // <SK>: 000101--- 0110
|
||||
0b0001100000000101, // <SN>: 00010---- 0101
|
||||
};
|
||||
|
||||
OptionsField options_foxhunt {
|
||||
{ 4 * 8, 32 },
|
||||
7,
|
||||
{
|
||||
{ "0 (MOE)", 0 },
|
||||
{ "1 (MOI)", 1 },
|
||||
{ "2 (MOS)", 2 },
|
||||
{ "3 (MOH)", 3 },
|
||||
{ "4 (MO5)", 4 },
|
||||
{ "5 (MON)", 5 },
|
||||
{ "6 (MOD)", 6 },
|
||||
{ "7 (MOB)", 7 },
|
||||
{ "8 (MO6)", 8 },
|
||||
{ "9 (MO)", 9 },
|
||||
{ "10 (S)", 10 }
|
||||
}
|
||||
};
|
||||
|
||||
Text text_status {
|
||||
{ 172, 196, 64, 16 },
|
||||
"Ready"
|
||||
};
|
||||
|
||||
Checkbox checkbox_am_a {
|
||||
{ 2 * 8, 68 },
|
||||
4,
|
||||
"TEST"
|
||||
};
|
||||
|
||||
Button button_transmit {
|
||||
{ 24, 270, 48, 32 },
|
||||
"TX"
|
||||
};
|
||||
|
||||
Button button_exit {
|
||||
{ 176, 270, 48, 32 },
|
||||
"Exit"
|
||||
};
|
||||
};
|
||||
|
||||
} /* namespace ui */
|
@ -32,30 +32,30 @@
|
||||
#include "bmp_modal_warning.hpp"
|
||||
|
||||
#include "ui_about.hpp"
|
||||
#include "ui_setup.hpp"
|
||||
#include "ui_debug.hpp"
|
||||
|
||||
#include "ui_numbers.hpp"
|
||||
#include "ui_whipcalc.hpp"
|
||||
#include "ui_closecall.hpp"
|
||||
#include "ui_freqman.hpp"
|
||||
#include "ui_nuoptix.hpp"
|
||||
#include "ui_soundboard.hpp"
|
||||
#include "ui_encoders.hpp"
|
||||
#include "ui_rds.hpp"
|
||||
#include "ui_xylos.hpp"
|
||||
#include "ui_epar.hpp"
|
||||
#include "ui_lcr.hpp"
|
||||
#include "analog_audio_app.hpp"
|
||||
#include "ui_adsbtx.hpp"
|
||||
#include "ui_closecall.hpp"
|
||||
#include "ui_debug.hpp"
|
||||
#include "ui_encoders.hpp"
|
||||
#include "ui_freqman.hpp"
|
||||
#include "ui_jammer.hpp"
|
||||
#include "ui_lcr.hpp"
|
||||
#include "ui_morse.hpp"
|
||||
#include "ui_numbers.hpp"
|
||||
#include "ui_nuoptix.hpp"
|
||||
#include "ui_rds.hpp"
|
||||
#include "ui_setup.hpp"
|
||||
#include "ui_soundboard.hpp"
|
||||
#include "ui_whipcalc.hpp"
|
||||
#include "ui_whistle.hpp"
|
||||
#include "ui_bht_tx.hpp"
|
||||
|
||||
#include "analog_audio_app.hpp"
|
||||
#include "ais_app.hpp"
|
||||
#include "ert_app.hpp"
|
||||
#include "tpms_app.hpp"
|
||||
#include "pocsag_app.hpp"
|
||||
#include "capture_app.hpp"
|
||||
|
||||
#include "ui_debug.hpp"
|
||||
#include "core_control.hpp"
|
||||
|
||||
#include "file.hpp"
|
||||
@ -66,28 +66,31 @@ namespace ui {
|
||||
/* SystemStatusView ******************************************************/
|
||||
|
||||
SystemStatusView::SystemStatusView() {
|
||||
uint8_t cfg;
|
||||
|
||||
add_children({ {
|
||||
&button_back,
|
||||
&title,
|
||||
&button_stealth,
|
||||
&button_textentry,
|
||||
&button_camera,
|
||||
&button_sleep,
|
||||
&sd_card_status_view,
|
||||
} });
|
||||
|
||||
cfg = portapack::persistent_memory::ui_config_textentry();
|
||||
|
||||
if (!cfg)
|
||||
if (portapack::persistent_memory::ui_config_textentry())
|
||||
button_textentry.set_bitmap(&bitmap_keyboard);
|
||||
else
|
||||
button_textentry.set_bitmap(&bitmap_unistroke);
|
||||
|
||||
if (portapack::persistent_memory::stealth_mode())
|
||||
button_stealth.set_foreground(ui::Color::green());
|
||||
|
||||
button_back.on_select = [this](Button&){
|
||||
if( this->on_back ) {
|
||||
if (this->on_back)
|
||||
this->on_back();
|
||||
}
|
||||
};
|
||||
|
||||
button_stealth.on_select = [this](ImageButton&) {
|
||||
this->on_stealth();
|
||||
};
|
||||
|
||||
button_textentry.on_select = [this](ImageButton&) {
|
||||
@ -117,6 +120,20 @@ void SystemStatusView::set_title(const std::string new_value) {
|
||||
}
|
||||
}
|
||||
|
||||
void SystemStatusView::on_stealth() {
|
||||
bool cfg;
|
||||
|
||||
cfg = portapack::persistent_memory::stealth_mode();
|
||||
portapack::persistent_memory::set_stealth_mode(not cfg);
|
||||
|
||||
if (!cfg)
|
||||
button_stealth.set_foreground(ui::Color::green());
|
||||
else
|
||||
button_stealth.set_foreground(ui::Color::light_grey());
|
||||
|
||||
button_stealth.set_dirty();
|
||||
}
|
||||
|
||||
void SystemStatusView::on_textentry() {
|
||||
uint8_t cfg;
|
||||
|
||||
@ -185,7 +202,7 @@ void NavigationView::pop_modal() {
|
||||
modal_view = nullptr;
|
||||
}
|
||||
|
||||
// Pop modal view and underlying app view
|
||||
// Pop modal view + underlying app view
|
||||
if( view_stack.size() > 2 ) {
|
||||
free_view();
|
||||
view_stack.pop_back();
|
||||
@ -221,8 +238,10 @@ void NavigationView::free_view() {
|
||||
|
||||
void NavigationView::update_view() {
|
||||
const auto new_view = view_stack.back().get();
|
||||
|
||||
add_child(new_view);
|
||||
new_view->set_parent_rect({ {0, 0}, size() });
|
||||
|
||||
focus();
|
||||
set_dirty();
|
||||
|
||||
@ -271,14 +290,13 @@ ReceiverMenuView::ReceiverMenuView(NavigationView& nav) {
|
||||
/* TransmitterCodedMenuView ******************************************************/
|
||||
|
||||
TransmitterCodedMenuView::TransmitterCodedMenuView(NavigationView& nav) {
|
||||
add_items<8>({ {
|
||||
{ "ADS-B Mode S", ui::Color::yellow(), [&nav](){ nav.push<ADSBTxView>(); } },
|
||||
{ "BHT Epar", ui::Color::grey(), [&nav](){ nav.push<NotImplementedView>(); } },
|
||||
{ "BHT Xylos", ui::Color::green(), [&nav](){ nav.push<XylosView>(); } },
|
||||
{ "Morse beacon", ui::Color::grey(), [&nav](){ nav.push<NotImplementedView>(); } },
|
||||
add_items<7>({ {
|
||||
{ "ADS-B Mode S", ui::Color::orange(), [&nav](){ nav.push<ADSBTxView>(); } },
|
||||
{ "BHT EPAR/Xylos", ui::Color::yellow(), [&nav](){ nav.push<BHTView>(); } },
|
||||
{ "Morse beacon", ui::Color::yellow(), [&nav](){ nav.push<MorseView>(); } },
|
||||
{ "Nuoptix DTMF timecode", ui::Color::green(), [&nav](){ nav.push<NuoptixView>(); } },
|
||||
{ "OOK remote encoders", ui::Color::green(), [&nav](){ nav.push<EncodersView>(); } },
|
||||
{ "RDS", ui::Color::orange(), [&nav](){ nav.push<RDSView>(); } },
|
||||
{ "RDS", ui::Color::green(), [&nav](){ nav.push<RDSView>(); } },
|
||||
{ "TEDI/LCR AFSK", ui::Color::green(), [&nav](){ nav.push<LCRView>(); } },
|
||||
} });
|
||||
on_left = [&nav](){ nav.pop(); };
|
||||
@ -291,7 +309,7 @@ TransmitterAudioMenuView::TransmitterAudioMenuView(NavigationView& nav) {
|
||||
{ "Soundboard", ui::Color::green(), [&nav](){ nav.push<SoundBoardView>(); } },
|
||||
{ "Numbers station", ui::Color::green(), [&nav](){ nav.push<NumbersStationView>(); } },
|
||||
{ "Microphone", ui::Color::grey(), [&nav](){ nav.push<NotImplementedView>(); } },
|
||||
{ "Whistle", ui::Color::grey(), [&nav](){ nav.push<NotImplementedView>(); } },
|
||||
{ "Whistle", ui::Color::yellow(), [&nav](){ nav.push<WhistleView>(); } },
|
||||
} });
|
||||
on_left = [&nav](){ nav.pop(); };
|
||||
}
|
||||
@ -310,7 +328,7 @@ UtilitiesView::UtilitiesView(NavigationView& nav) {
|
||||
|
||||
SystemMenuView::SystemMenuView(NavigationView& nav) {
|
||||
add_items<11>({ {
|
||||
{ "Play dead", ui::Color::red(), [&nav](){ nav.push<PlayDeadView>(false); } },
|
||||
{ "Play dead", ui::Color::red(), [&nav](){ nav.push<PlayDeadView>(); } },
|
||||
{ "Receivers", ui::Color::cyan(), [&nav](){ nav.push<ReceiverMenuView>(); } },
|
||||
{ "Capture", ui::Color::cyan(), [&nav](){ nav.push<CaptureAppView>(); } },
|
||||
{ "Code transmitters", ui::Color::green(), [&nav](){ nav.push<TransmitterCodedMenuView>(); } },
|
||||
@ -324,6 +342,8 @@ SystemMenuView::SystemMenuView(NavigationView& nav) {
|
||||
{ "HackRF mode", ui::Color::white(), [&nav](){ nav.push<HackRFFirmwareView>(); } },
|
||||
{ "About", ui::Color::white(), [&nav](){ nav.push<AboutView>(); } }
|
||||
} });
|
||||
|
||||
set_highlighted(1); // Startup selection is "Receivers"
|
||||
}
|
||||
|
||||
/* SystemView ************************************************************/
|
||||
@ -363,22 +383,16 @@ SystemView::SystemView(
|
||||
this->status_view.set_title(new_view.title());
|
||||
};
|
||||
|
||||
// Initial view.
|
||||
// TODO: Restore from non-volatile memory?
|
||||
|
||||
//if (persistent_memory::playing_dead() == 0x59)
|
||||
// navigation_view.push(new PlayDeadView { navigation_view, true });
|
||||
//else
|
||||
// navigation_view.push(new BMPView { navigation_view });
|
||||
|
||||
if (portapack::persistent_memory::ui_config() & 1)
|
||||
navigation_view.push<BMPView>();
|
||||
else
|
||||
//navigation_view.push<SoundBoardView>();
|
||||
//navigation_view.push<CloseCallView>();
|
||||
//navigation_view.push<HandWriteView>(debugtxt, 20);
|
||||
|
||||
navigation_view.push<SystemMenuView>();
|
||||
// Initial view
|
||||
if ((portapack::persistent_memory::playing_dead() == 0x5920C1DF) || // Enable code
|
||||
(portapack::persistent_memory::ui_config() & 16)) { // Login option
|
||||
navigation_view.push<PlayDeadView>();
|
||||
} else {
|
||||
if (portapack::persistent_memory::ui_config() & 1)
|
||||
navigation_view.push<BMPView>();
|
||||
else
|
||||
navigation_view.push<SystemMenuView>();
|
||||
}
|
||||
}
|
||||
|
||||
Context& SystemView::context() const {
|
||||
@ -419,45 +433,56 @@ BMPView::BMPView(NavigationView& nav) {
|
||||
&button_done
|
||||
} });
|
||||
|
||||
button_done.on_select = [this,&nav](Button&){
|
||||
button_done.on_select = [this, &nav](Button&){
|
||||
nav.pop();
|
||||
nav.push<SystemMenuView>();
|
||||
};
|
||||
}
|
||||
|
||||
void BMPView::paint(Painter& painter) {
|
||||
(void)painter;
|
||||
portapack::display.drawBMP({(240-185)/2, 0}, splash_bmp, false);
|
||||
void BMPView::paint(Painter&) {
|
||||
portapack::display.drawBMP({(240 - 185) / 2, 0}, splash_bmp, false);
|
||||
}
|
||||
|
||||
/* PlayDeadView **********************************************************/
|
||||
|
||||
void PlayDeadView::focus() {
|
||||
button_done.focus();
|
||||
button_seq_entry.focus();
|
||||
}
|
||||
|
||||
PlayDeadView::PlayDeadView(NavigationView& nav, bool booting) {
|
||||
_booting = booting;
|
||||
portapack::persistent_memory::set_playing_dead(0x59);
|
||||
void PlayDeadView::paint(Painter& painter) {
|
||||
if (!(portapack::persistent_memory::ui_config() & 16)) {
|
||||
// Blank the whole display
|
||||
painter.fill_rectangle(
|
||||
portapack::display.screen_rect(),
|
||||
style().background
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
PlayDeadView::PlayDeadView(NavigationView& nav) {
|
||||
portapack::persistent_memory::set_playing_dead(0x5920C1DF); // Enable
|
||||
|
||||
add_children({ {
|
||||
&text_playdead1,
|
||||
&text_playdead2,
|
||||
&button_done,
|
||||
&text_playdead3,
|
||||
&button_seq_entry,
|
||||
} });
|
||||
|
||||
button_done.on_dir = [this,&nav](Button&, KeyEvent key){
|
||||
sequence = (sequence<<3) | static_cast<std::underlying_type<KeyEvent>::type>(key);
|
||||
text_playdead3.hidden(true);
|
||||
|
||||
button_seq_entry.on_dir = [this](Button&, KeyEvent key){
|
||||
sequence = (sequence << 3) | static_cast<std::underlying_type<KeyEvent>::type>(key);
|
||||
return true;
|
||||
};
|
||||
|
||||
button_done.on_select = [this,&nav](Button&){
|
||||
button_seq_entry.on_select = [this, &nav](Button&){
|
||||
if (sequence == portapack::persistent_memory::playdead_sequence()) {
|
||||
portapack::persistent_memory::set_playing_dead(0);
|
||||
if (_booting) {
|
||||
nav.pop();
|
||||
nav.push<SystemMenuView>();
|
||||
portapack::persistent_memory::set_playing_dead(0x82175E23); // Disable
|
||||
if (!(portapack::persistent_memory::ui_config() & 16)) {
|
||||
text_playdead3.hidden(false);
|
||||
} else {
|
||||
nav.pop();
|
||||
nav.push<SystemMenuView>();
|
||||
}
|
||||
} else {
|
||||
sequence = 0;
|
||||
@ -487,6 +512,7 @@ void NotImplementedView::focus() {
|
||||
}
|
||||
|
||||
/* ModalMessageView ******************************************************/
|
||||
|
||||
ModalMessageView::ModalMessageView(
|
||||
NavigationView& nav,
|
||||
const std::string& title,
|
||||
@ -497,7 +523,6 @@ ModalMessageView::ModalMessageView(
|
||||
type_ { type },
|
||||
on_choice_ { on_choice }
|
||||
{
|
||||
|
||||
add_child(&text_message);
|
||||
|
||||
if (type == INFO) {
|
||||
@ -537,8 +562,7 @@ ModalMessageView::ModalMessageView(
|
||||
text_message.set(message);
|
||||
}
|
||||
|
||||
void ModalMessageView::paint(Painter& painter) {
|
||||
(void)painter;
|
||||
void ModalMessageView::paint(Painter&) {
|
||||
portapack::display.drawBMP({96, 64}, modal_warning_bmp, false);
|
||||
}
|
||||
|
||||
|
@ -61,31 +61,38 @@ private:
|
||||
//static constexpr auto back_text_disabled = " * ";
|
||||
|
||||
Button button_back {
|
||||
{ 0 * 8, 0 * 16, 20, 16 },
|
||||
{ 0 * 8, 0 * 16, 16, 16 },
|
||||
"", //back_text_disabled,
|
||||
};
|
||||
|
||||
Text title {
|
||||
{ 3 * 8, 0, 16 * 8, 1 * 16 },
|
||||
{ 20, 0, 16 * 8, 1 * 16 },
|
||||
default_title,
|
||||
};
|
||||
|
||||
ImageButton button_stealth {
|
||||
{ 152, 0, 2 * 8, 1 * 16 },
|
||||
&bitmap_stealth,
|
||||
Color::light_grey(),
|
||||
Color::black()
|
||||
};
|
||||
|
||||
ImageButton button_textentry {
|
||||
{ 164, 0, 2 * 8, 1 * 16 },
|
||||
{ 170, 0, 2 * 8, 1 * 16 },
|
||||
&bitmap_unistroke,
|
||||
Color::white(),
|
||||
Color::black()
|
||||
};
|
||||
|
||||
ImageButton button_camera {
|
||||
{ 184, 0, 2 * 8, 1 * 16 },
|
||||
{ 188, 0, 2 * 8, 1 * 16 },
|
||||
&bitmap_camera,
|
||||
Color::white(),
|
||||
Color::black()
|
||||
};
|
||||
|
||||
ImageButton button_sleep {
|
||||
{ 204, 0, 2 * 8, 1 * 16 },
|
||||
{ 206, 0, 2 * 8, 1 * 16 },
|
||||
&bitmap_sleep,
|
||||
Color::white(),
|
||||
Color::black()
|
||||
@ -95,8 +102,9 @@ private:
|
||||
{ 28 * 8, 0 * 16, 2 * 8, 1 * 16 }
|
||||
};
|
||||
|
||||
void on_camera();
|
||||
void on_stealth();
|
||||
void on_textentry();
|
||||
void on_camera();
|
||||
};
|
||||
|
||||
class NavigationView : public View {
|
||||
@ -144,7 +152,7 @@ public:
|
||||
class BMPView : public View {
|
||||
public:
|
||||
BMPView(NavigationView& nav);
|
||||
void paint(Painter& painter) override;
|
||||
void paint(Painter&) override;
|
||||
void focus() override;
|
||||
|
||||
private:
|
||||
@ -161,12 +169,14 @@ private:
|
||||
|
||||
class PlayDeadView : public View {
|
||||
public:
|
||||
PlayDeadView(NavigationView& nav, bool booting);
|
||||
PlayDeadView(NavigationView& nav);
|
||||
|
||||
void focus() override;
|
||||
void paint(Painter& painter) override;
|
||||
|
||||
private:
|
||||
bool _booting;
|
||||
uint32_t sequence = 0;
|
||||
|
||||
Text text_playdead1 {
|
||||
{ 6 * 8, 7 * 16, 14 * 8, 16 },
|
||||
"Firmware error"
|
||||
@ -175,8 +185,12 @@ private:
|
||||
{ 6 * 8, 9 * 16, 16 * 8, 16 },
|
||||
"0x1400_0000 : 2C"
|
||||
};
|
||||
Text text_playdead3 {
|
||||
{ 6 * 8, 12 * 16, 16 * 8, 16 },
|
||||
"Please reset"
|
||||
};
|
||||
|
||||
Button button_done {
|
||||
Button button_seq_entry {
|
||||
{ 240, 0, 1, 1 },
|
||||
""
|
||||
};
|
||||
@ -296,11 +310,11 @@ public:
|
||||
const modal_t type,
|
||||
const std::function<void(bool)> on_choice
|
||||
);
|
||||
|
||||
void paint(Painter& painter) override;
|
||||
|
||||
void focus() override;
|
||||
|
||||
// std::string title() const override { return title_; };
|
||||
std::string title() const override { return title_; };
|
||||
|
||||
private:
|
||||
const std::string title_;
|
||||
|
@ -23,10 +23,8 @@
|
||||
#include "ui_nuoptix.hpp"
|
||||
|
||||
#include "ch.h"
|
||||
|
||||
#include "lfsr_random.hpp"
|
||||
#include "ui_alphanum.hpp"
|
||||
#include "portapack.hpp"
|
||||
#include "lfsr_random.hpp"
|
||||
#include "string_format.hpp"
|
||||
|
||||
#include "portapack_shared_memory.hpp"
|
||||
@ -39,7 +37,12 @@ using namespace portapack;
|
||||
namespace ui {
|
||||
|
||||
void NuoptixView::focus() {
|
||||
number_timecode.focus();
|
||||
button_tx.focus();
|
||||
}
|
||||
|
||||
NuoptixView::~NuoptixView() {
|
||||
transmitter_model.disable();
|
||||
baseband::shutdown();
|
||||
}
|
||||
|
||||
void NuoptixView::on_tuning_frequency_changed(rf::Frequency f) {
|
||||
@ -47,8 +50,9 @@ void NuoptixView::on_tuning_frequency_changed(rf::Frequency f) {
|
||||
}
|
||||
|
||||
void NuoptixView::transmit(bool setup) {
|
||||
uint8_t mod;
|
||||
uint8_t mod, tone_code;
|
||||
uint8_t c;
|
||||
uint8_t dtmf_message[6];
|
||||
|
||||
if (!tx_mode) {
|
||||
transmitter_model.disable();
|
||||
@ -56,7 +60,7 @@ void NuoptixView::transmit(bool setup) {
|
||||
}
|
||||
|
||||
if (tx_mode == IMPROVISE)
|
||||
timecode = lfsr_iterate(timecode) % 1999; // Should be 9999 but that would be one long audio track !
|
||||
timecode = lfsr_iterate(timecode) % 1999; // Could be 9999 but that would be one long audio track !
|
||||
|
||||
if (setup) {
|
||||
pbar.set_max(4);
|
||||
@ -64,7 +68,7 @@ void NuoptixView::transmit(bool setup) {
|
||||
if (tx_mode == NORMAL)
|
||||
timecode = number_timecode.value();
|
||||
else
|
||||
timecode = 0125;
|
||||
timecode = 0125; // TODO: Use RTC as seed ?
|
||||
|
||||
transmitter_model.set_baseband_configuration({
|
||||
.mode = 0,
|
||||
@ -77,35 +81,63 @@ void NuoptixView::transmit(bool setup) {
|
||||
transmitter_model.set_baseband_bandwidth(1750000);
|
||||
transmitter_model.enable();
|
||||
|
||||
shared_memory.tx_data[0] = '*'; // "Pre-tone for restart" method #1
|
||||
shared_memory.tx_data[1] = 'A'; // "Restart" method #1
|
||||
dtmf_message[0] = '*'; // "Pre-tone for restart" method #1
|
||||
dtmf_message[1] = 'A'; // "Restart" method #1
|
||||
} else {
|
||||
shared_memory.tx_data[0] = '#';
|
||||
shared_memory.tx_data[1] = (timecode / 1000) % 10;
|
||||
dtmf_message[0] = '#';
|
||||
dtmf_message[1] = (timecode / 1000) % 10;
|
||||
chThdSleepMilliseconds(92); // 141-49ms
|
||||
number_timecode.set_value(timecode);
|
||||
}
|
||||
|
||||
pbar.set_value(0);
|
||||
|
||||
shared_memory.tx_data[2] = (timecode / 100) % 10;
|
||||
shared_memory.tx_data[3] = (timecode / 10) % 10;
|
||||
shared_memory.tx_data[4] = timecode % 10;
|
||||
dtmf_message[2] = (timecode / 100) % 10;
|
||||
dtmf_message[3] = (timecode / 10) % 10;
|
||||
dtmf_message[4] = timecode % 10;
|
||||
|
||||
mod = 0;
|
||||
for (c = 1; c < 5; c++)
|
||||
if (shared_memory.tx_data[c] <= 9)
|
||||
mod += shared_memory.tx_data[c];
|
||||
if (dtmf_message[c] <= 9)
|
||||
mod += dtmf_message[c];
|
||||
|
||||
mod = 10 - (mod % 10);
|
||||
if (mod == 10) mod = 0; // Is this right ?
|
||||
|
||||
text_mod.set("Mod: " + to_string_dec_uint(mod));
|
||||
|
||||
shared_memory.tx_data[5] = mod;
|
||||
dtmf_message[5] = mod;
|
||||
|
||||
shared_memory.tx_data[6] = 0xFF; // End of message
|
||||
for (c = 0; c < 6; c++) {
|
||||
tone_code = dtmf_message[c];
|
||||
|
||||
if (tone_code == 'A')
|
||||
tone_code = 10;
|
||||
else if (tone_code == 'B')
|
||||
tone_code = 11;
|
||||
else if (tone_code == 'C')
|
||||
tone_code = 12;
|
||||
else if (tone_code == 'D')
|
||||
tone_code = 13;
|
||||
else if (tone_code == '#')
|
||||
tone_code = 14;
|
||||
else if (tone_code == '*')
|
||||
tone_code = 15;
|
||||
|
||||
shared_memory.bb_data.tones_data.message[c * 2] = tone_code;
|
||||
shared_memory.bb_data.tones_data.message[c * 2 + 1] = 0xFF; // Silence
|
||||
}
|
||||
|
||||
baseband::set_dtmf_data(number_bw.value(), 49, 49); // 49ms tone, 49ms space
|
||||
for (c = 0; c < 16; c++) {
|
||||
shared_memory.bb_data.tones_data.tone_defs[c * 2].delta = dtmf_deltas[c][0];
|
||||
shared_memory.bb_data.tones_data.tone_defs[c * 2].duration = NUOPTIX_TONE_LENGTH;
|
||||
shared_memory.bb_data.tones_data.tone_defs[c * 2 + 1].delta = dtmf_deltas[c][1];
|
||||
shared_memory.bb_data.tones_data.tone_defs[c * 2 + 1].duration = NUOPTIX_TONE_LENGTH;
|
||||
}
|
||||
shared_memory.bb_data.tones_data.silence = NUOPTIX_TONE_LENGTH; // 49ms tone, 49ms space
|
||||
|
||||
audio::set_rate(audio::Rate::Hz_24000);
|
||||
baseband::set_tones_data(number_bw.value(), 0, 6 * 2, true, true);
|
||||
|
||||
timecode++;
|
||||
}
|
||||
@ -114,7 +146,7 @@ NuoptixView::NuoptixView(
|
||||
NavigationView& nav
|
||||
)
|
||||
{
|
||||
baseband::run_image(portapack::spi_flash::image_tag_dtmf_tx);
|
||||
baseband::run_image(portapack::spi_flash::image_tag_tones);
|
||||
|
||||
add_children({ {
|
||||
&field_frequency,
|
||||
@ -129,7 +161,6 @@ NuoptixView::NuoptixView(
|
||||
&button_exit
|
||||
} });
|
||||
|
||||
//check_loop.set_value(false);
|
||||
number_bw.set_value(15);
|
||||
number_timecode.set_value(1);
|
||||
|
||||
@ -174,9 +205,4 @@ NuoptixView::NuoptixView(
|
||||
};
|
||||
}
|
||||
|
||||
NuoptixView::~NuoptixView() {
|
||||
transmitter_model.disable();
|
||||
baseband::shutdown();
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -30,6 +30,20 @@
|
||||
#include "ui_navigation.hpp"
|
||||
#include "ui_receiver.hpp"
|
||||
#include "message.hpp"
|
||||
#include "volume.hpp"
|
||||
#include "audio.hpp"
|
||||
|
||||
#define DTMF_DELTA_COEF (43.691) // (65536*1024)/1536000
|
||||
#define DTMF_C0 (uint32_t)(1209 * DTMF_DELTA_COEF)
|
||||
#define DTMF_C1 (uint32_t)(1336 * DTMF_DELTA_COEF)
|
||||
#define DTMF_C2 (uint32_t)(1477 * DTMF_DELTA_COEF)
|
||||
#define DTMF_C3 (uint32_t)(1633 * DTMF_DELTA_COEF)
|
||||
#define DTMF_R0 (uint32_t)(697 * DTMF_DELTA_COEF)
|
||||
#define DTMF_R1 (uint32_t)(770 * DTMF_DELTA_COEF)
|
||||
#define DTMF_R2 (uint32_t)(852 * DTMF_DELTA_COEF)
|
||||
#define DTMF_R3 (uint32_t)(941 * DTMF_DELTA_COEF)
|
||||
|
||||
#define NUOPTIX_TONE_LENGTH 75264 // 1536000*0.049
|
||||
|
||||
namespace ui {
|
||||
|
||||
@ -50,7 +64,27 @@ private:
|
||||
};
|
||||
|
||||
tx_modes tx_mode = IDLE;
|
||||
|
||||
|
||||
// 0123456789ABCD#*
|
||||
const uint32_t dtmf_deltas[16][2] = {
|
||||
{ DTMF_C1, DTMF_R3 },
|
||||
{ DTMF_C0, DTMF_R0 },
|
||||
{ DTMF_C1, DTMF_R0 },
|
||||
{ DTMF_C2, DTMF_R0 },
|
||||
{ DTMF_C0, DTMF_R1 },
|
||||
{ DTMF_C1, DTMF_R1 },
|
||||
{ DTMF_C2, DTMF_R1 },
|
||||
{ DTMF_C0, DTMF_R2 },
|
||||
{ DTMF_C1, DTMF_R2 },
|
||||
{ DTMF_C2, DTMF_R2 },
|
||||
{ DTMF_C3, DTMF_R0 },
|
||||
{ DTMF_C3, DTMF_R1 },
|
||||
{ DTMF_C3, DTMF_R2 },
|
||||
{ DTMF_C3, DTMF_R3 },
|
||||
{ DTMF_C2, DTMF_R3 },
|
||||
{ DTMF_C0, DTMF_R3 }
|
||||
};
|
||||
|
||||
void on_tuning_frequency_changed(rf::Frequency f);
|
||||
void transmit(bool setup);
|
||||
|
||||
@ -95,24 +129,18 @@ private:
|
||||
{ 16, 236, 208, 16 }
|
||||
};
|
||||
|
||||
/*Checkbox check_loop {
|
||||
{ 16, 274 },
|
||||
4,
|
||||
"Loop"
|
||||
};*/
|
||||
|
||||
Button button_tx {
|
||||
{ 70, 128, 100, 40 },
|
||||
{ 64, 128, 112, 40 },
|
||||
"TX"
|
||||
};
|
||||
|
||||
Button button_impro {
|
||||
{ 70, 184, 100, 40 },
|
||||
{ 64, 184, 112, 40 },
|
||||
"IMPROVISE"
|
||||
};
|
||||
|
||||
Button button_exit {
|
||||
{ 160, 270, 64, 32 },
|
||||
{ 88, 270, 64, 32 },
|
||||
"Exit"
|
||||
};
|
||||
|
||||
@ -120,10 +148,10 @@ private:
|
||||
Message::ID::TXDone,
|
||||
[this](const Message* const p) {
|
||||
const auto message = *reinterpret_cast<const TXDoneMessage*>(p);
|
||||
if (message.n == 0xFF)
|
||||
if (message.progress == 0xFF)
|
||||
transmit(false);
|
||||
else
|
||||
pbar.set_value(message.n);
|
||||
pbar.set_value(message.progress);
|
||||
}
|
||||
};
|
||||
};
|
||||
|
@ -62,8 +62,8 @@ void RDSView::start_tx() {
|
||||
}
|
||||
|
||||
void RDSView::paint(Painter& painter) {
|
||||
char RadioTextA[17];
|
||||
(void)painter;
|
||||
char RadioTextA[17];
|
||||
|
||||
text_psn.set(PSN);
|
||||
|
||||
@ -81,25 +81,29 @@ RDSView::RDSView(NavigationView& nav) {
|
||||
strcpy(PSN, "TEST1234");
|
||||
strcpy(RadioText, "Radiotext test ABCD1234");
|
||||
|
||||
rds_flags.DI = false;
|
||||
rds_flags.MS = false;
|
||||
rds_flags.PI_code = 0x1337;
|
||||
rds_flags.TA = false;
|
||||
rds_flags.TP = true;
|
||||
|
||||
add_children({ {
|
||||
&field_frequency,
|
||||
&text_pty,
|
||||
&options_pty,
|
||||
&text_countrycode,
|
||||
&options_countrycode,
|
||||
&text_coverage,
|
||||
&options_coverage,
|
||||
&text_tx,
|
||||
&options_tx,
|
||||
&check_mono_stereo,
|
||||
&check_TA,
|
||||
&check_TP,
|
||||
&check_MS,
|
||||
&text_pi_code,
|
||||
&sym_pi_code,
|
||||
&button_editpsn,
|
||||
&text_psn,
|
||||
&button_txpsn,
|
||||
&button_editradiotext,
|
||||
&text_radiotext,
|
||||
&text_radiotexta,
|
||||
&text_radiotextb,
|
||||
&button_txradiotext,
|
||||
&button_tx,
|
||||
&button_exit
|
||||
} });
|
||||
|
||||
@ -116,27 +120,72 @@ RDSView::RDSView(NavigationView& nav) {
|
||||
};
|
||||
};
|
||||
|
||||
options_countrycode.set_selected_index(18); // France
|
||||
options_coverage.set_selected_index(0); // Local
|
||||
check_TA.set_value(true);
|
||||
check_TP.set_value(true);
|
||||
|
||||
sym_pi_code.set_value(0, 0xF);
|
||||
sym_pi_code.set_value(1, 0x3);
|
||||
sym_pi_code.set_value(2, 0xE);
|
||||
sym_pi_code.set_value(3, 0x0);
|
||||
sym_pi_code.on_change = [this]() {
|
||||
rds_flags.PI_code = sym_pi_code.value_hex_u64();
|
||||
};
|
||||
|
||||
options_pty.on_change = [this](size_t, int32_t v) {
|
||||
rds_flags.PTY = v;
|
||||
};
|
||||
|
||||
options_pty.set_selected_index(0); // None
|
||||
|
||||
options_countrycode.on_change = [this](size_t, int32_t) {
|
||||
//rds_flags.PTY = v;
|
||||
};
|
||||
options_countrycode.set_selected_index(18); // Baguette du fromage
|
||||
|
||||
options_coverage.on_change = [this](size_t, int32_t) {
|
||||
//rds_flags.PTY = v;
|
||||
};
|
||||
options_coverage.set_selected_index(0); // Local
|
||||
|
||||
<<<<<<< HEAD
|
||||
button_editpsn.on_select = [this, &nav](Button&) {
|
||||
textentry(nav, PSN, 8);
|
||||
};
|
||||
button_tx.on_select = [this](Button&) {
|
||||
=======
|
||||
options_pty.on_change = [this](size_t, int32_t v) {
|
||||
rds_flags.PTY = v;
|
||||
};
|
||||
|
||||
button_editpsn.on_select = [this,&nav](Button&) {
|
||||
textentry(nav, PSN, 8);
|
||||
};
|
||||
button_txpsn.on_select = [this](Button&) {
|
||||
>>>>>>> d402a87... RDS radiotext and time group generators
|
||||
if (txing) {
|
||||
button_txpsn.set_text("PSN");
|
||||
button_txradiotext.set_text("Radiotext");
|
||||
transmitter_model.disable();
|
||||
button_tx.set_text("START");
|
||||
txing = false;
|
||||
} else {
|
||||
<<<<<<< HEAD
|
||||
rds_flags.PI_code = sym_pi_code.value_hex_u64();
|
||||
rds_flags.PTY = options_pty.selected_index_value();
|
||||
rds_flags.DI = check_mono_stereo.value() ? 1 : 0;
|
||||
rds_flags.TP = check_TP.value();
|
||||
rds_flags.TA = check_TA.value();
|
||||
rds_flags.MS = check_MS.value();
|
||||
|
||||
if (options_tx.selected_index() == 0)
|
||||
message_length = gen_PSN(PSN, &rds_flags);
|
||||
else if (options_tx.selected_index() == 1)
|
||||
message_length = gen_RadioText(RadioText, 0, &rds_flags);
|
||||
else
|
||||
message_length = gen_ClockTime(&rds_flags, 2016, 12, 1, 9, 23, 2);
|
||||
|
||||
button_tx.set_text("STOP");
|
||||
=======
|
||||
message_length = gen_PSN(PSN, &rds_flags);
|
||||
button_txpsn.set_text("STOP");
|
||||
>>>>>>> d402a87... RDS radiotext and time group generators
|
||||
txing = true;
|
||||
start_tx();
|
||||
}
|
||||
@ -145,6 +194,8 @@ RDSView::RDSView(NavigationView& nav) {
|
||||
button_editradiotext.on_select = [this, &nav](Button&){
|
||||
textentry(nav, RadioText, 24);
|
||||
};
|
||||
<<<<<<< HEAD
|
||||
=======
|
||||
button_txradiotext.on_select = [this](Button&){
|
||||
if (txing) {
|
||||
button_txpsn.set_text("PSN");
|
||||
@ -158,6 +209,7 @@ RDSView::RDSView(NavigationView& nav) {
|
||||
start_tx();
|
||||
}
|
||||
};
|
||||
>>>>>>> d402a87... RDS radiotext and time group generators
|
||||
|
||||
button_exit.on_select = [&nav](Button&){
|
||||
nav.pop();
|
||||
|
@ -55,11 +55,15 @@ private:
|
||||
void on_tuning_frequency_changed(rf::Frequency f);
|
||||
|
||||
FrequencyField field_frequency {
|
||||
{ 1 * 8, 1 * 16 },
|
||||
{ 1 * 8, 4 },
|
||||
};
|
||||
|
||||
Text text_pty {
|
||||
{ 1 * 8, 16 + 8, 4 * 8, 16 },
|
||||
"PTY:"
|
||||
};
|
||||
OptionsField options_pty {
|
||||
{ 1 * 8, 2 * 16 },
|
||||
{ 5 * 8, 16 + 8 },
|
||||
8,
|
||||
{
|
||||
{ "None", 0 },
|
||||
@ -97,8 +101,12 @@ private:
|
||||
}
|
||||
};
|
||||
|
||||
Text text_countrycode {
|
||||
{ 14 * 8, 16 + 8, 4 * 8, 16 },
|
||||
"CC:"
|
||||
};
|
||||
OptionsField options_countrycode {
|
||||
{ 1 * 8, 3 * 16 },
|
||||
{ 17 * 8, 16 + 8 },
|
||||
11,
|
||||
{
|
||||
{ "Albania", 9 },
|
||||
@ -166,12 +174,26 @@ private:
|
||||
}
|
||||
};
|
||||
|
||||
Text text_pi_code {
|
||||
{ 1 * 8, 32 + 8, 3 * 8, 16 },
|
||||
"PI:"
|
||||
};
|
||||
SymField sym_pi_code {
|
||||
{ 4 * 8, 32 + 8 },
|
||||
4,
|
||||
true
|
||||
};
|
||||
|
||||
Text text_coverage {
|
||||
{ 13 * 8, 32 + 8, 9 * 8, 16 },
|
||||
"Cov:"
|
||||
};
|
||||
OptionsField options_coverage {
|
||||
{ 1 * 8, 4 * 16 },
|
||||
13,
|
||||
{ 17 * 8, 32 + 8 },
|
||||
12,
|
||||
{
|
||||
{ "Local", 0 },
|
||||
{ "International", 1 },
|
||||
{ "Internat.", 1 },
|
||||
{ "National", 2 },
|
||||
{ "Sup-regional", 3 },
|
||||
{ "R11", 4 },
|
||||
@ -189,43 +211,73 @@ private:
|
||||
}
|
||||
};
|
||||
|
||||
Text text_tx {
|
||||
{ 19 * 8, 4 * 16, 9 * 8, 16 },
|
||||
"Transmit:"
|
||||
Checkbox check_mono_stereo {
|
||||
{ 1 * 8, 4 * 16 },
|
||||
6,
|
||||
"Stereo"
|
||||
};
|
||||
Checkbox check_TA {
|
||||
{ 12 * 8, 4 * 16 },
|
||||
2,
|
||||
"TA"
|
||||
};
|
||||
Checkbox check_TP {
|
||||
{ 18 * 8, 4 * 16 },
|
||||
2,
|
||||
"TP"
|
||||
};
|
||||
Checkbox check_MS {
|
||||
{ 24 * 8, 4 * 16 },
|
||||
2,
|
||||
"MS"
|
||||
};
|
||||
|
||||
Button button_editpsn {
|
||||
{ 2 * 8, 6 * 16, 64, 24 },
|
||||
"Set"
|
||||
};
|
||||
Text text_psn {
|
||||
{ 2 * 8, 15 * 8, 8 * 8, 16 },
|
||||
"TEST1234"
|
||||
{ 2 * 8, 6 * 16, 12 * 8, 16 },
|
||||
"PSN:"
|
||||
};
|
||||
Button button_txpsn {
|
||||
{ 19 * 8, 6 * 16, 10 * 8, 32 },
|
||||
"PSN"
|
||||
Button button_editpsn {
|
||||
{ 22 * 8, 5 * 16 + 12, 48, 24 },
|
||||
"Set"
|
||||
};
|
||||
|
||||
Button button_editradiotext {
|
||||
{ 2 * 8, 9 * 16, 64, 24 },
|
||||
"Set"
|
||||
Text text_radiotext {
|
||||
{ 2 * 8, 8 * 16, 10 * 8, 16 },
|
||||
"RadioText:"
|
||||
};
|
||||
Text text_radiotexta {
|
||||
{ 2 * 8, 21 * 8, 16 * 8, 16 },
|
||||
"Radiotext test !"
|
||||
{ 2 * 8, 9 * 16, 19 * 8, 16 },
|
||||
"-"
|
||||
};
|
||||
Text text_radiotextb {
|
||||
{ 2 * 8, 23 * 8, 16 * 8, 16 },
|
||||
"--------"
|
||||
{ 2 * 8, 10 * 16, 19 * 8, 16 },
|
||||
"-"
|
||||
};
|
||||
Button button_txradiotext {
|
||||
{ 19 * 8, 9 * 16, 10 * 8, 32 },
|
||||
"Radiotext"
|
||||
Button button_editradiotext {
|
||||
{ 22 * 8, 8 * 16 + 12, 48, 24 },
|
||||
"Set"
|
||||
};
|
||||
|
||||
Text text_tx {
|
||||
{ 2 * 8, 14 * 16, 3 * 8, 16 },
|
||||
"TX:"
|
||||
};
|
||||
OptionsField options_tx {
|
||||
{ 5 * 8, 14 * 16 },
|
||||
11,
|
||||
{
|
||||
{ "PSN", 0 },
|
||||
{ "RadioText", 1 },
|
||||
{ "Date & time", 2 }
|
||||
}
|
||||
};
|
||||
|
||||
Button button_tx {
|
||||
{ 3 * 8, 16 * 16, 80, 32 },
|
||||
"START"
|
||||
};
|
||||
Button button_exit {
|
||||
{ 72, 260, 96, 32 },
|
||||
{ 19 * 8, 16 * 16, 64, 32 },
|
||||
"Exit"
|
||||
};
|
||||
};
|
||||
|
@ -150,12 +150,16 @@ FrequencyKeypadView::FrequencyKeypadView(
|
||||
|
||||
add_children({ {
|
||||
&button_save,
|
||||
&button_load,
|
||||
&button_close
|
||||
} });
|
||||
|
||||
button_save.on_select = [this, &nav](Button&) {
|
||||
nav.push<FrequencySaveView>(this->value());
|
||||
};
|
||||
button_load.on_select = [this, &nav](Button&) {
|
||||
nav.push<FrequencyLoadView>(this->value());
|
||||
};
|
||||
|
||||
button_close.on_select = [this, &nav](Button&) {
|
||||
if( on_changed ) {
|
||||
|
@ -204,11 +204,15 @@ private:
|
||||
std::array<Button, 12> buttons;
|
||||
|
||||
Button button_save {
|
||||
{ 0, button_h * 4 + button_h, button_w, button_h },
|
||||
{ 0, button_h * 5, 60, button_h },
|
||||
"Save"
|
||||
};
|
||||
Button button_load {
|
||||
{ 60, button_h * 5, 60, button_h },
|
||||
"Load"
|
||||
};
|
||||
Button button_close {
|
||||
{ button_w, button_h * 4 + button_h, button_w * 2, button_h },
|
||||
{ 128, button_h * 5, 112, button_h },
|
||||
"Done"
|
||||
};
|
||||
|
||||
|
@ -1,5 +1,6 @@
|
||||
/*
|
||||
* Copyright (C) 2015 Jared Boone, ShareBrained Technology, Inc.
|
||||
* Copyright (C) 2016 Furrtek
|
||||
*
|
||||
* This file is part of PortaPack.
|
||||
*
|
||||
|
@ -1,5 +1,6 @@
|
||||
/*
|
||||
* Copyright (C) 2015 Jared Boone, ShareBrained Technology, Inc.
|
||||
* Copyright (C) 2016 Furrtek
|
||||
*
|
||||
* This file is part of PortaPack.
|
||||
*
|
||||
|
@ -1,5 +1,6 @@
|
||||
/*
|
||||
* Copyright (C) 2015 Jared Boone, ShareBrained Technology, Inc.
|
||||
* Copyright (C) 2016 Furrtek
|
||||
*
|
||||
* This file is part of PortaPack.
|
||||
*
|
||||
@ -180,22 +181,24 @@ SetPlayDeadView::SetPlayDeadView(NavigationView& nav) {
|
||||
&text_sequence,
|
||||
&button_enter,
|
||||
&button_cancel
|
||||
}});
|
||||
}});
|
||||
|
||||
button_enter.on_select = [this,&nav](Button&){
|
||||
if (entermode == false) {
|
||||
button_enter.on_select = [this, &nav](Button&){
|
||||
if (!entermode) {
|
||||
sequence = 0;
|
||||
memset(sequence_txt,0,11);
|
||||
text_sequence.set("");
|
||||
keycount = 0;
|
||||
memset(sequence_txt, '-', 10);
|
||||
text_sequence.set(sequence_txt);
|
||||
entermode = true;
|
||||
button_cancel.hidden(true);
|
||||
set_dirty();
|
||||
} else {
|
||||
persistent_memory::set_playdead_sequence(sequence);
|
||||
nav.pop();
|
||||
}
|
||||
};
|
||||
button_enter.on_dir = [this,&nav](Button&, KeyEvent key){
|
||||
|
||||
button_enter.on_dir = [this](Button&, KeyEvent key){
|
||||
if ((entermode == true) && (keycount < 10)) {
|
||||
key_code = static_cast<std::underlying_type<KeyEvent>::type>(key);
|
||||
if (key_code == 0)
|
||||
@ -207,25 +210,28 @@ SetPlayDeadView::SetPlayDeadView(NavigationView& nav) {
|
||||
else if (key_code == 3)
|
||||
sequence_txt[keycount] = 'U';
|
||||
text_sequence.set(sequence_txt);
|
||||
sequence = (sequence<<3) | key_code;
|
||||
sequence = (sequence << 3) | key_code;
|
||||
keycount++;
|
||||
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
};
|
||||
|
||||
button_cancel.on_select = [&nav](Button&){ nav.pop(); };
|
||||
}
|
||||
|
||||
void SetPlayDeadView::focus() {
|
||||
button_enter.focus();
|
||||
button_cancel.focus();
|
||||
}
|
||||
|
||||
SetUIView::SetUIView(NavigationView& nav) {
|
||||
uint32_t ui_config;
|
||||
|
||||
add_children({{
|
||||
&checkbox_showsplash,
|
||||
&checkbox_login,
|
||||
&checkbox_bloff,
|
||||
&options_bloff,
|
||||
&checkbox_showsplash,
|
||||
&button_ok
|
||||
}});
|
||||
|
||||
@ -233,14 +239,18 @@ SetUIView::SetUIView(NavigationView& nav) {
|
||||
|
||||
if (ui_config & 1) checkbox_showsplash.set_value(true);
|
||||
if (ui_config & 2) checkbox_bloff.set_value(true);
|
||||
if (ui_config & 16) checkbox_login.set_value(true);
|
||||
options_bloff.set_selected_index((ui_config >> 5) & 7);
|
||||
|
||||
button_ok.on_select = [&nav,this](Button&){
|
||||
uint32_t ui_config = 0;
|
||||
if (checkbox_showsplash.value() == true) ui_config |= 1;
|
||||
if (checkbox_bloff.value() == true) ui_config |= 2;
|
||||
ui_config |= (portapack::persistent_memory::ui_config_textentry() << 2);
|
||||
ui_config |= (options_bloff.selected_index() << 5);
|
||||
button_ok.on_select = [&nav, &ui_config, this](Button&) {
|
||||
ui_config &= ~0b10011;
|
||||
|
||||
if (checkbox_login.value()) {
|
||||
portapack::persistent_memory::set_playing_dead(0x5920C1DF); // Enable
|
||||
ui_config |= (1 << 4);
|
||||
}
|
||||
if (checkbox_showsplash.value()) ui_config |= (1 << 0);
|
||||
if (checkbox_bloff.value()) ui_config |= (1 << 1);
|
||||
|
||||
portapack::persistent_memory::set_ui_config(ui_config);
|
||||
nav.pop();
|
||||
@ -248,7 +258,7 @@ SetUIView::SetUIView(NavigationView& nav) {
|
||||
}
|
||||
|
||||
void SetUIView::focus() {
|
||||
checkbox_showsplash.focus();
|
||||
checkbox_login.focus();
|
||||
}
|
||||
|
||||
/*void ModInfoView::on_show() {
|
||||
|
@ -1,5 +1,6 @@
|
||||
/*
|
||||
* Copyright (C) 2015 Jared Boone, ShareBrained Technology, Inc.
|
||||
* Copyright (C) 2016 Furrtek
|
||||
*
|
||||
* This file is part of PortaPack.
|
||||
*
|
||||
@ -253,20 +254,20 @@ public:
|
||||
void focus() override;
|
||||
|
||||
private:
|
||||
Checkbox checkbox_showsplash {
|
||||
{ 3 * 8, 2 * 16},
|
||||
11,
|
||||
"Show splash"
|
||||
Checkbox checkbox_login {
|
||||
{ 3 * 8, 2 * 16 },
|
||||
20,
|
||||
"Login with play dead"
|
||||
};
|
||||
|
||||
Checkbox checkbox_bloff {
|
||||
{ 3 * 8, 4 * 16},
|
||||
{ 3 * 8, 5 * 16 },
|
||||
20,
|
||||
"Backlight off after:"
|
||||
};
|
||||
|
||||
OptionsField options_bloff {
|
||||
{ 10 * 8, 5 * 16 + 4 },
|
||||
{ 10 * 8, 6 * 16 + 4 },
|
||||
10,
|
||||
{
|
||||
{ "5 seconds", 0 },
|
||||
@ -277,6 +278,12 @@ private:
|
||||
}
|
||||
};
|
||||
|
||||
Checkbox checkbox_showsplash {
|
||||
{ 3 * 8, 8 * 16 },
|
||||
11,
|
||||
"Show splash"
|
||||
};
|
||||
|
||||
Button button_ok {
|
||||
{ 72, 260, 96, 32 },
|
||||
"OK"
|
||||
@ -286,12 +293,14 @@ private:
|
||||
class SetPlayDeadView : public View {
|
||||
public:
|
||||
SetPlayDeadView(NavigationView& nav);
|
||||
|
||||
void focus() override;
|
||||
|
||||
private:
|
||||
bool entermode = false;
|
||||
uint32_t sequence = 0;
|
||||
uint8_t keycount, key_code;
|
||||
char sequence_txt[11];
|
||||
char sequence_txt[11] = { 0 };
|
||||
|
||||
Text text_sequence {
|
||||
{ 64, 32, 14 * 8, 16 },
|
||||
|
@ -271,6 +271,7 @@ SoundBoardView::SoundBoardView(
|
||||
|
||||
const auto button_dir = [this](Button& button, const KeyEvent key) {
|
||||
this->change_page(button, key);
|
||||
return false;
|
||||
};
|
||||
|
||||
size_t n = 0;
|
||||
|
@ -152,7 +152,7 @@ private:
|
||||
};
|
||||
|
||||
Checkbox check_loop {
|
||||
{ 16, 274 },
|
||||
{ 8, 274 },
|
||||
4,
|
||||
"Loop"
|
||||
};
|
||||
|
@ -24,7 +24,7 @@
|
||||
|
||||
namespace ui {
|
||||
|
||||
bool textentry(NavigationView& nav, char * str, uint16_t max_length) {
|
||||
bool textentry(NavigationView& nav, char * str, size_t max_length) {
|
||||
if (portapack::persistent_memory::ui_config_textentry() == 0) {
|
||||
auto an_view = nav.push<AlphanumView>(str, max_length);
|
||||
an_view->on_changed = [str, max_length](char * value) {
|
||||
|
@ -28,6 +28,6 @@
|
||||
|
||||
namespace ui {
|
||||
|
||||
bool textentry(NavigationView& nav, char * str, uint16_t max_length);
|
||||
bool textentry(NavigationView& nav, char * str, size_t max_length);
|
||||
|
||||
} /* namespace ui */
|
||||
|
@ -1,5 +1,6 @@
|
||||
/*
|
||||
* Copyright (C) 2015 Jared Boone, ShareBrained Technology, Inc.
|
||||
* Copyright (C) 2016 Furrtek
|
||||
*
|
||||
* This file is part of PortaPack.
|
||||
*
|
||||
@ -22,14 +23,7 @@
|
||||
#include "ui_whistle.hpp"
|
||||
#include "ui_receiver.hpp"
|
||||
|
||||
#include "ch.h"
|
||||
#include "evtimer.h"
|
||||
|
||||
#include "ff.h"
|
||||
#include "hackrf_gpio.hpp"
|
||||
#include "portapack.hpp"
|
||||
#include "radio.hpp"
|
||||
|
||||
#include "hackrf_hal.hpp"
|
||||
#include "portapack_shared_memory.hpp"
|
||||
#include "portapack_persistent_memory.hpp"
|
||||
@ -46,41 +40,28 @@ void WhistleView::focus() {
|
||||
}
|
||||
|
||||
WhistleView::~WhistleView() {
|
||||
transmitter_model.disable();
|
||||
//transmitter_model.disable();
|
||||
}
|
||||
|
||||
void WhistleView::paint(Painter& painter) {
|
||||
(void)painter;
|
||||
}
|
||||
|
||||
void WhistleView::whistle_th(void *arg) {
|
||||
Mailbox* mbox = (Mailbox *)arg;
|
||||
chMBPost(mbox, 1, TIME_INFINITE);
|
||||
}
|
||||
|
||||
Button WhistleView::button_scan = {
|
||||
{ 76, 270, 72, 32 },
|
||||
"SCAN"
|
||||
};
|
||||
|
||||
WhistleView::WhistleView(
|
||||
NavigationView& nav,
|
||||
TransmitterModel& transmitter_model
|
||||
) : transmitter_model(transmitter_model)
|
||||
NavigationView& nav
|
||||
)
|
||||
{
|
||||
Mailbox mbox;
|
||||
msg_t mbox_buffer[3];
|
||||
chMBInit(&mbox, mbox_buffer, 3);
|
||||
|
||||
transmitter_model.set_modulation(TX_TONE);
|
||||
transmitter_model.set_tuning_frequency(portapack::persistent_memory::tuned_frequency());
|
||||
|
||||
add_children({ {
|
||||
&button_transmit,
|
||||
&button_exit
|
||||
} });
|
||||
|
||||
button_transmit.on_select = [this,&transmitter_model](Button&){
|
||||
button_transmit.on_select = [this](Button&){
|
||||
/*uint16_t c;
|
||||
ui::Context context;
|
||||
|
||||
@ -99,7 +80,7 @@ WhistleView::WhistleView(
|
||||
|
||||
text_status.set("Send...");*/
|
||||
|
||||
transmitter_model.enable();
|
||||
//transmitter_model.enable();
|
||||
};
|
||||
|
||||
button_exit.on_select = [&nav](Button&){
|
||||
|
@ -1,5 +1,6 @@
|
||||
/*
|
||||
* Copyright (C) 2015 Jared Boone, ShareBrained Technology, Inc.
|
||||
* Copyright (C) 2016 Furrtek
|
||||
*
|
||||
* This file is part of PortaPack.
|
||||
*
|
||||
@ -22,21 +23,16 @@
|
||||
#include "ui.hpp"
|
||||
#include "ui_widget.hpp"
|
||||
#include "ui_painter.hpp"
|
||||
#include "ui_menu.hpp"
|
||||
#include "ui_navigation.hpp"
|
||||
#include "ui_font_fixed_8x16.hpp"
|
||||
#include "clock_manager.hpp"
|
||||
#include "message.hpp"
|
||||
#include "rf_path.hpp"
|
||||
#include "max2837.hpp"
|
||||
#include "volume.hpp"
|
||||
#include "transmitter_model.hpp"
|
||||
|
||||
namespace ui {
|
||||
|
||||
class WhistleView : public View {
|
||||
public:
|
||||
WhistleView(NavigationView& nav, TransmitterModel& transmitter_model);
|
||||
WhistleView(NavigationView& nav);
|
||||
~WhistleView();
|
||||
|
||||
void focus() override;
|
||||
@ -65,7 +61,6 @@ private:
|
||||
{{ 468225000, 468275000, 468325000 }, 458275000}
|
||||
};
|
||||
rf::Frequency f;
|
||||
TransmitterModel& transmitter_model;
|
||||
|
||||
Text text_status {
|
||||
{ 172, 196, 64, 16 },
|
||||
|
@ -1,431 +0,0 @@
|
||||
/*
|
||||
* 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 "ui_xylos.hpp"
|
||||
#include "ui_alphanum.hpp"
|
||||
|
||||
#include "portapack.hpp"
|
||||
#include "baseband_api.hpp"
|
||||
//#include "audio.hpp"
|
||||
#include "portapack_persistent_memory.hpp"
|
||||
|
||||
#include <cstring>
|
||||
#include <stdio.h>
|
||||
|
||||
using namespace portapack;
|
||||
|
||||
namespace ui {
|
||||
|
||||
/*
|
||||
void XylosRXView::talk() {
|
||||
uint8_t c;
|
||||
|
||||
xylos_voice_phrase[0] = XYLOS_VOICE_HEADER; // Header
|
||||
for (c=0; c<4; c++)
|
||||
xylos_voice_phrase[c+1] = XYLOS_VOICE_ZERO + ccir_received[c];
|
||||
xylos_voice_phrase[5] = XYLOS_VOICE_HEADER + 1; // City
|
||||
xylos_voice_phrase[6] = XYLOS_VOICE_ZERO + ccir_received[4];
|
||||
xylos_voice_phrase[7] = XYLOS_VOICE_ZERO + ccir_received[5];
|
||||
xylos_voice_phrase[8] = XYLOS_VOICE_HEADER + 2; // Family
|
||||
xylos_voice_phrase[9] = XYLOS_VOICE_ZERO + ccir_received[6];
|
||||
xylos_voice_phrase[10] = XYLOS_VOICE_HEADER + 3; // Subfamily
|
||||
xylos_voice_phrase[11] = XYLOS_VOICE_ZERO + ccir_received[7];
|
||||
xylos_voice_phrase[12] = XYLOS_VOICE_HEADER + 4; // Address
|
||||
xylos_voice_phrase[13] = XYLOS_VOICE_ZERO + ccir_received[8];
|
||||
xylos_voice_phrase[14] = XYLOS_VOICE_ZERO + ccir_received[9];
|
||||
xylos_voice_phrase[15] = XYLOS_VOICE_RELAYS; // Relays
|
||||
for (c=0; c<4; c++) {
|
||||
xylos_voice_phrase[(c*2)+16] = XYLOS_VOICE_ZERO + 1 + c;
|
||||
xylos_voice_phrase[(c*2)+17] = XYLOS_VOICE_RELAYS + 1 + ccir_received[c+11];
|
||||
}
|
||||
xylos_voice_phrase[24] = XYLOS_VOICE_TRAILER; // Trailer
|
||||
for (c=0; c<4; c++)
|
||||
xylos_voice_phrase[c+25] = XYLOS_VOICE_ZERO + ccir_received[c+16];
|
||||
xylos_voice_phrase[29] = 0xFF;
|
||||
}
|
||||
|
||||
void XylosRXView::focus() {
|
||||
button_start.focus();
|
||||
}
|
||||
|
||||
XylosRXView::~XylosRXView() {
|
||||
receiver_model.disable();
|
||||
}
|
||||
|
||||
void XylosRXView::on_show() {
|
||||
//chVTSet(&vt, MS2ST(1000), do_something, NULL);
|
||||
}
|
||||
|
||||
XylosRXView::XylosRXView(
|
||||
NavigationView& nav
|
||||
)
|
||||
{
|
||||
char ccirdebug[21] = { 0,0,0,0,1,8,1,10,10,10,11,1,1,2,0,11,0,0,0,0,0xFF };
|
||||
|
||||
memcpy(ccir_received, ccirdebug, 21);
|
||||
|
||||
add_children({ {
|
||||
&text_dbg,
|
||||
&button_start,
|
||||
&button_exit
|
||||
} });
|
||||
|
||||
button_start.on_select = [this](Button&) {
|
||||
talk();
|
||||
p_idx = 0;
|
||||
};
|
||||
|
||||
button_exit.on_select = [&nav](Button&){
|
||||
nav.pop();
|
||||
};
|
||||
|
||||
}
|
||||
*/
|
||||
|
||||
|
||||
|
||||
void XylosView::focus() {
|
||||
options_ra.focus();
|
||||
}
|
||||
|
||||
XylosView::~XylosView() {
|
||||
transmitter_model.disable();
|
||||
baseband::shutdown();
|
||||
}
|
||||
|
||||
void XylosView::generate_message() {
|
||||
uint8_t c;
|
||||
|
||||
// Init CCIR message
|
||||
memcpy(ccir_message, ccir_base, 21);
|
||||
|
||||
// Header
|
||||
ccir_message[0] = (header_code_a.value() / 10) + 0x30;
|
||||
ccir_message[1] = (header_code_a.value() % 10) + 0x30;
|
||||
ccir_message[2] = (header_code_b.value() / 10) + 0x30;
|
||||
ccir_message[3] = (header_code_b.value() % 10) + 0x30;
|
||||
|
||||
// Addresses
|
||||
ccir_message[4] = (city_code.value() / 10) + 0x30;
|
||||
ccir_message[5] = (city_code.value() % 10) + 0x30;
|
||||
ccir_message[6] = family_code.value() + 0x30;
|
||||
|
||||
if (checkbox_wcsubfamily.value() == false)
|
||||
ccir_message[7] = subfamily_code.value() + 0x30;
|
||||
else
|
||||
ccir_message[7] = 'A';
|
||||
|
||||
if (checkbox_wcid.value() == false) {
|
||||
ccir_message[8] = (receiver_code.value() / 10) + 0x30;
|
||||
ccir_message[9] = (receiver_code.value() % 10) + 0x30;
|
||||
} else {
|
||||
ccir_message[8] = 'A';
|
||||
ccir_message[9] = 'A';
|
||||
}
|
||||
|
||||
// Commands
|
||||
ccir_message[11] = options_ra.selected_index() + 0x30;
|
||||
ccir_message[12] = options_rb.selected_index() + 0x30;
|
||||
ccir_message[13] = options_rc.selected_index() + 0x30;
|
||||
ccir_message[14] = options_rd.selected_index() + 0x30;
|
||||
|
||||
// Get rid of repeats with E code
|
||||
for (c = 1; c < 20; c++)
|
||||
if (ccir_message[c] == ccir_message[c - 1]) ccir_message[c] = 'E';
|
||||
|
||||
// Display as text
|
||||
text_message.set(ccir_message);
|
||||
|
||||
ascii_to_ccir(ccir_message);
|
||||
}
|
||||
|
||||
void XylosView::start_tx() {
|
||||
//audio::headphone::set_volume(volume_t::decibel(90 - 99) + audio::headphone::volume_range().max);
|
||||
|
||||
transmitter_model.set_tuning_frequency(xylos_freqs[options_freq.selected_index()]);
|
||||
transmitter_model.set_baseband_configuration({
|
||||
.mode = 0,
|
||||
.sampling_rate = 1536000U,
|
||||
.decimation_factor = 1,
|
||||
});
|
||||
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.tx_data, ccir_message, 21);
|
||||
baseband::set_ccir_data(CCIR_TONELENGTH, 20);
|
||||
}
|
||||
|
||||
// ASCII to frequency LUT index
|
||||
void XylosView::ascii_to_ccir(char *ascii) {
|
||||
uint8_t c;
|
||||
|
||||
for (c = 0; c < 20; c++) {
|
||||
if (ascii[c] > '9')
|
||||
ascii[c] -= 0x37;
|
||||
else
|
||||
ascii[c] -= 0x30;
|
||||
}
|
||||
|
||||
// EOM code for baseband
|
||||
ascii[20] = 0xFF;
|
||||
}
|
||||
|
||||
void XylosView::on_txdone(const int n) {
|
||||
size_t sr;
|
||||
|
||||
if (tx_mode == TESTING) {
|
||||
if (n == 25) {
|
||||
transmitter_model.disable();
|
||||
|
||||
/*if (sequence_idx != 9) {
|
||||
chThdSleepMilliseconds(15000);
|
||||
memcpy(ccir_message, &xylos_sequence[sequence_idx][0], 21);
|
||||
// ASCII to frequency LUT index
|
||||
for (c=0; c<20; c++) {
|
||||
if (ccir_message[c] > '9')
|
||||
ccir_message[c] -= 0x37;
|
||||
else
|
||||
ccir_message[c] -= 0x30;
|
||||
}
|
||||
ccir_message[20] = 0xFF;
|
||||
//memcpy(shared_memory.xylosdata, ccirmessage, 21); TODO !!!
|
||||
|
||||
sequence_idx++;
|
||||
tx_mode = TESTING;
|
||||
transmitter_model.enable();
|
||||
} else {*/
|
||||
button_txtest.set_style(&style());
|
||||
button_txtest.set_text("TEST");
|
||||
tx_mode = IDLE;
|
||||
//}
|
||||
}
|
||||
} else {
|
||||
if (n == 25) {
|
||||
//audio::headphone::set_volume(volume_t::decibel(0 - 99) + audio::headphone::volume_range().max);
|
||||
transmitter_model.disable();
|
||||
progress.set_value(0);
|
||||
|
||||
if (checkbox_cligno.value() == false) {
|
||||
tx_mode = IDLE;
|
||||
button_transmit.set_style(&style_val);
|
||||
button_transmit.set_text("START");
|
||||
} else {
|
||||
chThdSleepMilliseconds(tempo_cligno.value() * 1000);
|
||||
|
||||
// Invert relay states
|
||||
sr = options_ra.selected_index();
|
||||
if (sr > 0) options_ra.set_selected_index(sr ^ 3);
|
||||
sr = options_rb.selected_index();
|
||||
if (sr > 0) options_rb.set_selected_index(sr ^ 3);
|
||||
sr = options_rc.selected_index();
|
||||
if (sr > 0) options_rc.set_selected_index(sr ^ 3);
|
||||
sr = options_rd.selected_index();
|
||||
if (sr > 0) options_rd.set_selected_index(sr ^ 3);
|
||||
|
||||
generate_message();
|
||||
start_tx();
|
||||
}
|
||||
} else {
|
||||
progress.set_value(n);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
XylosView::XylosView(NavigationView& nav) {
|
||||
(void)nav;
|
||||
|
||||
baseband::run_image(portapack::spi_flash::image_tag_xylos);
|
||||
|
||||
add_children({ {
|
||||
&button_txtest,
|
||||
&text_header,
|
||||
&header_code_a,
|
||||
&header_code_b,
|
||||
&text_city,
|
||||
&city_code,
|
||||
&text_family,
|
||||
&family_code,
|
||||
&text_subfamily,
|
||||
&subfamily_code,
|
||||
&checkbox_wcsubfamily,
|
||||
&text_receiver,
|
||||
&receiver_code,
|
||||
&checkbox_wcid,
|
||||
&text_freq,
|
||||
&options_freq,
|
||||
&text_relais,
|
||||
&options_ra,
|
||||
&options_rb,
|
||||
&options_rc,
|
||||
&options_rd,
|
||||
&progress,
|
||||
&text_message,
|
||||
&button_transmit,
|
||||
&checkbox_cligno,
|
||||
&tempo_cligno,
|
||||
&text_cligno
|
||||
} });
|
||||
|
||||
city_code.set_value(18);
|
||||
family_code.set_value(1);
|
||||
subfamily_code.set_value(1);
|
||||
receiver_code.set_value(1);
|
||||
header_code_a.set_value(0);
|
||||
header_code_b.set_value(0);
|
||||
options_freq.set_selected_index(5);
|
||||
tempo_cligno.set_value(5);
|
||||
|
||||
progress.set_max(20);
|
||||
|
||||
options_ra.set_selected_index(1); // R1 OFF
|
||||
|
||||
checkbox_wcsubfamily.set_value(true);
|
||||
checkbox_wcid.set_value(true);
|
||||
|
||||
header_code_a.on_change = [this](int32_t v) {
|
||||
(void)v;
|
||||
generate_message();
|
||||
};
|
||||
header_code_b.on_change = [this](int32_t v) {
|
||||
(void)v;
|
||||
generate_message();
|
||||
};
|
||||
city_code.on_change = [this](int32_t v) {
|
||||
(void)v;
|
||||
generate_message();
|
||||
};
|
||||
family_code.on_change = [this](int32_t v) {
|
||||
(void)v;
|
||||
generate_message();
|
||||
};
|
||||
subfamily_code.on_change = [this](int32_t v) {
|
||||
(void)v;
|
||||
generate_message();
|
||||
};
|
||||
receiver_code.on_change = [this](int32_t v) {
|
||||
(void)v;
|
||||
generate_message();
|
||||
};
|
||||
|
||||
checkbox_wcsubfamily.on_select = [this](Checkbox&) {
|
||||
if (checkbox_wcsubfamily.value() == true) {
|
||||
receiver_code.set_focusable(false);
|
||||
text_subfamily.set_style(&style_grey);
|
||||
} else {
|
||||
receiver_code.set_focusable(true);
|
||||
text_subfamily.set_style(&style());
|
||||
}
|
||||
generate_message();
|
||||
};
|
||||
|
||||
checkbox_wcid.on_select = [this](Checkbox&) {
|
||||
if (checkbox_wcid.value() == true) {
|
||||
receiver_code.set_focusable(false);
|
||||
text_receiver.set_style(&style_grey);
|
||||
} else {
|
||||
receiver_code.set_focusable(true);
|
||||
text_receiver.set_style(&style());
|
||||
}
|
||||
receiver_code.set_dirty();
|
||||
generate_message();
|
||||
};
|
||||
|
||||
options_ra.on_change = [this](size_t n, OptionsField::value_t v) {
|
||||
(void)n;
|
||||
(void)v;
|
||||
generate_message();
|
||||
};
|
||||
options_rb.on_change = [this](size_t n, OptionsField::value_t v) {
|
||||
(void)n;
|
||||
(void)v;
|
||||
generate_message();
|
||||
};
|
||||
options_rc.on_change = [this](size_t n, OptionsField::value_t v) {
|
||||
(void)n;
|
||||
(void)v;
|
||||
generate_message();
|
||||
};
|
||||
options_rd.on_change = [this](size_t n, OptionsField::value_t v) {
|
||||
(void)n;
|
||||
(void)v;
|
||||
generate_message();
|
||||
};
|
||||
|
||||
button_transmit.set_style(&style_val);
|
||||
|
||||
generate_message();
|
||||
|
||||
// Transmission and tones testing
|
||||
button_txtest.on_select = [this](Button&) {
|
||||
// Tones going up in pitch
|
||||
const uint8_t ccir_test[21] = { 11, 13, 15, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 14, 12, 10, 12, 14, 0, 9, 0xFF };
|
||||
|
||||
if (tx_mode == IDLE) {
|
||||
tx_mode = TESTING;
|
||||
memcpy(ccir_message, ccir_test, 21);
|
||||
//audio::headphone::set_volume(volume_t::decibel(90 - 99) + audio::headphone::volume_range().max);
|
||||
button_txtest.set_style(&style_cancel);
|
||||
button_txtest.set_text("Wait");
|
||||
start_tx();
|
||||
}
|
||||
};
|
||||
|
||||
// Sequence playback
|
||||
/*button_txtest.on_select = [this](Button&) {
|
||||
if (tx_mode == IDLE) {
|
||||
tx_mode = TESTING;
|
||||
sequence_idx = 0;
|
||||
|
||||
memcpy(ccir_message, &xylos_sequence[sequence_idx][0], 21);
|
||||
|
||||
ascii_to_ccir(ccir_message);
|
||||
|
||||
sequence_idx++;
|
||||
|
||||
button_txtest.set_style(&style_cancel);
|
||||
button_txtest.set_text("Wait");
|
||||
start_tx();
|
||||
}
|
||||
};*/
|
||||
|
||||
// Single transmit
|
||||
button_transmit.on_select = [this,&nav](Button&) {
|
||||
if (tx_mode == IDLE) {
|
||||
/*auto modal_view = nav.push<ModalMessageView>("TX", "TX ?", true);
|
||||
modal_view->on_choice = [this](bool choice) {
|
||||
if (choice) {*/
|
||||
// audio::headphone::set_volume(volume_t::decibel(90 - 99) + audio::headphone::volume_range().max);
|
||||
tx_mode = SINGLE;
|
||||
button_transmit.set_style(&style_cancel);
|
||||
button_transmit.set_text("Wait");
|
||||
generate_message();
|
||||
start_tx();
|
||||
//}
|
||||
//};
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
} /* namespace ui */
|
@ -1,387 +0,0 @@
|
||||
/*
|
||||
* 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 "ui.hpp"
|
||||
#include "ui_widget.hpp"
|
||||
#include "ui_navigation.hpp"
|
||||
#include "ui_font_fixed_8x16.hpp"
|
||||
|
||||
#include "bmp_bulb_on.hpp"
|
||||
#include "bmp_bulb_off.hpp"
|
||||
#include "bmp_bulb_ignore.hpp"
|
||||
|
||||
#include "message.hpp"
|
||||
//#include "volume.hpp"
|
||||
#include "transmitter_model.hpp"
|
||||
//#include "receiver_model.hpp"
|
||||
#include "portapack.hpp"
|
||||
|
||||
#define CCIR_TONELENGTH (15360*2)-1 // 1536000/10/10
|
||||
|
||||
namespace ui {
|
||||
|
||||
/*
|
||||
#define XYLOS_VOICE_ZERO 0
|
||||
#define XYLOS_VOICE_HEADER 16
|
||||
#define XYLOS_VOICE_RELAYS 21
|
||||
#define XYLOS_VOICE_TRAILER 25
|
||||
|
||||
class XylosRXView : public View {
|
||||
public:
|
||||
XylosRXView(NavigationView& nav);
|
||||
~XylosRXView();
|
||||
|
||||
void talk();
|
||||
void on_show() override;
|
||||
void focus() override;
|
||||
|
||||
Text text_dbg {
|
||||
{ 5 * 8, 14 * 16, 20 * 8, 16 },
|
||||
"--------------------"
|
||||
};
|
||||
|
||||
void do_something(void *p);
|
||||
|
||||
private:
|
||||
uint8_t p_idx;
|
||||
const rf::Frequency xylos_freqs[7] = { 31325000, 31387500, 31437500, 31475000, 31687500, 31975000, 88000000 };
|
||||
uint8_t xylos_voice_phrase[32] = { 0xFF };
|
||||
const char * xylos_voice_filenames[26] = { "zero.wav",
|
||||
"one.wav",
|
||||
"two.wav",
|
||||
"three.wav",
|
||||
"four.wav",
|
||||
"five.wav",
|
||||
"six.wav",
|
||||
"seven.wav",
|
||||
"eight.wav",
|
||||
"nine.wav",
|
||||
"a.wav",
|
||||
"b.wav",
|
||||
"c.wav",
|
||||
"d.wav",
|
||||
"e.wav",
|
||||
"f.wav",
|
||||
"header.wav",
|
||||
"city.wav",
|
||||
"family.wav",
|
||||
"subfamily.wav",
|
||||
"address.wav",
|
||||
"relays.wav",
|
||||
"ignored.wav",
|
||||
"off.wav",
|
||||
"on.wav",
|
||||
"trailer.wav"
|
||||
};
|
||||
char ccir_received[21];
|
||||
|
||||
Text text_title {
|
||||
{ 1 * 8, 1 * 16, 11, 16 },
|
||||
"BH Xylos RX"
|
||||
};
|
||||
|
||||
Text text_city {
|
||||
{ 4 * 8, 3 * 16, 11 * 8, 16 },
|
||||
"Code ville:"
|
||||
};
|
||||
NumberField city_code {
|
||||
{ 16 * 8, 3 * 16 },
|
||||
2,
|
||||
{ 0, 99 },
|
||||
1,
|
||||
' '
|
||||
};
|
||||
|
||||
Text text_freq {
|
||||
{ 5 * 8, 9 * 16, 10 * 8, 16 },
|
||||
"Frequence:"
|
||||
};
|
||||
OptionsField options_freq {
|
||||
{ 16 * 8, 9 * 16 },
|
||||
7,
|
||||
{
|
||||
{ "31.3250", 0 },
|
||||
{ "31.3875", 1 },
|
||||
{ "31.4375", 2 },
|
||||
{ "31.4750", 3 },
|
||||
{ "31.6875", 4 },
|
||||
{ "31.9750", 5 },
|
||||
{ "TEST 88", 6 }
|
||||
}
|
||||
};
|
||||
|
||||
Button button_start {
|
||||
{ 2 * 8, 16 * 16, 64, 32 },
|
||||
"START"
|
||||
};
|
||||
|
||||
Button button_exit {
|
||||
{ 21 * 8, 16 * 16, 64, 32 },
|
||||
"Exit"
|
||||
};
|
||||
};*/
|
||||
|
||||
class XylosView : public View {
|
||||
public:
|
||||
XylosView(NavigationView& nav);
|
||||
~XylosView();
|
||||
|
||||
void focus() override;
|
||||
|
||||
std::string title() const override { return "Xylos transmit"; };
|
||||
|
||||
private:
|
||||
enum tx_modes {
|
||||
IDLE = 0,
|
||||
SINGLE,
|
||||
SEQUENCE,
|
||||
TESTING
|
||||
};
|
||||
|
||||
tx_modes tx_mode = IDLE;
|
||||
|
||||
// 93.975
|
||||
// 94.1625
|
||||
// 94.3125
|
||||
// 94.425
|
||||
// 95.0625
|
||||
// 95.925
|
||||
const rf::Frequency xylos_freqs[7] = { 31325000, 31387500, 31437500, 31475000, 31687500, 31975000, 88000000 };
|
||||
|
||||
char ccir_message[21];
|
||||
|
||||
const char ccir_base[21] = "0000000000B0000B0000";
|
||||
|
||||
/*
|
||||
const char xylos_sequence[9][21] = {
|
||||
"0E0E18920EB1E10B0E0E",
|
||||
"0E0E1890E0B0E12B0E0E",
|
||||
"0E0E18920EB1E20B0E0E",
|
||||
"0E0E18920EB1210B0E0E",
|
||||
"0E0E18920EB1E10B0E0E",
|
||||
"0E0E18920EB1E10B0E0E",
|
||||
"0E0E181AEAB10E0B0E0E",
|
||||
"0E01E81AEAB10E0B0E0E",
|
||||
"0E03181AEAB10E0B0E0E"
|
||||
};*/
|
||||
|
||||
unsigned int sequence_idx;
|
||||
|
||||
void ascii_to_ccir(char *ascii);
|
||||
void start_tx();
|
||||
void generate_message();
|
||||
void on_txdone(const int n);
|
||||
|
||||
const Style style_val {
|
||||
.font = font::fixed_8x16,
|
||||
.background = Color::black(),
|
||||
.foreground = Color::green(),
|
||||
};
|
||||
const Style style_cancel {
|
||||
.font = font::fixed_8x16,
|
||||
.background = Color::black(),
|
||||
.foreground = Color::red(),
|
||||
};
|
||||
const Style style_grey {
|
||||
.font = font::fixed_8x16,
|
||||
.background = Color::black(),
|
||||
.foreground = Color::grey(),
|
||||
};
|
||||
|
||||
Text text_header {
|
||||
{ 8 * 8, 1 * 16, 7 * 8, 16 },
|
||||
"Header:"
|
||||
};
|
||||
NumberField header_code_a {
|
||||
{ 16 * 8, 1 * 16 },
|
||||
2,
|
||||
{ 0, 99 },
|
||||
1,
|
||||
'0'
|
||||
};
|
||||
NumberField header_code_b {
|
||||
{ 18 * 8, 1 * 16 },
|
||||
2,
|
||||
{ 0, 99 },
|
||||
1,
|
||||
'0'
|
||||
};
|
||||
|
||||
Text text_city {
|
||||
{ 4 * 8, 2 * 16, 11 * 8, 16 },
|
||||
"Code ville:"
|
||||
};
|
||||
NumberField city_code {
|
||||
{ 16 * 8, 2 * 16 },
|
||||
2,
|
||||
{ 0, 99 },
|
||||
1,
|
||||
' '
|
||||
};
|
||||
|
||||
Text text_family {
|
||||
{ 7 * 8, 3 * 16, 8 * 8, 16 },
|
||||
"Famille:"
|
||||
};
|
||||
NumberField family_code {
|
||||
{ 16 * 8, 3 * 16 },
|
||||
1,
|
||||
{ 0, 9 },
|
||||
1,
|
||||
' '
|
||||
};
|
||||
|
||||
Text text_subfamily {
|
||||
{ 2 * 8, 4 * 16 + 4, 13 * 8, 16 },
|
||||
"Sous-famille:"
|
||||
};
|
||||
NumberField subfamily_code {
|
||||
{ 16 * 8, 4 * 16 + 4 },
|
||||
1,
|
||||
{ 0, 9 },
|
||||
1,
|
||||
' '
|
||||
};
|
||||
Checkbox checkbox_wcsubfamily {
|
||||
{ 20 * 8, 4 * 16},
|
||||
6,
|
||||
"Toutes"
|
||||
};
|
||||
|
||||
Text text_receiver {
|
||||
{ 2 * 8, 6 * 16, 13 * 8, 16 },
|
||||
"ID recepteur:"
|
||||
};
|
||||
NumberField receiver_code {
|
||||
{ 16 * 8, 6 * 16 },
|
||||
2,
|
||||
{ 0, 99 },
|
||||
1,
|
||||
'0'
|
||||
};
|
||||
Checkbox checkbox_wcid {
|
||||
{ 20 * 8, 6 * 16 },
|
||||
4,
|
||||
"Tous"
|
||||
};
|
||||
|
||||
Text text_freq {
|
||||
{ 6 * 8, 8 * 16, 10 * 8, 16 },
|
||||
"Frequence:"
|
||||
};
|
||||
OptionsField options_freq {
|
||||
{ 17 * 8, 8 * 16},
|
||||
7,
|
||||
{
|
||||
{ "31.3250", 0 },
|
||||
{ "31.3875", 1 },
|
||||
{ "31.4375", 2 },
|
||||
{ "31.4750", 3 },
|
||||
{ "31.6875", 4 },
|
||||
{ "31.9750", 5 },
|
||||
{ "TEST 88", 6 }
|
||||
}
|
||||
};
|
||||
|
||||
Text text_relais {
|
||||
{ 8, 9 * 16 + 4, 7 * 8, 16 },
|
||||
"Relais:"
|
||||
};
|
||||
|
||||
ImageOptionsField options_ra {
|
||||
{ 26, 166, 24, 24 },
|
||||
{
|
||||
{ &bulb_ignore_bmp[0], 0 },
|
||||
{ &bulb_off_bmp[0], 1 },
|
||||
{ &bulb_on_bmp[0], 2 }
|
||||
}
|
||||
};
|
||||
ImageOptionsField options_rb {
|
||||
{ 79, 166, 24, 24 },
|
||||
{
|
||||
{ &bulb_ignore_bmp[0], 0 },
|
||||
{ &bulb_off_bmp[0], 1 },
|
||||
{ &bulb_on_bmp[0], 2 }
|
||||
}
|
||||
};
|
||||
ImageOptionsField options_rc {
|
||||
{ 133, 166, 24, 24 },
|
||||
{
|
||||
{ &bulb_ignore_bmp[0], 0 },
|
||||
{ &bulb_off_bmp[0], 1 },
|
||||
{ &bulb_on_bmp[0], 2 }
|
||||
}
|
||||
};
|
||||
ImageOptionsField options_rd {
|
||||
{ 186, 166, 24, 24 },
|
||||
{
|
||||
{ &bulb_ignore_bmp[0], 0 },
|
||||
{ &bulb_off_bmp[0], 1 },
|
||||
{ &bulb_on_bmp[0], 2 }
|
||||
}
|
||||
};
|
||||
|
||||
ProgressBar progress {
|
||||
{ 5 * 8, 13 * 16, 20 * 8, 16 },
|
||||
};
|
||||
Text text_message {
|
||||
{ 5 * 8, 14 * 16, 20 * 8, 16 },
|
||||
"--------------------"
|
||||
};
|
||||
|
||||
Button button_transmit {
|
||||
{ 2 * 8, 16 * 16, 64, 32 },
|
||||
"START"
|
||||
};
|
||||
|
||||
Checkbox checkbox_cligno {
|
||||
{ 96, 16 * 16},
|
||||
3,
|
||||
"J/N"
|
||||
};
|
||||
NumberField tempo_cligno {
|
||||
{ 104, 16 * 16 + 28 },
|
||||
2,
|
||||
{ 1, 99 },
|
||||
1,
|
||||
' '
|
||||
};
|
||||
Text text_cligno {
|
||||
{ 104 + 16, 16 * 16 + 28, 2 * 8, 16 },
|
||||
"s."
|
||||
};
|
||||
|
||||
Button button_txtest {
|
||||
{ 20 * 8, 16 * 16, 64, 32 },
|
||||
"TEST"
|
||||
};
|
||||
|
||||
MessageHandlerRegistration message_handler_tx_done {
|
||||
Message::ID::TXDone,
|
||||
[this](const Message* const p) {
|
||||
const auto message = *reinterpret_cast<const TXDoneMessage*>(p);
|
||||
this->on_txdone(message.n);
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
} /* namespace ui */
|
@ -1,4 +1,4 @@
|
||||
unsigned char ymdata_bin[] = {
|
||||
const uint8_t ymdata_bin[] = {
|
||||
0x00, 0x03, 0x1e, 0x00, 0xb4, 0x01, 0x3e, 0x02, 0x61, 0x05, 0xf7, 0x06,
|
||||
0x6d, 0x08, 0xc2, 0x09, 0x68, 0x0a, 0x8d, 0x0c, 0x43, 0x0d, 0x9e, 0x0f,
|
||||
0xef, 0x10, 0x01, 0x11, 0x0f, 0x11, 0x01, 0x64, 0x98, 0x3d, 0x82, 0xc8,
|
||||
|
@ -375,19 +375,12 @@ DeclareTargets(PAFS afsk)
|
||||
#)
|
||||
#DeclareTargets(PEPR epar)
|
||||
|
||||
### Xylos
|
||||
### Tones
|
||||
|
||||
set(MODE_CPPSRC
|
||||
proc_xylos.cpp
|
||||
proc_tones.cpp
|
||||
)
|
||||
DeclareTargets(PXYL xylos)
|
||||
|
||||
### DTMF TX
|
||||
|
||||
set(MODE_CPPSRC
|
||||
proc_dtmf_tx.cpp
|
||||
)
|
||||
DeclareTargets(PDTX dtmf_tx)
|
||||
DeclareTargets(PTON tones)
|
||||
|
||||
### RDS
|
||||
|
||||
|
@ -31,6 +31,12 @@
|
||||
#include <cstddef>
|
||||
#include <array>
|
||||
|
||||
void AudioOutput::configure(
|
||||
const bool do_proc
|
||||
) {
|
||||
do_processing = do_proc;
|
||||
}
|
||||
|
||||
void AudioOutput::configure(
|
||||
const iir_biquad_config_t& hpf_config,
|
||||
const iir_biquad_config_t& deemph_config,
|
||||
@ -69,20 +75,25 @@ void AudioOutput::write(
|
||||
void AudioOutput::on_block(
|
||||
const buffer_f32_t& audio
|
||||
) {
|
||||
const auto audio_present_now = squelch.execute(audio);
|
||||
|
||||
hpf.execute_in_place(audio);
|
||||
deemph.execute_in_place(audio);
|
||||
|
||||
audio_present_history = (audio_present_history << 1) | (audio_present_now ? 1 : 0);
|
||||
const bool audio_present = (audio_present_history != 0);
|
||||
bool audio_present;
|
||||
|
||||
if( !audio_present ) {
|
||||
for(size_t i=0; i<audio.count; i++) {
|
||||
audio.p[i] = 0;
|
||||
}
|
||||
}
|
||||
if (do_processing) {
|
||||
const auto audio_present_now = squelch.execute(audio);
|
||||
|
||||
hpf.execute_in_place(audio);
|
||||
deemph.execute_in_place(audio);
|
||||
|
||||
audio_present_history = (audio_present_history << 1) | (audio_present_now ? 1 : 0);
|
||||
audio_present = (audio_present_history != 0);
|
||||
|
||||
if( !audio_present ) {
|
||||
for(size_t i=0; i<audio.count; i++) {
|
||||
audio.p[i] = 0;
|
||||
}
|
||||
}
|
||||
} else
|
||||
audio_present = true;
|
||||
|
||||
fill_audio_buffer(audio, audio_present);
|
||||
}
|
||||
|
||||
|
@ -36,6 +36,10 @@
|
||||
|
||||
class AudioOutput {
|
||||
public:
|
||||
void configure(
|
||||
const bool do_proc
|
||||
);
|
||||
|
||||
void configure(
|
||||
const iir_biquad_config_t& hpf_config,
|
||||
const iir_biquad_config_t& deemph_config = iir_config_passthrough,
|
||||
@ -64,6 +68,8 @@ private:
|
||||
AudioStatsCollector audio_stats;
|
||||
|
||||
uint64_t audio_present_history = 0;
|
||||
|
||||
bool do_processing = true;
|
||||
|
||||
void on_block(const buffer_f32_t& audio);
|
||||
void fill_audio_buffer(const buffer_f32_t& audio, const bool send_to_fifo);
|
||||
|
Binary file not shown.
BIN
firmware/baseband/baseband_tones.img
Normal file
BIN
firmware/baseband/baseband_tones.img
Normal file
Binary file not shown.
@ -50,7 +50,7 @@ void ADSBTXProcessor::execute(const buffer_c8_t& buffer) {
|
||||
if (!bit_part) {
|
||||
if (bit_pos >= 112) {
|
||||
// Stop
|
||||
message.n = 200;
|
||||
message.progress = 200;
|
||||
shared_memory.application_queue.push(message);
|
||||
configured = false;
|
||||
cur_bit = 0;
|
||||
|
@ -41,8 +41,8 @@ void AFSKProcessor::execute(const buffer_c8_t& buffer) {
|
||||
|
||||
if (sample_count >= afsk_samples_per_bit) {
|
||||
if (configured) {
|
||||
cur_byte = shared_memory.tx_data[byte_pos];
|
||||
ext_byte = shared_memory.tx_data[byte_pos + 1];
|
||||
cur_byte = shared_memory.bb_data.data[byte_pos];
|
||||
ext_byte = shared_memory.bb_data.data[byte_pos + 1];
|
||||
|
||||
if (!(cur_byte | ext_byte)) {
|
||||
// End of data
|
||||
@ -50,16 +50,16 @@ void AFSKProcessor::execute(const buffer_c8_t& buffer) {
|
||||
// Repeat
|
||||
bit_pos = 0;
|
||||
byte_pos = 0;
|
||||
cur_byte = shared_memory.tx_data[0];
|
||||
ext_byte = shared_memory.tx_data[1];
|
||||
message.n = repeat_counter + 1;
|
||||
cur_byte = shared_memory.bb_data.data[0];
|
||||
ext_byte = shared_memory.bb_data.data[1];
|
||||
message.progress = repeat_counter + 1;
|
||||
shared_memory.application_queue.push(message);
|
||||
repeat_counter++;
|
||||
} else {
|
||||
// Stop
|
||||
cur_byte = 0;
|
||||
ext_byte = 0;
|
||||
message.n = 0;
|
||||
message.progress = 0;
|
||||
shared_memory.application_queue.push(message);
|
||||
configured = false;
|
||||
}
|
||||
|
@ -48,15 +48,15 @@ void DTMFTXProcessor::execute(const buffer_c8_t& buffer) {
|
||||
tone = true;
|
||||
timer = tone_length;
|
||||
|
||||
tone_code = shared_memory.tx_data[tone_idx];
|
||||
tone_code = shared_memory.bb_data.data[tone_idx];
|
||||
|
||||
if (tone_code == 0xFF) {
|
||||
txdone_message.n = 0xFF; // End of list
|
||||
txdone_message.done = true; // End of list
|
||||
shared_memory.application_queue.push(txdone_message);
|
||||
configured = false;
|
||||
tone = false;
|
||||
} else {
|
||||
txdone_message.n = tone_idx; // New tone (progress)
|
||||
txdone_message.progress = tone_idx; // New tone
|
||||
shared_memory.application_queue.push(txdone_message);
|
||||
tone_idx++;
|
||||
}
|
||||
@ -99,7 +99,7 @@ void DTMFTXProcessor::on_message(const Message* const msg) {
|
||||
if (message.id == Message::ID::DTMFTXConfig) {
|
||||
|
||||
// Translate DTMF message to index in DTMF frequencies table
|
||||
tone_ptr = &shared_memory.tx_data[0];
|
||||
tone_ptr = &shared_memory.bb_data.data[0];
|
||||
for (;;) {
|
||||
tone_code = *tone_ptr;
|
||||
if (tone_code == 0xFF)
|
||||
@ -132,6 +132,8 @@ void DTMFTXProcessor::on_message(const Message* const msg) {
|
||||
tone_length = message.tone_length * 154; // 153.6
|
||||
pause_length = message.pause_length * 154; // 153.6
|
||||
as = 0;
|
||||
txdone_message.progress = 0;
|
||||
txdone_message.done = false;
|
||||
tone = false;
|
||||
timer = 0;
|
||||
tone_idx = 0;
|
||||
|
@ -87,7 +87,7 @@ void JammerProcessor::execute(const buffer_c8_t& buffer) {
|
||||
|
||||
void JammerProcessor::on_message(const Message* const msg) {
|
||||
|
||||
jammer_ranges = (JammerRange*)shared_memory.tx_data;
|
||||
jammer_ranges = (JammerRange*)shared_memory.bb_data.data;
|
||||
|
||||
/*const auto message = *reinterpret_cast<const DTMFTXConfigMessage*>(msg);
|
||||
|
||||
|
@ -49,15 +49,15 @@ void OOKProcessor::execute(const buffer_c8_t& buffer) {
|
||||
if (repeat_counter < repeat) {
|
||||
// Repeat
|
||||
bit_pos = 0;
|
||||
cur_bit = shared_memory.tx_data[0] & 0x80;
|
||||
message.n = repeat_counter + 1;
|
||||
shared_memory.application_queue.push(message);
|
||||
cur_bit = shared_memory.bb_data.data[0] & 0x80;
|
||||
txdone_message.progress = repeat_counter + 1;
|
||||
shared_memory.application_queue.push(txdone_message);
|
||||
repeat_counter++;
|
||||
} else {
|
||||
// Stop
|
||||
cur_bit = 0;
|
||||
message.n = 0;
|
||||
shared_memory.application_queue.push(message);
|
||||
txdone_message.done = true;
|
||||
shared_memory.application_queue.push(txdone_message);
|
||||
configured = false;
|
||||
}
|
||||
pause_counter = 0;
|
||||
@ -65,7 +65,7 @@ void OOKProcessor::execute(const buffer_c8_t& buffer) {
|
||||
pause_counter--;
|
||||
}
|
||||
} else {
|
||||
cur_bit = (shared_memory.tx_data[bit_pos >> 3] << (bit_pos & 7)) & 0x80;
|
||||
cur_bit = (shared_memory.bb_data.data[bit_pos >> 3] << (bit_pos & 7)) & 0x80;
|
||||
bit_pos++;
|
||||
}
|
||||
}
|
||||
@ -108,6 +108,8 @@ void OOKProcessor::on_message(const Message* const p) {
|
||||
repeat_counter = 0;
|
||||
bit_pos = 0;
|
||||
cur_bit = 0;
|
||||
txdone_message.progress = 0;
|
||||
txdone_message.done = false;
|
||||
configured = true;
|
||||
}
|
||||
}
|
||||
|
@ -52,7 +52,7 @@ private:
|
||||
uint32_t tone_phase, phase, sphase;
|
||||
int32_t tone_sample, sig, frq;
|
||||
|
||||
TXDoneMessage message;
|
||||
TXDoneMessage txdone_message;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
@ -98,7 +98,7 @@ void RDSProcessor::execute(const buffer_c8_t& buffer) {
|
||||
void RDSProcessor::on_message(const Message* const msg) {
|
||||
if (msg->id == Message::ID::RDSConfigure) {
|
||||
const auto message = *reinterpret_cast<const RDSConfigureMessage*>(msg);
|
||||
rdsdata = (uint32_t*)shared_memory.tx_data;
|
||||
rdsdata = (uint32_t*)shared_memory.bb_data.data;
|
||||
message_length = message.length;
|
||||
configured = true;
|
||||
}
|
||||
|
152
firmware/baseband/proc_tones.cpp
Normal file
152
firmware/baseband/proc_tones.cpp
Normal file
@ -0,0 +1,152 @@
|
||||
/*
|
||||
* 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_tones.hpp"
|
||||
#include "sine_table_int8.hpp"
|
||||
#include "event_m4.hpp"
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
// This is called at 1536000/2048 = 750Hz
|
||||
void TonesProcessor::execute(const buffer_c8_t& buffer) {
|
||||
|
||||
if (!configured) return;
|
||||
|
||||
ai = 0;
|
||||
|
||||
for (size_t i = 0; i < buffer.count; i++) {
|
||||
|
||||
// Tone generation at full samplerate
|
||||
if (silence_count) {
|
||||
// Just occupy channel with carrier
|
||||
silence_count--;
|
||||
if (!silence_count) {
|
||||
sample_count = 0;
|
||||
tone_a_phase = 0;
|
||||
tone_b_phase = 0;
|
||||
}
|
||||
tone_sample = 0;
|
||||
re = 0;
|
||||
im = 0;
|
||||
} else {
|
||||
if (!sample_count) {
|
||||
digit = shared_memory.bb_data.tones_data.message[digit_pos++];
|
||||
if (digit_pos >= message_length) {
|
||||
configured = false;
|
||||
txdone_message.done = true;
|
||||
shared_memory.application_queue.push(txdone_message);
|
||||
} else {
|
||||
txdone_message.progress = digit_pos; // Inform UI about progress
|
||||
shared_memory.application_queue.push(txdone_message);
|
||||
}
|
||||
|
||||
if ((digit >= 32) || (tone_deltas[digit] == 0)) {
|
||||
silence_count = shared_memory.bb_data.tones_data.silence;
|
||||
} else {
|
||||
if (!dual_tone) {
|
||||
tone_a_delta = tone_deltas[digit];
|
||||
} else {
|
||||
tone_a_delta = tone_deltas[digit << 1];
|
||||
tone_b_delta = tone_deltas[(digit << 1) + 1];
|
||||
}
|
||||
sample_count = tone_durations[digit];
|
||||
}
|
||||
} else {
|
||||
sample_count--;
|
||||
}
|
||||
|
||||
// Ugly
|
||||
if (digit >= 32) {
|
||||
tone_sample = 0;
|
||||
re = 0;
|
||||
im = 0;
|
||||
} else {
|
||||
if (!dual_tone) {
|
||||
tone_sample = (sine_table_i8[(tone_a_phase & 0x03FC0000) >> 18]);
|
||||
tone_a_phase += tone_a_delta;
|
||||
} else {
|
||||
tone_sample = sine_table_i8[(tone_a_phase & 0x03FC0000) >> 18] >> 1;
|
||||
tone_sample += sine_table_i8[(tone_b_phase & 0x03FC0000) >> 18] >> 1;
|
||||
|
||||
tone_a_phase += tone_a_delta;
|
||||
tone_b_phase += tone_b_delta;
|
||||
}
|
||||
|
||||
// FM
|
||||
delta = tone_sample * fm_delta;
|
||||
|
||||
phase += delta;
|
||||
sphase = phase + (64 << 18);
|
||||
|
||||
re = (sine_table_i8[(sphase & 0x03FC0000) >> 18]);
|
||||
im = (sine_table_i8[(phase & 0x03FC0000) >> 18]);
|
||||
}
|
||||
}
|
||||
|
||||
// Headphone output sample generation: 1536000/24000 = 64
|
||||
if (audio_out) {
|
||||
if (!as) {
|
||||
as = 64; // 63 ?
|
||||
audio_buffer.p[ai++] = tone_sample * 128;
|
||||
} else {
|
||||
as--;
|
||||
}
|
||||
}
|
||||
|
||||
buffer.p[i] = {(int8_t)re, (int8_t)im};
|
||||
}
|
||||
|
||||
if (audio_out) audio_output.write(audio_buffer);
|
||||
}
|
||||
|
||||
void TonesProcessor::on_message(const Message* const p) {
|
||||
const auto message = *reinterpret_cast<const TonesConfigureMessage*>(p);
|
||||
if (message.id == Message::ID::TonesConfigure) {
|
||||
silence_count = message.pre_silence; // In samples
|
||||
for (uint8_t c = 0; c < 32; c++) {
|
||||
tone_deltas[c] = shared_memory.bb_data.tones_data.tone_defs[c].delta;
|
||||
tone_durations[c] = shared_memory.bb_data.tones_data.tone_defs[c].duration;
|
||||
}
|
||||
message_length = message.tone_count;
|
||||
fm_delta = message.fm_delta;
|
||||
audio_out = message.audio_out;
|
||||
dual_tone = message.dual_tone;
|
||||
|
||||
if (audio_out) audio_output.configure(false);
|
||||
|
||||
txdone_message.done = false;
|
||||
txdone_message.progress = 0;
|
||||
|
||||
digit_pos = 0;
|
||||
tone_a_phase = 0;
|
||||
tone_b_phase = 0;
|
||||
as = 0;
|
||||
|
||||
configured = true;
|
||||
}
|
||||
}
|
||||
|
||||
int main() {
|
||||
EventDispatcher event_dispatcher { std::make_unique<TonesProcessor>() };
|
||||
event_dispatcher.run();
|
||||
return 0;
|
||||
}
|
@ -20,18 +20,15 @@
|
||||
* Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
#ifndef __PROC_XYLOS_H__
|
||||
#define __PROC_XYLOS_H__
|
||||
#ifndef __PROC_TONES_H__
|
||||
#define __PROC_TONES_H__
|
||||
|
||||
#include "portapack_shared_memory.hpp"
|
||||
#include "baseband_processor.hpp"
|
||||
#include "baseband_thread.hpp"
|
||||
#include "audio_output.hpp"
|
||||
|
||||
//#include "audio_output.hpp"
|
||||
|
||||
#define CCIR_PHASEINC (436.91/2) // (65536*1024)/1536000*10
|
||||
#define CCIR_SILENCE (122880)-1 // 400ms
|
||||
|
||||
class XylosProcessor : public BasebandProcessor {
|
||||
class TonesProcessor : public BasebandProcessor {
|
||||
public:
|
||||
void execute(const buffer_c8_t& buffer) override;
|
||||
|
||||
@ -42,37 +39,31 @@ private:
|
||||
|
||||
BasebandThread baseband_thread { 1536000, this, NORMALPRIO + 20, baseband::Direction::Transmit };
|
||||
|
||||
const uint32_t ccir_phases[16] = {
|
||||
(uint32_t)(1981*CCIR_PHASEINC),
|
||||
(uint32_t)(1124*CCIR_PHASEINC),
|
||||
(uint32_t)(1197*CCIR_PHASEINC),
|
||||
(uint32_t)(1275*CCIR_PHASEINC),
|
||||
(uint32_t)(1358*CCIR_PHASEINC),
|
||||
(uint32_t)(1446*CCIR_PHASEINC),
|
||||
(uint32_t)(1540*CCIR_PHASEINC),
|
||||
(uint32_t)(1640*CCIR_PHASEINC),
|
||||
(uint32_t)(1747*CCIR_PHASEINC),
|
||||
(uint32_t)(1860*CCIR_PHASEINC),
|
||||
(uint32_t)(2400*CCIR_PHASEINC),
|
||||
(uint32_t)(930*CCIR_PHASEINC),
|
||||
(uint32_t)(2247*CCIR_PHASEINC),
|
||||
(uint32_t)(991*CCIR_PHASEINC),
|
||||
(uint32_t)(2110*CCIR_PHASEINC),
|
||||
(uint32_t)(1055*CCIR_PHASEINC)
|
||||
};
|
||||
std::array<int16_t, 32> audio; // 2048/64
|
||||
const buffer_s16_t audio_buffer {
|
||||
(int16_t*)audio.data(),
|
||||
sizeof(audio) / sizeof(int16_t)
|
||||
};
|
||||
|
||||
uint32_t samples_per_tone;
|
||||
int8_t re, im;
|
||||
uint8_t s, as = 0, ai;
|
||||
uint8_t byte_pos = 0;
|
||||
uint8_t digit = 0;
|
||||
uint32_t sample_count = 0;
|
||||
uint32_t tone_phase, phase, sphase;
|
||||
int32_t tone_sample, delta;
|
||||
bool silence = true;
|
||||
TXDoneMessage message;
|
||||
uint32_t tone_deltas[32];
|
||||
uint32_t tone_durations[32];
|
||||
|
||||
//AudioOutput audio_output;
|
||||
bool audio_out;
|
||||
bool dual_tone;
|
||||
uint32_t fm_delta;
|
||||
uint32_t tone_a_phase, tone_b_phase;
|
||||
uint32_t tone_a_delta, tone_b_delta;
|
||||
uint8_t digit_pos;
|
||||
uint8_t digit;
|
||||
uint32_t silence_count, sample_count;
|
||||
uint32_t message_length;
|
||||
uint32_t phase, sphase;
|
||||
int32_t tone_sample, delta;
|
||||
int8_t re, im;
|
||||
uint8_t as, ai;
|
||||
|
||||
TXDoneMessage txdone_message;
|
||||
AudioOutput audio_output;
|
||||
};
|
||||
|
||||
#endif
|
@ -1,122 +0,0 @@
|
||||
/*
|
||||
* 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_xylos.hpp"
|
||||
#include "portapack_shared_memory.hpp"
|
||||
#include "sine_table_int8.hpp"
|
||||
#include "event_m4.hpp"
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
void XylosProcessor::execute(const buffer_c8_t& buffer) {
|
||||
|
||||
// This is called at 1536000/2048 = 750Hz
|
||||
|
||||
if (!configured) return;
|
||||
|
||||
for (size_t i = 0; i<buffer.count; i++) {
|
||||
|
||||
// Tone generation at 1536000/5 = 307.2kHz
|
||||
if (s >= (5 - 1)) {
|
||||
s = 0;
|
||||
|
||||
if (silence) {
|
||||
// Just occupy channel with carrier
|
||||
if (sample_count >= CCIR_SILENCE) {
|
||||
silence = false;
|
||||
sample_count = samples_per_tone;
|
||||
} else {
|
||||
sample_count++;
|
||||
}
|
||||
} else {
|
||||
if (sample_count >= samples_per_tone) {
|
||||
digit = shared_memory.tx_data[byte_pos++];
|
||||
if ((digit == 0xFF) || (byte_pos >= 21)) {
|
||||
configured = false;
|
||||
message.n = 25; // End of message code
|
||||
shared_memory.application_queue.push(message);
|
||||
} else {
|
||||
message.n = byte_pos; // Inform UI about progress (just as eye candy)
|
||||
shared_memory.application_queue.push(message);
|
||||
}
|
||||
|
||||
sample_count = 0;
|
||||
} else {
|
||||
sample_count++;
|
||||
}
|
||||
|
||||
tone_phase += ccir_phases[digit];
|
||||
}
|
||||
} else {
|
||||
s++;
|
||||
}
|
||||
|
||||
if (silence) {
|
||||
re = 0;
|
||||
im = 0;
|
||||
} else {
|
||||
tone_sample = (sine_table_i8[(tone_phase & 0x03FC0000) >> 18]);
|
||||
|
||||
// Audio preview sample generation: 1536000/48000 = 32
|
||||
/*if (as >= 31) {
|
||||
as = 0;
|
||||
audio[ai++] = sample * 128;
|
||||
} else {
|
||||
as++;
|
||||
}*/
|
||||
|
||||
// FM
|
||||
// 1<<18 = 262144
|
||||
// m = (262144 * BW) / 1536000 / 2
|
||||
delta = tone_sample * 853; // 10kHz BW
|
||||
|
||||
phase += delta;
|
||||
sphase = phase + (64 << 18);
|
||||
|
||||
re = (sine_table_i8[(sphase & 0x03FC0000) >> 18]);
|
||||
im = (sine_table_i8[(phase & 0x03FC0000) >> 18]);
|
||||
}
|
||||
|
||||
buffer.p[i] = {(int8_t)re, (int8_t)im};
|
||||
}
|
||||
|
||||
//audio_output.write(audio_buffer);
|
||||
}
|
||||
|
||||
void XylosProcessor::on_message(const Message* const p) {
|
||||
const auto message = *reinterpret_cast<const CCIRConfigureMessage*>(p);
|
||||
if (message.id == Message::ID::CCIRConfigure) {
|
||||
byte_pos = 0;
|
||||
digit = 0;
|
||||
samples_per_tone = message.samples_per_tone;
|
||||
sample_count = samples_per_tone;
|
||||
as = 0;
|
||||
silence = true;
|
||||
configured = true;
|
||||
}
|
||||
}
|
||||
|
||||
int main() {
|
||||
EventDispatcher event_dispatcher { std::make_unique<XylosProcessor>() };
|
||||
event_dispatcher.run();
|
||||
return 0;
|
||||
}
|
Binary file not shown.
@ -23,7 +23,7 @@ __process_stack_size__ = 0x1000; /* main() stack */
|
||||
|
||||
MEMORY
|
||||
{
|
||||
flash : org = 0x00000000, len = 256k /* SPIFI flash @ 0x140????? */
|
||||
flash : org = 0x00000000, len = 512k /* SPIFI flash @ 0x140????? */
|
||||
ram : org = 0x20000000, len = 64k /* AHB SRAM @ 0x20000000 */
|
||||
}
|
||||
|
||||
|
@ -1,5 +1,6 @@
|
||||
/*
|
||||
* Copyright (C) 2015 Jared Boone, ShareBrained Technology, Inc.
|
||||
* Copyright (C) 2016 Furrtek
|
||||
*
|
||||
* This file is part of PortaPack.
|
||||
*
|
||||
@ -68,7 +69,7 @@ public:
|
||||
|
||||
TXDone = 20,
|
||||
Retune = 21,
|
||||
CCIRConfigure = 22,
|
||||
TonesConfigure = 22,
|
||||
AFSKConfigure = 23,
|
||||
PWMRSSIConfigure = 24,
|
||||
OOKConfigure = 25,
|
||||
@ -498,7 +499,8 @@ public:
|
||||
{
|
||||
}
|
||||
|
||||
int n = 0;
|
||||
uint32_t progress = 0;
|
||||
bool done = false;
|
||||
};
|
||||
|
||||
|
||||
@ -521,19 +523,28 @@ public:
|
||||
const int32_t avg;
|
||||
};
|
||||
|
||||
class CCIRConfigureMessage : public Message {
|
||||
class TonesConfigureMessage : public Message {
|
||||
public:
|
||||
constexpr CCIRConfigureMessage(
|
||||
const uint32_t samples_per_tone,
|
||||
const uint16_t tone_count
|
||||
) : Message { ID::CCIRConfigure },
|
||||
samples_per_tone(samples_per_tone),
|
||||
tone_count(tone_count)
|
||||
constexpr TonesConfigureMessage(
|
||||
const uint32_t fm_delta,
|
||||
const uint32_t pre_silence,
|
||||
const uint16_t tone_count,
|
||||
const bool dual_tone,
|
||||
const bool audio_out
|
||||
) : Message { ID::TonesConfigure },
|
||||
fm_delta(fm_delta),
|
||||
pre_silence(pre_silence),
|
||||
tone_count(tone_count),
|
||||
dual_tone(dual_tone),
|
||||
audio_out(audio_out)
|
||||
{
|
||||
}
|
||||
|
||||
const uint32_t samples_per_tone;
|
||||
const uint32_t fm_delta;
|
||||
const uint32_t pre_silence;
|
||||
const uint16_t tone_count;
|
||||
const bool dual_tone;
|
||||
const bool audio_out;
|
||||
};
|
||||
|
||||
class RDSConfigureMessage : public Message {
|
||||
|
@ -184,6 +184,14 @@ void set_playdead_sequence(const uint32_t new_value) {
|
||||
data->playdead_sequence = new_value;
|
||||
}
|
||||
|
||||
bool stealth_mode() {
|
||||
return ((data->ui_config >> 3) & 1) ? true : false;
|
||||
}
|
||||
|
||||
void set_stealth_mode(const bool new_value) {
|
||||
data->ui_config = (data->ui_config & ~0b1000) | ((new_value & 1) << 3);
|
||||
}
|
||||
|
||||
uint32_t ui_config() {
|
||||
uint8_t bloff_value;
|
||||
|
||||
@ -210,7 +218,7 @@ uint16_t ui_config_bloff() {
|
||||
}
|
||||
|
||||
void set_config_textentry(uint8_t new_value) {
|
||||
data->ui_config = (data->ui_config & ~0b1100) | ((new_value & 1) << 2);
|
||||
data->ui_config = (data->ui_config & ~0b100) | ((new_value & 1) << 2);
|
||||
}
|
||||
|
||||
uint8_t ui_config_textentry() {
|
||||
|
@ -65,6 +65,9 @@ void set_playing_dead(const uint32_t new_value);
|
||||
uint32_t playdead_sequence();
|
||||
void set_playdead_sequence(const uint32_t new_value);
|
||||
|
||||
bool stealth_mode();
|
||||
void set_stealth_mode(const bool new_value);
|
||||
|
||||
uint32_t ui_config();
|
||||
void set_ui_config(const uint32_t new_value);
|
||||
|
||||
|
@ -1,5 +1,6 @@
|
||||
/*
|
||||
* Copyright (C) 2015 Jared Boone, ShareBrained Technology, Inc.
|
||||
* Copyright (C) 2016 Furrtek
|
||||
*
|
||||
* This file is part of PortaPack.
|
||||
*
|
||||
@ -25,6 +26,7 @@
|
||||
#include <cstdint>
|
||||
#include <cstddef>
|
||||
|
||||
#include "portapack_shared_memory.hpp"
|
||||
#include "message_queue.hpp"
|
||||
|
||||
struct JammerRange {
|
||||
@ -34,6 +36,17 @@ struct JammerRange {
|
||||
uint32_t duration;
|
||||
};
|
||||
|
||||
struct ToneDef {
|
||||
uint32_t delta;
|
||||
uint32_t duration;
|
||||
};
|
||||
|
||||
struct ToneData {
|
||||
ToneDef tone_defs[32];
|
||||
uint32_t silence;
|
||||
uint8_t message[128];
|
||||
};
|
||||
|
||||
/* NOTE: These structures must be located in the same location in both M4 and M0 binaries */
|
||||
struct SharedMemory {
|
||||
static constexpr size_t application_queue_k = 11;
|
||||
@ -47,8 +60,11 @@ struct SharedMemory {
|
||||
|
||||
char m4_panic_msg[32] { 0 };
|
||||
|
||||
// struct tx_data union for 9x JammerRange ?
|
||||
uint8_t tx_data[512] { 0 };
|
||||
union {
|
||||
ToneData tones_data;
|
||||
JammerRange jammer_ranges[9];
|
||||
uint8_t data[512];
|
||||
} bb_data;
|
||||
};
|
||||
|
||||
extern SharedMemory& shared_memory;
|
||||
|
@ -1,5 +1,6 @@
|
||||
/*
|
||||
* Copyright (C) 2015 Jared Boone, ShareBrained Technology, Inc.
|
||||
* Copyright (C) 2016 Furrtek
|
||||
*
|
||||
* This file is part of PortaPack.
|
||||
*
|
||||
@ -77,10 +78,9 @@ constexpr image_tag_t image_tag_jammer { 'P', 'J', 'A', 'M' };
|
||||
constexpr image_tag_t image_tag_audio_tx { 'P', 'A', 'T', 'X' };
|
||||
constexpr image_tag_t image_tag_afsk { 'P', 'A', 'F', 'S' };
|
||||
constexpr image_tag_t image_tag_epar { 'P', 'E', 'P', 'R' };
|
||||
constexpr image_tag_t image_tag_xylos { 'P', 'X', 'Y', 'L' };
|
||||
constexpr image_tag_t image_tag_tones { 'P', 'T', 'O', 'N' };
|
||||
constexpr image_tag_t image_tag_rds { 'P', 'R', 'D', 'S' };
|
||||
constexpr image_tag_t image_tag_ook { 'P', 'O', 'O', 'K' };
|
||||
constexpr image_tag_t image_tag_dtmf_tx { 'P', 'D', 'T', 'X' };
|
||||
constexpr image_tag_t image_tag_adsb_tx { 'P', 'A', 'D', 'S' };
|
||||
|
||||
constexpr image_tag_t image_tag_hackrf { 'H', 'R', 'F', '1' };
|
||||
|
@ -250,6 +250,12 @@ void View::remove_child(Widget* const widget) {
|
||||
}
|
||||
}
|
||||
|
||||
void View::remove_children(const std::vector<Widget*>& children) {
|
||||
for(auto child : children) {
|
||||
remove_child(child);
|
||||
}
|
||||
}
|
||||
|
||||
const std::vector<Widget*>& View::children() const {
|
||||
return children_;
|
||||
}
|
||||
@ -564,13 +570,17 @@ void Checkbox::set_text(const std::string value) {
|
||||
set_dirty();
|
||||
}
|
||||
|
||||
/*std::string Checkbox::text() const {
|
||||
return text_;
|
||||
}*/
|
||||
|
||||
void Checkbox::set_value(const bool value) {
|
||||
bool Checkbox::set_value(const bool value) {
|
||||
value_ = value;
|
||||
|
||||
if( on_select ) {
|
||||
on_select(*this);
|
||||
return true;
|
||||
}
|
||||
|
||||
set_dirty();
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Checkbox::value() const {
|
||||
@ -616,15 +626,8 @@ void Checkbox::paint(Painter& painter) {
|
||||
}
|
||||
|
||||
bool Checkbox::on_key(const KeyEvent key) {
|
||||
if( key == KeyEvent::Select ) {
|
||||
value_ = not value_;
|
||||
set_dirty();
|
||||
|
||||
if( on_select ) {
|
||||
on_select(*this);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
if( key == KeyEvent::Select )
|
||||
return set_value(not value_);
|
||||
|
||||
return false;
|
||||
}
|
||||
@ -714,8 +717,7 @@ bool Button::on_key(const KeyEvent key) {
|
||||
}
|
||||
} else {
|
||||
if( on_dir ) {
|
||||
on_dir(*this, key);
|
||||
return false;
|
||||
return on_dir(*this, key);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -29,6 +29,7 @@
|
||||
#include "ui_focus.hpp"
|
||||
#include "radio.hpp"
|
||||
|
||||
#include "portapack.hpp"
|
||||
#include "utility.hpp"
|
||||
|
||||
#include <memory>
|
||||
@ -161,6 +162,7 @@ public:
|
||||
void add_child(Widget* const widget);
|
||||
void add_children(const std::vector<Widget*>& children);
|
||||
void remove_child(Widget* const widget);
|
||||
void remove_children(const std::vector<Widget*>& children);
|
||||
const std::vector<Widget*>& children() const override;
|
||||
|
||||
virtual std::string title() const;
|
||||
@ -264,7 +266,7 @@ public:
|
||||
|
||||
void set_text(const std::string value);
|
||||
// std::string text() const;
|
||||
void set_value(const bool value);
|
||||
bool set_value(const bool value);
|
||||
bool value() const;
|
||||
|
||||
void paint(Painter& painter) override;
|
||||
@ -281,7 +283,7 @@ private:
|
||||
class Button : public Widget {
|
||||
public:
|
||||
std::function<void(Button&)> on_select;
|
||||
std::function<void(Button&,KeyEvent)> on_dir;
|
||||
std::function<bool(Button&,KeyEvent)> on_dir;
|
||||
std::function<void(Button&)> on_highlight;
|
||||
|
||||
Button(Rect parent_rect, std::string text);
|
||||
@ -354,6 +356,11 @@ public:
|
||||
|
||||
ImageOptionsField(Rect parent_rect, options_t options);
|
||||
|
||||
ImageOptionsField(
|
||||
) : ImageOptionsField { { }, { } }
|
||||
{
|
||||
}
|
||||
|
||||
void set_options(options_t new_options);
|
||||
|
||||
size_t selected_index() const;
|
||||
|
Binary file not shown.
Loading…
Reference in New Issue
Block a user