This commit is contained in:
Brumi-2021
2022-01-29 12:19:06 +01:00
48 changed files with 463551 additions and 431 deletions
+2
View File
@@ -172,6 +172,7 @@ set(CPPSRC
irq_rtc.cpp
log_file.cpp
portapack.cpp
qrcodegen.cpp
radio.cpp
receiver_model.cpp
recent_entries.cpp
@@ -206,6 +207,7 @@ set(CPPSRC
ui/ui_channel.cpp
ui/ui_font_fixed_8x16.cpp
ui/ui_geomap.cpp
ui/ui_qrcode.cpp
ui/ui_menu.cpp
ui/ui_btngrid.cpp
ui/ui_receiver.cpp
+2 -2
View File
@@ -59,7 +59,7 @@ static float latlon_float(const int32_t normalized) {
static std::string mmsi(
const ais::MMSI& mmsi
) {
return to_string_dec_uint(mmsi, 9);
return to_string_dec_uint(mmsi, 9, '0'); // MMSI is always is always 9 characters pre-padded with zeros
}
static std::string navigational_status(const unsigned int value) {
@@ -240,7 +240,7 @@ AISRecentEntryDetailView::AISRecentEntryDetailView(NavigationView& nav) {
void AISRecentEntryDetailView::update_position() {
if (send_updates)
geomap_view->update_position(ais::format::latlon_float(entry_.last_position.latitude.normalized()), ais::format::latlon_float(entry_.last_position.longitude.normalized()), (float)entry_.last_position.true_heading);
geomap_view->update_position(ais::format::latlon_float(entry_.last_position.latitude.normalized()), ais::format::latlon_float(entry_.last_position.longitude.normalized()), (float)entry_.last_position.true_heading, 0);
}
void AISRecentEntryDetailView::focus() {
+6 -6
View File
@@ -75,8 +75,8 @@ CaptureAppView::CaptureAppView(NavigationView& nav) {
option_bandwidth.on_change = [this](size_t, uint32_t base_rate) {
sampling_rate = 8 * base_rate; // Decimation by 8 done on baseband side
/* base_rate is used for FFT calculation and display LCD, and also in recording writing SD Card rate. */
/* ex. sampling_rate values, 4Mhz, when recording 500 khz (BW) and fs 8 Mhz , when selected 1 Mhz BW ...*/
/* ex. recording 500khz BW to .C16 file, base_rate clock 500khz x2(I,Q) x 2 bytes (int signed) =2MB/sec rate SD Card */
/* ex. sampling_rate values, 4Mhz, when recording 500 kHz (BW) and fs 8 Mhz , when selected 1 Mhz BW ...*/
/* ex. recording 500kHz BW to .C16 file, base_rate clock 500kHz x2(I,Q) x 2 bytes (int signed) =2MB/sec rate SD Card */
waterfall.on_hide();
record_view.set_sampling_rate(sampling_rate);
@@ -85,12 +85,12 @@ CaptureAppView::CaptureAppView(NavigationView& nav) {
switch(sampling_rate) { // we use the var fs (sampling_rate) , to set up BPF aprox < fs_max/2 by Nyquist theorem.
case 0 ... 2000000: // BW Captured range (0 <= 250Khz max ) fs = 8 x 250 Khz
case 0 ... 2000000: // BW Captured range (0 <= 250kHz max ) fs = 8 x 250 kHz
anti_alias_baseband_bandwidth_filter = 1750000; // Minimum BPF MAX2837 for all those lower BW options.
break;
case 4000000 ... 6000000: // BW capture range (500k ... 750Khz max ) fs_max = 8 x 750Khz = 6Mhz
// BW 500k ... 750khz , ex. 500khz (fs = 8*BW = 4Mhz) , BW 600Khz (fs = 4,8Mhz) , BW 750 Khz (fs = 6Mhz)
case 4000000 ... 6000000: // BW capture range (500k ... 750kHz max ) fs_max = 8 x 750kHz = 6Mhz
// BW 500k ... 750kHz , ex. 500kHz (fs = 8*BW = 4Mhz) , BW 600kHz (fs = 4,8Mhz) , BW 750 kHz (fs = 6Mhz)
anti_alias_baseband_bandwidth_filter = 2500000; // in some IC MAX2837 appear 2250000 , but both works similar.
break;
@@ -123,7 +123,7 @@ CaptureAppView::CaptureAppView(NavigationView& nav) {
waterfall.on_show();
};
option_bandwidth.set_selected_index(7); // 500k, Preselected starting default option 500khz
option_bandwidth.set_selected_index(7); // 500k, Preselected starting default option 500kHz
receiver_model.set_modulation(ReceiverModel::Mode::Capture);
receiver_model.enable();
+14 -5
View File
@@ -145,16 +145,23 @@ void ReplayAppView::start() {
field_rfgain.set_value(tx_gain);
receiver_model.set_tx_gain(tx_gain);
field_rfamp.on_change = [this](int32_t v) {
rf_amp = (bool)v;
};
field_rfamp.set_value(rf_amp ? 14 : 0);
radio::enable({
receiver_model.tuning_frequency(),
sample_rate * 8 ,
baseband_bandwidth,
rf::Direction::Transmit,
receiver_model.rf_amp(),
rf_amp, // previous code line : "receiver_model.rf_amp()," was passing the same rf_amp of all Receiver Apps
static_cast<int8_t>(receiver_model.lna()),
static_cast<int8_t>(receiver_model.vga())
});
}
});
}
void ReplayAppView::stop(const bool do_loop) {
if( is_active() )
@@ -186,7 +193,9 @@ ReplayAppView::ReplayAppView(
) : nav_ (nav)
{
tx_gain = 35;field_rfgain.set_value(tx_gain);
tx_gain = 35;field_rfgain.set_value(tx_gain); // Initial default value (-12 dB's max ).
field_rfamp.set_value(rf_amp ? 14 : 0); // Initial default value True. (TX RF amp on , +14dB's)
baseband::run_image(portapack::spi_flash::image_tag_replay);
add_children({
@@ -198,7 +207,7 @@ ReplayAppView::ReplayAppView(
&progressbar,
&field_frequency,
&field_rfgain,
&field_rf_amp,
&field_rfamp, // let's not use common rf_amp
&check_loop,
&button_play,
&waterfall,
+7 -2
View File
@@ -52,6 +52,7 @@ private:
uint32_t sample_rate = 0;
int32_t tx_gain { 47 };
bool rf_amp { true }; // aux private var to store temporal, Replay App rf_amp user selection.
static constexpr uint32_t baseband_bandwidth = 2500000;
const size_t read_size { 16384 };
const size_t buffer_count { 3 };
@@ -112,8 +113,12 @@ private:
1,
' '
};
RFAmpField field_rf_amp {
{ 19 * 8, 2 * 16 }
NumberField field_rfamp { // previously I was using "RFAmpField field_rf_amp" but that is general Receiver amp setting.
{ 19 * 8, 2 * 16 },
2,
{ 0, 14 }, // this time we will display GUI , 0 or 14 dBs same as Mic App
14,
' '
};
Checkbox check_loop {
{ 21 * 8, 2 * 16 },
@@ -6,7 +6,8 @@ namespace ui
{
add_children({&console, &button_ok});
button_ok.on_select = [&nav](Button &) {
button_ok.on_select = [&nav](Button &)
{
nav.pop();
};
@@ -34,14 +35,16 @@ namespace ui
console.writeln("zhang00963,RedFox-Fr,aldude999");
console.writeln("East2West,fossum,ArjanOnwezen");
console.writeln("vXxOinvizioNxX,teixeluis");
console.writeln("heurist1,intoxsick");
console.writeln("Brumi-2021,texasyojimbo");
console.writeln("heurist1,intoxsick,ckuethe");
console.writeln("notpike");
console.writeln("");
break;
case 2:
// https://github.com/eried/portapack-mayhem/graphs/contributors?to=2020-04-12&from=2015-07-31&type=c
console.writeln("\x1B\x06Havoc:\x1B\x10");
console.writeln("furrtek,mrmookie,notpike");
console.writeln("furrtek,mrmookie,NotPike");
console.writeln("mjwaxios,ImDroided,Giorgiofox");
console.writeln("F4GEV,z4ziggy,xmycroftx");
console.writeln("troussos,silascutler");
+187 -18
View File
@@ -72,7 +72,7 @@ void RecentEntriesTable<AircraftRecentEntries>::draw(
to_string_dec_uint((unsigned int)entry.velo.speed,4) +
to_string_dec_uint((unsigned int)(entry.amp>>9),4) + " " +
(entry.hits <= 999 ? to_string_dec_uint(entry.hits, 3) + " " : "1k+ ") +
to_string_dec_uint(entry.age, 3);
to_string_dec_uint(entry.age, 4);
#endif
painter.draw_string(
@@ -91,6 +91,153 @@ void ADSBLogger::log_str(std::string& logline) {
log_file.write_entry(datetime,logline);
}
// Aircraft Details
void ADSBRxAircraftDetailsView::focus() {
button_close.focus();
}
ADSBRxAircraftDetailsView::~ADSBRxAircraftDetailsView() {
on_close_();
}
ADSBRxAircraftDetailsView::ADSBRxAircraftDetailsView(
NavigationView& nav,
const AircraftRecentEntry& entry,
const std::function<void(void)> on_close
) : entry_copy(entry),
on_close_(on_close)
{
char file_buffer[32] { 0 };
bool found = false;
size_t number_of_icao_codes = 0;
std::string icao_code;
add_children({
&labels,
&text_icao_address,
&text_registration,
&text_manufacturer,
&text_model,
&text_type,
&text_number_of_engines,
&text_engine_type,
&text_owner,
&text_operator,
&button_close
});
std::unique_ptr<ADSBLogger> logger { };
//update(entry_copy);
text_icao_address.set(to_string_hex(entry_copy.ICAO_address, 6));
// Try getting the aircraft information from icao24.db
auto result = db_file.open("ADSB/icao24.db");
if (!result.is_valid()) {
// determine number of ICAO24 codes in file, total size / (single index + record size)
number_of_icao_codes = (db_file.size() / 153);
icao_code = to_string_hex(entry_copy.ICAO_address, 6);
// binary search
int first = 0, // First search element
last = number_of_icao_codes - 1, // Last search element
middle, // Mid point of search
position = -1; // Position of search value
while (!found && first <= last) {
middle = (first + last) / 2; // Calculate mid point
db_file.seek(middle * 7);
db_file.read(file_buffer, 6);
if (file_buffer == icao_code) { // If value is found at mid
found = true;
position = middle;
}
else if (file_buffer > icao_code) // If value is in lower half
last = middle - 1;
else
first = middle + 1; // If value is in upper half
}
if (position > -1) {
db_file.seek((number_of_icao_codes * 7) + (position * 146)); // seek starting after index
db_file.read(file_buffer, 9);
text_registration.set(file_buffer);
db_file.read(file_buffer, 33);
text_manufacturer.set(file_buffer);
db_file.read(file_buffer, 33);
text_model.set(file_buffer);
db_file.read(file_buffer, 5); // ICAO type decripton
if(strlen(file_buffer) == 3) {
switch(file_buffer[0]) {
case 'L':
text_type.set("Landplane");
break;
case 'S':
text_type.set("Seaplane");
break;
case 'A':
text_type.set("Amphibian");
break;
case 'H':
text_type.set("Helicopter");
break;
case 'G':
text_type.set("Gyrocopter");
break;
case 'T':
text_type.set("Tilt-wing aircraft");
break;
}
text_number_of_engines.set(std::string(1, file_buffer[1]));
switch(file_buffer[2]) {
case 'P':
text_engine_type.set("Piston engine");
break;
case 'T':
text_engine_type.set("Turboprop/Turboshaft engine");
break;
case 'J':
text_engine_type.set("Jet engine");
break;
case 'E':
text_engine_type.set("Electric engine");
break;
}
}
// check for ICAO type designator
else if(strlen(file_buffer) == 4) {
if(strcmp(file_buffer,"SHIP") == 0) text_type.set("Airship");
else if(strcmp(file_buffer,"BALL") == 0) text_type.set("Balloon");
else if(strcmp(file_buffer,"GLID") == 0) text_type.set("Glider / sailplane");
else if(strcmp(file_buffer,"ULAC") == 0) text_type.set("Micro/ultralight aircraft");
else if(strcmp(file_buffer,"GYRO") == 0) text_type.set("Micro/ultralight autogyro");
else if(strcmp(file_buffer,"UHEL") == 0) text_type.set("Micro/ultralight helicopter");
else if(strcmp(file_buffer,"SHIP") == 0) text_type.set("Airship");
else if(strcmp(file_buffer,"PARA") == 0) text_type.set("Powered parachute/paraplane");
}
db_file.read(file_buffer, 33);
text_owner.set(file_buffer);
db_file.read(file_buffer, 33);
text_operator.set(file_buffer);
} else {
text_registration.set("Unknown");
text_manufacturer.set("Unknown");
}
} else {
text_manufacturer.set("No icao24.db file");
}
button_close.on_select = [&nav](Button&){
nav.pop();
};
};
// End of Aicraft details
void ADSBRxDetailsView::focus() {
button_see_map.focus();
}
@@ -114,7 +261,7 @@ void ADSBRxDetailsView::update(const AircraftRecentEntry& entry) {
text_frame_pos_odd.set(to_string_hex_array(entry_copy.frame_pos_odd.get_raw_data(), 14));
if (send_updates)
geomap_view->update_position(entry_copy.pos.latitude, entry_copy.pos.longitude, entry_copy.velo.heading);
geomap_view->update_position(entry_copy.pos.latitude, entry_copy.pos.longitude, entry_copy.velo.heading, entry_copy.pos.altitude);
}
ADSBRxDetailsView::~ADSBRxDetailsView() {
@@ -130,12 +277,12 @@ ADSBRxDetailsView::ADSBRxDetailsView(
{
char file_buffer[32] { 0 };
bool found = false;
int number_of_airlines = 0;
size_t number_of_airlines = 0;
std::string airline_code;
size_t c;
add_children({
&labels,
&text_icao_address,
&text_callsign,
&text_last_seen,
&text_airline,
@@ -144,6 +291,7 @@ ADSBRxDetailsView::ADSBRxDetailsView(
&text_info2,
&text_frame_pos_even,
&text_frame_pos_odd,
&button_aircraft_details,
&button_see_map
});
@@ -157,19 +305,28 @@ ADSBRxDetailsView::ADSBRxDetailsView(
// Search for 3-letter code
number_of_airlines = (db_file.size() / 68); // determine number of airlines in file
airline_code = entry_copy.callsign.substr(0, 3);
c = 0;
do {
db_file.read(file_buffer, 4);
if (!file_buffer[0])
break;
if (!airline_code.compare(0, 4, file_buffer))
found = true;
else
c++;
} while (!found && (c < number_of_airlines));
// binary search
int first = 0, // First search element
last = number_of_airlines - 1, // Last search element
middle, // Mid point of search
position = -1; // Position of search value
while (!found && first <= last) {
middle = (first + last) / 2; // Calculate mid point
db_file.seek(middle * 4);
db_file.read(file_buffer, 3);
if (file_buffer == airline_code) { // If value is found at mid
found = true;
position = middle;
}
else if (file_buffer > airline_code) // If value is in lower half
last = middle - 1;
else
first = middle + 1; // If value is in upper half
}
if (found) {
db_file.seek((number_of_airlines * 4) + (c << 6)); // seek starting after index
if (position > -1) {
db_file.seek((number_of_airlines * 4) + (position << 6)); // seek starting after index
db_file.read(file_buffer, 32);
text_airline.set(file_buffer);
db_file.read(file_buffer, 32);
@@ -184,9 +341,20 @@ ADSBRxDetailsView::ADSBRxDetailsView(
}
text_callsign.set(entry_copy.callsign);
text_icao_address.set(to_string_hex(entry_copy.ICAO_address, 6));
button_aircraft_details.on_select = [this, &nav](Button&) {
//detailed_entry_key = entry.key();
aircraft_details_view = nav.push<ADSBRxAircraftDetailsView>(
entry_copy,
[this]() {
send_updates = false;
});
send_updates = false;
};
button_see_map.on_select = [this, &nav](Button&) {
if (!send_updates) { // Prevent recursivley launching the map
if (!send_updates) { // Prevent recursively launching the map
geomap_view = nav.push<GeoMapView>(
entry_copy.callsign,
entry_copy.pos.altitude,
@@ -202,6 +370,7 @@ ADSBRxDetailsView::ADSBRxDetailsView(
};
};
void ADSBRxView::focus() {
field_vga.focus();
}
+105 -4
View File
@@ -159,6 +159,94 @@ private:
};
class ADSBRxAircraftDetailsView : public View {
public:
ADSBRxAircraftDetailsView(NavigationView&, const AircraftRecentEntry& entry, const std::function<void(void)> on_close);
~ADSBRxAircraftDetailsView();
ADSBRxAircraftDetailsView(const ADSBRxAircraftDetailsView&) = delete;
ADSBRxAircraftDetailsView(ADSBRxAircraftDetailsView&&) = delete;
ADSBRxAircraftDetailsView& operator=(const ADSBRxAircraftDetailsView&) = delete;
ADSBRxAircraftDetailsView& operator=(ADSBRxAircraftDetailsView&&) = delete;
void focus() override;
void update(const AircraftRecentEntry& entry);
std::string title() const override { return "AC Details"; };
AircraftRecentEntry get_current_entry() { return entry_copy; }
private:
AircraftRecentEntry entry_copy { 0 };
std::function<void(void)> on_close_ { };
bool send_updates { false };
File db_file { };
Labels labels {
{ { 0 * 8, 1 * 16 }, "ICAO:", Color::light_grey() },
{ { 0 * 8, 2 * 16 }, "Registration:", Color::light_grey() },
{ { 0 * 8, 3 * 16 }, "Manufacturer:", Color::light_grey() },
{ { 0 * 8, 5 * 16 }, "Model:", Color::light_grey() },
{ { 0 * 8, 7 * 16 }, "Type:", Color::light_grey() },
{ { 0 * 8, 8 * 16 }, "Number of engines:", Color::light_grey() },
{ { 0 * 8, 9 * 16 }, "Engine type:", Color::light_grey() },
{ { 0 * 8, 11 * 16 }, "Owner:", Color::light_grey() },
{ { 0 * 8, 13 * 16 }, "Operator:", Color::light_grey() }
};
Text text_icao_address {
{ 5 * 8, 1 * 16, 6 * 8, 16},
"-"
};
Text text_registration {
{ 13 * 8, 2 * 16, 8 * 8, 16 },
"-"
};
Text text_manufacturer {
{ 0 * 8, 4 * 16, 19 * 8, 16 },
"-"
};
Text text_model {
{ 0 * 8, 6 * 16, 30 * 8, 16 },
"-"
};
Text text_type {
{ 5 * 8, 7 * 16, 22 * 8, 16 },
"-"
};
Text text_number_of_engines {
{ 18 * 8, 8 * 16, 30 * 8, 16 },
"-"
};
Text text_engine_type {
{ 0 * 8, 10 * 16, 30 * 8, 16},
"-"
};
Text text_owner {
{ 0 * 8, 12 * 16, 30 * 8, 16 },
"-"
};
Text text_operator {
{ 0 * 8, 14 * 16, 30 * 8, 16 },
"-"
};
Button button_close {
{ 9 * 8, 16 * 16, 12 * 8, 3 * 16 },
"Back"
};
};
class ADSBRxDetailsView : public View {
public:
ADSBRxDetailsView(NavigationView&, const AircraftRecentEntry& entry, const std::function<void(void)> on_close);
@@ -181,11 +269,13 @@ private:
AircraftRecentEntry entry_copy { 0 };
std::function<void(void)> on_close_ { };
GeoMapView* geomap_view { nullptr };
ADSBRxAircraftDetailsView* aircraft_details_view { nullptr };
bool send_updates { false };
File db_file { };
Labels labels {
{ { 0 * 8, 1 * 16 }, "Callsign:", Color::light_grey() },
{ { 0 * 8, 1 * 16 }, "ICAO:", Color::light_grey() },
{ { 13 * 8, 1 * 16 }, "Callsign:", Color::light_grey() },
{ { 0 * 8, 2 * 16 }, "Last seen:", Color::light_grey() },
{ { 0 * 8, 3 * 16 }, "Airline:", Color::light_grey() },
{ { 0 * 8, 5 * 16 }, "Country:", Color::light_grey() },
@@ -193,8 +283,13 @@ private:
{ { 0 * 8, 15 * 16 }, "Odd position frame:", Color::light_grey() }
};
Text text_icao_address {
{ 5 * 8, 1 * 16, 6 * 8, 16},
"-"
};
Text text_callsign {
{ 9 * 8, 1 * 16, 8 * 8, 16 },
{ 22 * 8, 1 * 16, 8 * 8, 16 },
"-"
};
@@ -232,11 +327,17 @@ private:
"-"
};
Button button_aircraft_details {
{ 2 * 8, 9 * 16, 12 * 8, 3 * 16 },
"A/C details"
};
Button button_see_map {
{ 8 * 8, 9 * 16, 14 * 8, 3 * 16 },
{ 16 * 8, 9 * 16, 12 * 8, 3 * 16 },
"See on map"
};
};
class ADSBRxView : public View {
public:
@@ -274,7 +375,7 @@ private:
{ "Spd", 3 },
{ "Amp", 3 },
{ "Hit", 3 },
{ "Age", 3 }
{ "Age", 4 }
#endif
} };
AircraftRecentEntries recent { };
+1 -1
View File
@@ -351,7 +351,7 @@ void APRSDetailsView::update() {
}
if (send_updates)
geomap_view->update_position(entry_copy.pos.latitude, entry_copy.pos.longitude, 0);
geomap_view->update_position(entry_copy.pos.latitude, entry_copy.pos.longitude, 0, 0);
}
APRSDetailsView::~APRSDetailsView() {
+11 -11
View File
@@ -242,16 +242,16 @@ private:
{ 17 * 8, (26 * 8) + 4 },
12,
{
{ "5Khz (SA AM)", 5000 },
{ "9Khz (EU AM)", 9000 },
{ "10Khz(US AM)", 10000 },
{ "50Khz (FM1)", 50000 },
{ "100Khz(FM2)", 100000 },
{ "6.25khz(NFM)", 6250 },
{ "12.5khz(NFM)", 12500 },
{ "25khz (N1)", 25000 },
{ "250khz (N2)", 250000 },
{ "8.33khz(AIR)", 8330 }
{ "5kHz (SA AM)", 5000 },
{ "9kHz (EU AM)", 9000 },
{ "10kHz(US AM)", 10000 },
{ "50kHz (FM1)", 50000 },
{ "100kHz(FM2)", 100000 },
{ "6.25kHz(NFM)", 6250 },
{ "12.5kHz(NFM)", 12500 },
{ "25kHz (N1)", 25000 },
{ "250kHz (N2)", 250000 },
{ "8.33kHz(AIR)", 8330 }
}
};
@@ -308,4 +308,4 @@ private:
};
};
} /* namespace ui */
} /* namespace ui */
+54
View File
@@ -41,6 +41,7 @@ void SondeLogger::on_packet(const sonde::Packet& packet) {
namespace ui {
SondeView::SondeView(NavigationView& nav) {
baseband::run_image(portapack::spi_flash::image_tag_sonde);
@@ -63,6 +64,7 @@ SondeView::SondeView(NavigationView& nav) {
&text_temp,
&text_humid,
&geopos,
&button_see_qr,
&button_see_map
});
@@ -108,6 +110,12 @@ SondeView::SondeView(NavigationView& nav) {
static_cast<int8_t>(receiver_model.vga()),
});
// QR code with geo URI
button_see_qr.on_select = [this, &nav](Button&) {
nav.push<QRCodeView>(geo_uri);
};
button_see_map.on_select = [this, &nav](Button&) {
nav.push<GeoMapView>(
sonde_id,
@@ -154,10 +162,56 @@ void SondeView::focus() {
field_vga.focus();
}
// used to convert float to character pointer, since unfortunately function like
// sprintf and c_str aren't supported.
char * SondeView::float_to_char(float x, char *p)
{
char *s = p + 9; // go to end of buffer
uint16_t decimals; // variable to store the decimals
int units; // variable to store the units (part to left of decimal place)
if (x < 0) { // take care of negative numbers
decimals = (int)(x * -100000) % 100000; // make 1000 for 3 decimals etc.
units = (int)(-1 * x);
} else { // positive numbers
decimals = (int)(x * 100000) % 100000;
units = (int)x;
}
// TODO: more elegant solution (loop?)
*--s = (decimals % 10) + '0';
decimals /= 10;
*--s = (decimals % 10) + '0';
decimals /= 10;
*--s = (decimals % 10) + '0';
decimals /= 10;
*--s = (decimals % 10) + '0';
decimals /= 10;
*--s = (decimals % 10) + '0';
*--s = '.';
while (units > 0) {
*--s = (units % 10) + '0';
units /= 10;
}
if (x < 0) *--s = '-'; // unary minus sign for negative numbers
return s;
}
void SondeView::on_packet(const sonde::Packet &packet)
{
if (!use_crc || packet.crc_ok()) //euquiq: Reject bad packet if crc is on
{
char buffer_lat[10] = {};
char buffer_lon[10] = {};
strcpy(geo_uri, "geo:");
strcat(geo_uri, float_to_char(gps_info.lat, buffer_lat));
strcat(geo_uri, ",");
strcat(geo_uri, float_to_char(gps_info.lon, buffer_lon));
text_signature.set(packet.type_string());
sonde_id = packet.serial_number(); //used also as tag on the geomap
+14 -3
View File
@@ -26,6 +26,7 @@
#include "ui_navigation.hpp"
#include "ui_receiver.hpp"
#include "ui_rssi.hpp"
#include "ui_qrcode.hpp"
#include "ui_geomap.hpp"
#include "event_m0.hpp"
@@ -63,6 +64,8 @@ public:
std::string title() const override { return "Radiosonde RX"; };
private:
std::unique_ptr<SondeLogger> logger { };
uint32_t target_frequency_ { 402700000 };
@@ -70,6 +73,8 @@ private:
bool use_crc { false };
bool beep { false };
char geo_uri[32] = {};
sonde::GPS_data gps_info { };
sonde::temp_humid temp_humid_info { };
std::string sonde_id { };
@@ -173,9 +178,15 @@ private:
{ 0, 12 * 16 },
GeoPos::alt_unit::METERS
};
Button button_see_qr {
{ 2 * 8, 15 * 16, 12 * 8, 3 * 16 },
"See QR"
};
Button button_see_map {
{ 8 * 8, 16 * 16, 14 * 8, 3 * 16 },
{ 16 * 8, 15 * 16, 12 * 8, 3 * 16 },
"See on map"
};
@@ -190,7 +201,7 @@ private:
void on_packet(const sonde::Packet& packet);
void on_headphone_volume_changed(int32_t v);
char * float_to_char(float x, char *p);
void set_target_frequency(const uint32_t new_value);
uint32_t tuning_frequency() const;
+55 -2
View File
@@ -1,7 +1,7 @@
/*
* Copyright (C) 2015 Jared Boone, ShareBrained Technology, Inc.
* Copyright (C) 2017 Furrtek
* Copyright (C) 2017 NotPike
* Copyright (C) 2022 NotPike
*
* This file is part of PortaPack.
*
@@ -27,6 +27,7 @@
#include "baseband_api.hpp"
#include "string_format.hpp"
using namespace portapack;
using namespace encoders;
@@ -45,7 +46,13 @@ void TouchTunesView::stop_tx() {
transmitter_model.disable();
tx_mode = IDLE;
progressbar.set_value(0);
text_status.set("Ready");
// EW Mode Check
if(check_ew.value()) {
start_ew();
} else {
text_status.set("Ready");
}
}
void TouchTunesView::on_tx_progress(const uint32_t progress, const bool done) {
@@ -72,7 +79,43 @@ void TouchTunesView::on_tx_progress(const uint32_t progress, const bool done) {
}
}
// EW (Electronic Warfare) Mode will jam the receiving jukebox
// while still alowing you (the hacker) to send commands
// to the target jukebox.
// EW Mode works by transmitting a CW on 433.92MHz inbetween
// transmission events.
void TouchTunesView::start_ew() {
// Radio
transmitter_model.set_tuning_frequency(433920000);
transmitter_model.set_sampling_rate(3072000U);
transmitter_model.set_rf_amp(true);
transmitter_model.set_baseband_bandwidth(3500000U);
transmitter_model.set_tx_gain(47);
transmitter_model.enable();
//UI
text_status.set("Jamming...");
progressbar.set_max(1);
progressbar.set_value(1);
}
void TouchTunesView::stop_ew() {
// Radio
transmitter_model.disable();
// UI
text_status.set("Ready");
progressbar.set_value(0);
}
void TouchTunesView::start_tx(const uint32_t button_index) {
// Check EW Mode
if(check_ew.value()) {
stop_ew();
}
std::string fragments = { "" };
size_t bit;
uint64_t frame_data;
@@ -136,6 +179,7 @@ TouchTunesView::TouchTunesView(
&labels,
&field_pin,
&check_scan,
&check_ew,
&text_status,
&progressbar
});
@@ -145,6 +189,15 @@ TouchTunesView::TouchTunesView(
field_pin.on_change = [this](int32_t v) {
pin = v;
};
// EW Mode
check_ew.on_select = [this](Checkbox&, bool v) {
if(v){
start_ew();
} else {
stop_ew();
}
};
const auto button_fn = [this](Button& button) {
start_tx(button.id);
+10 -2
View File
@@ -1,7 +1,7 @@
/*
* Copyright (C) 2015 Jared Boone, ShareBrained Technology, Inc.
* Copyright (C) 2017 Furrtek
* Copyright (C) 2018 NotPike
* Copyright (C) 2022 NotPike
*
* This file is part of PortaPack.
*
@@ -127,6 +127,8 @@ private:
void start_tx(const uint32_t button_index);
void stop_tx();
void on_tx_progress(const uint32_t progress, const bool done);
void start_ew();
void stop_ew();
struct remote_layout_t {
Point position;
@@ -191,11 +193,17 @@ private:
};
Checkbox check_scan {
{ 2 * 8, 27 * 8 },
{ 2 * 8, 25 * 8 },
4,
"Scan"
};
Checkbox check_ew {
{ 2 * 8, 29 * 8 },
4,
"EW Mode"
};
Text text_status {
{ 2 * 8, 33 * 8, 128, 16 },
"Ready"
+9 -9
View File
@@ -51,15 +51,15 @@ enum freqman_entry_type {
//Entry step placed for AlainD freqman version (or any other enhanced version)
enum freqman_entry_step {
STEP_DEF = 0, // default
AM_US, // 10 Khz AM/CB
AM_EUR, // 9 Khz LW/MW
NFM_1, // 12,5 Khz (Analogic PMR 446)
NFM_2, // 6,25 Khz (Digital PMR 446)
FM_1, // 100 Khz
FM_2, // 50 Khz
N_1, // 25 Khz
N_2, // 250 Khz
AIRBAND, // AIRBAND 8,33 Khz
AM_US, // 10 kHz AM/CB
AM_EUR, // 9 kHz LW/MW
NFM_1, // 12,5 kHz (Analogic PMR 446)
NFM_2, // 6,25 kHz (Digital PMR 446)
FM_1, // 100 kHz
FM_2, // 50 kHz
N_1, // 25 kHz
N_2, // 250 kHz
AIRBAND, // AIRBAND 8,33 kHz
ERROR_STEP
};
+876
View File
@@ -0,0 +1,876 @@
/**
* The MIT License (MIT)
*
* This library is written and maintained by Richard Moore.
* Major parts were derived from Project Nayuki's library.
*
* Copyright (c) 2017 Richard Moore (https://github.com/ricmoo/QRCode)
* Copyright (c) 2017 Project Nayuki (https://www.nayuki.io/page/qr-code-generator-library)
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
/**
* Special thanks to Nayuki (https://www.nayuki.io/) from which this library was
* heavily inspired and compared against.
*
* See: https://github.com/nayuki/QR-Code-generator/tree/master/cpp
*/
#include "qrcodegen.hpp"
#include <stdlib.h>
#include <string.h>
#pragma mark - Error Correction Lookup tables
#if LOCK_VERSION == 0
static const uint16_t NUM_ERROR_CORRECTION_CODEWORDS[4][40] = {
// 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, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40 Error correction level
{ 10, 16, 26, 36, 48, 64, 72, 88, 110, 130, 150, 176, 198, 216, 240, 280, 308, 338, 364, 416, 442, 476, 504, 560, 588, 644, 700, 728, 784, 812, 868, 924, 980, 1036, 1064, 1120, 1204, 1260, 1316, 1372}, // Medium
{ 7, 10, 15, 20, 26, 36, 40, 48, 60, 72, 80, 96, 104, 120, 132, 144, 168, 180, 196, 224, 224, 252, 270, 300, 312, 336, 360, 390, 420, 450, 480, 510, 540, 570, 570, 600, 630, 660, 720, 750}, // Low
{ 17, 28, 44, 64, 88, 112, 130, 156, 192, 224, 264, 308, 352, 384, 432, 480, 532, 588, 650, 700, 750, 816, 900, 960, 1050, 1110, 1200, 1260, 1350, 1440, 1530, 1620, 1710, 1800, 1890, 1980, 2100, 2220, 2310, 2430}, // High
{ 13, 22, 36, 52, 72, 96, 108, 132, 160, 192, 224, 260, 288, 320, 360, 408, 448, 504, 546, 600, 644, 690, 750, 810, 870, 952, 1020, 1050, 1140, 1200, 1290, 1350, 1440, 1530, 1590, 1680, 1770, 1860, 1950, 2040}, // Quartile
};
static const uint8_t NUM_ERROR_CORRECTION_BLOCKS[4][40] = {
// Version: (note that index 0 is for padding, and is set to an illegal value)
// 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, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40 Error correction level
{ 1, 1, 1, 2, 2, 4, 4, 4, 5, 5, 5, 8, 9, 9, 10, 10, 11, 13, 14, 16, 17, 17, 18, 20, 21, 23, 25, 26, 28, 29, 31, 33, 35, 37, 38, 40, 43, 45, 47, 49}, // Medium
{ 1, 1, 1, 1, 1, 2, 2, 2, 2, 4, 4, 4, 4, 4, 6, 6, 6, 6, 7, 8, 8, 9, 9, 10, 12, 12, 12, 13, 14, 15, 16, 17, 18, 19, 19, 20, 21, 22, 24, 25}, // Low
{ 1, 1, 2, 4, 4, 4, 5, 6, 8, 8, 11, 11, 16, 16, 18, 16, 19, 21, 25, 25, 25, 34, 30, 32, 35, 37, 40, 42, 45, 48, 51, 54, 57, 60, 63, 66, 70, 74, 77, 81}, // High
{ 1, 1, 2, 2, 4, 4, 6, 6, 8, 8, 8, 10, 12, 16, 12, 17, 16, 18, 21, 20, 23, 23, 25, 27, 29, 34, 34, 35, 38, 40, 43, 45, 48, 51, 53, 56, 59, 62, 65, 68}, // Quartile
};
static const uint16_t NUM_RAW_DATA_MODULES[40] = {
// 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17,
208, 359, 567, 807, 1079, 1383, 1568, 1936, 2336, 2768, 3232, 3728, 4256, 4651, 5243, 5867, 6523,
// 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
7211, 7931, 8683, 9252, 10068, 10916, 11796, 12708, 13652, 14628, 15371, 16411, 17483, 18587,
// 32, 33, 34, 35, 36, 37, 38, 39, 40
19723, 20891, 22091, 23008, 24272, 25568, 26896, 28256, 29648
};
// @TODO: Put other LOCK_VERSIONS here
#elif LOCK_VERSION == 3
static const int16_t NUM_ERROR_CORRECTION_CODEWORDS[4] = {
26, 15, 44, 36
};
static const int8_t NUM_ERROR_CORRECTION_BLOCKS[4] = {
1, 1, 2, 2
};
static const uint16_t NUM_RAW_DATA_MODULES = 567;
#else
#error Unsupported LOCK_VERSION (add it...)
#endif
static int max(int a, int b) {
if (a > b) { return a; }
return b;
}
/*
static int abs(int value) {
if (value < 0) { return -value; }
return value;
}
*/
#pragma mark - Mode testing and conversion
static int8_t getAlphanumeric(char c) {
if (c >= '0' && c <= '9') { return (c - '0'); }
if (c >= 'A' && c <= 'Z') { return (c - 'A' + 10); }
switch (c) {
case ' ': return 36;
case '$': return 37;
case '%': return 38;
case '*': return 39;
case '+': return 40;
case '-': return 41;
case '.': return 42;
case '/': return 43;
case ':': return 44;
}
return -1;
}
static bool isAlphanumeric(const char *text, uint16_t length) {
while (length != 0) {
if (getAlphanumeric(text[--length]) == -1) { return false; }
}
return true;
}
static bool isNumeric(const char *text, uint16_t length) {
while (length != 0) {
char c = text[--length];
if (c < '0' || c > '9') { return false; }
}
return true;
}
#pragma mark - Counting
// We store the following tightly packed (less 8) in modeInfo
// <=9 <=26 <= 40
// NUMERIC ( 10, 12, 14);
// ALPHANUMERIC ( 9, 11, 13);
// BYTE ( 8, 16, 16);
static char getModeBits(uint8_t version, uint8_t mode) {
// Note: We use 15 instead of 16; since 15 doesn't exist and we cannot store 16 (8 + 8) in 3 bits
// hex(int("".join(reversed([('00' + bin(x - 8)[2:])[-3:] for x in [10, 9, 8, 12, 11, 15, 14, 13, 15]])), 2))
unsigned int modeInfo = 0x7bbb80a;
#if LOCK_VERSION == 0 || LOCK_VERSION > 9
if (version > 9) { modeInfo >>= 9; }
#endif
#if LOCK_VERSION == 0 || LOCK_VERSION > 26
if (version > 26) { modeInfo >>= 9; }
#endif
char result = 8 + ((modeInfo >> (3 * mode)) & 0x07);
if (result == 15) { result = 16; }
return result;
}
#pragma mark - BitBucket
typedef struct BitBucket {
uint32_t bitOffsetOrWidth;
uint16_t capacityBytes;
uint8_t *data;
} BitBucket;
/*
void bb_dump(BitBucket *bitBuffer) {
printf("Buffer: ");
for (uint32_t i = 0; i < bitBuffer->capacityBytes; i++) {
printf("%02x", bitBuffer->data[i]);
if ((i % 4) == 3) { printf(" "); }
}
printf("\n");
}
*/
static uint16_t bb_getGridSizeBytes(uint8_t size) {
return (((size * size) + 7) / 8);
}
static uint16_t bb_getBufferSizeBytes(uint32_t bits) {
return ((bits + 7) / 8);
}
static void bb_initBuffer(BitBucket *bitBuffer, uint8_t *data, int32_t capacityBytes) {
bitBuffer->bitOffsetOrWidth = 0;
bitBuffer->capacityBytes = capacityBytes;
bitBuffer->data = data;
memset(data, 0, bitBuffer->capacityBytes);
}
static void bb_initGrid(BitBucket *bitGrid, uint8_t *data, uint8_t size) {
bitGrid->bitOffsetOrWidth = size;
bitGrid->capacityBytes = bb_getGridSizeBytes(size);
bitGrid->data = data;
memset(data, 0, bitGrid->capacityBytes);
}
static void bb_appendBits(BitBucket *bitBuffer, uint32_t val, uint8_t length) {
uint32_t offset = bitBuffer->bitOffsetOrWidth;
for (int8_t i = length - 1; i >= 0; i--, offset++) {
bitBuffer->data[offset >> 3] |= ((val >> i) & 1) << (7 - (offset & 7));
}
bitBuffer->bitOffsetOrWidth = offset;
}
/*
void bb_setBits(BitBucket *bitBuffer, uint32_t val, int offset, uint8_t length) {
for (int8_t i = length - 1; i >= 0; i--, offset++) {
bitBuffer->data[offset >> 3] |= ((val >> i) & 1) << (7 - (offset & 7));
}
}
*/
static void bb_setBit(BitBucket *bitGrid, uint8_t x, uint8_t y, bool on) {
uint32_t offset = y * bitGrid->bitOffsetOrWidth + x;
uint8_t mask = 1 << (7 - (offset & 0x07));
if (on) {
bitGrid->data[offset >> 3] |= mask;
} else {
bitGrid->data[offset >> 3] &= ~mask;
}
}
static void bb_invertBit(BitBucket *bitGrid, uint8_t x, uint8_t y, bool invert) {
uint32_t offset = y * bitGrid->bitOffsetOrWidth + x;
uint8_t mask = 1 << (7 - (offset & 0x07));
bool on = ((bitGrid->data[offset >> 3] & (1 << (7 - (offset & 0x07)))) != 0);
if (on ^ invert) {
bitGrid->data[offset >> 3] |= mask;
} else {
bitGrid->data[offset >> 3] &= ~mask;
}
}
static bool bb_getBit(BitBucket *bitGrid, uint8_t x, uint8_t y) {
uint32_t offset = y * bitGrid->bitOffsetOrWidth + x;
return (bitGrid->data[offset >> 3] & (1 << (7 - (offset & 0x07)))) != 0;
}
#pragma mark - Drawing Patterns
// XORs the data modules in this QR Code with the given mask pattern. Due to XOR's mathematical
// properties, calling applyMask(m) twice with the same value is equivalent to no change at all.
// This means it is possible to apply a mask, undo it, and try another mask. Note that a final
// well-formed QR Code symbol needs exactly one mask applied (not zero, not two, etc.).
static void applyMask(BitBucket *modules, BitBucket *isFunction, uint8_t mask) {
uint8_t size = modules->bitOffsetOrWidth;
for (uint8_t y = 0; y < size; y++) {
for (uint8_t x = 0; x < size; x++) {
if (bb_getBit(isFunction, x, y)) { continue; }
bool invert = 0;
switch (mask) {
case 0: invert = (x + y) % 2 == 0; break;
case 1: invert = y % 2 == 0; break;
case 2: invert = x % 3 == 0; break;
case 3: invert = (x + y) % 3 == 0; break;
case 4: invert = (x / 3 + y / 2) % 2 == 0; break;
case 5: invert = x * y % 2 + x * y % 3 == 0; break;
case 6: invert = (x * y % 2 + x * y % 3) % 2 == 0; break;
case 7: invert = ((x + y) % 2 + x * y % 3) % 2 == 0; break;
}
bb_invertBit(modules, x, y, invert);
}
}
}
static void setFunctionModule(BitBucket *modules, BitBucket *isFunction, uint8_t x, uint8_t y, bool on) {
bb_setBit(modules, x, y, on);
bb_setBit(isFunction, x, y, true);
}
// Draws a 9*9 finder pattern including the border separator, with the center module at (x, y).
static void drawFinderPattern(BitBucket *modules, BitBucket *isFunction, uint8_t x, uint8_t y) {
uint8_t size = modules->bitOffsetOrWidth;
for (int8_t i = -4; i <= 4; i++) {
for (int8_t j = -4; j <= 4; j++) {
uint8_t dist = max(abs(i), abs(j)); // Chebyshev/infinity norm
int16_t xx = x + j, yy = y + i;
if (0 <= xx && xx < size && 0 <= yy && yy < size) {
setFunctionModule(modules, isFunction, xx, yy, dist != 2 && dist != 4);
}
}
}
}
// Draws a 5*5 alignment pattern, with the center module at (x, y).
static void drawAlignmentPattern(BitBucket *modules, BitBucket *isFunction, uint8_t x, uint8_t y) {
for (int8_t i = -2; i <= 2; i++) {
for (int8_t j = -2; j <= 2; j++) {
setFunctionModule(modules, isFunction, x + j, y + i, max(abs(i), abs(j)) != 1);
}
}
}
// Draws two copies of the format bits (with its own error correction code)
// based on the given mask and this object's error correction level field.
static void drawFormatBits(BitBucket *modules, BitBucket *isFunction, uint8_t ecc, uint8_t mask) {
uint8_t size = modules->bitOffsetOrWidth;
// Calculate error correction code and pack bits
uint32_t data = ecc << 3 | mask; // errCorrLvl is uint2, mask is uint3
uint32_t rem = data;
for (int i = 0; i < 10; i++) {
rem = (rem << 1) ^ ((rem >> 9) * 0x537);
}
data = data << 10 | rem;
data ^= 0x5412; // uint15
// Draw first copy
for (uint8_t i = 0; i <= 5; i++) {
setFunctionModule(modules, isFunction, 8, i, ((data >> i) & 1) != 0);
}
setFunctionModule(modules, isFunction, 8, 7, ((data >> 6) & 1) != 0);
setFunctionModule(modules, isFunction, 8, 8, ((data >> 7) & 1) != 0);
setFunctionModule(modules, isFunction, 7, 8, ((data >> 8) & 1) != 0);
for (int8_t i = 9; i < 15; i++) {
setFunctionModule(modules, isFunction, 14 - i, 8, ((data >> i) & 1) != 0);
}
// Draw second copy
for (int8_t i = 0; i <= 7; i++) {
setFunctionModule(modules, isFunction, size - 1 - i, 8, ((data >> i) & 1) != 0);
}
for (int8_t i = 8; i < 15; i++) {
setFunctionModule(modules, isFunction, 8, size - 15 + i, ((data >> i) & 1) != 0);
}
setFunctionModule(modules, isFunction, 8, size - 8, true);
}
// Draws two copies of the version bits (with its own error correction code),
// based on this object's version field (which only has an effect for 7 <= version <= 40).
static void drawVersion(BitBucket *modules, BitBucket *isFunction, uint8_t version) {
int8_t size = modules->bitOffsetOrWidth;
#if LOCK_VERSION != 0 && LOCK_VERSION < 7
return;
#else
if (version < 7) { return; }
// Calculate error correction code and pack bits
uint32_t rem = version; // version is uint6, in the range [7, 40]
for (uint8_t i = 0; i < 12; i++) {
rem = (rem << 1) ^ ((rem >> 11) * 0x1F25);
}
uint32_t data = version << 12 | rem; // uint18
// Draw two copies
for (uint8_t i = 0; i < 18; i++) {
bool bit = ((data >> i) & 1) != 0;
uint8_t a = size - 11 + i % 3, b = i / 3;
setFunctionModule(modules, isFunction, a, b, bit);
setFunctionModule(modules, isFunction, b, a, bit);
}
#endif
}
static void drawFunctionPatterns(BitBucket *modules, BitBucket *isFunction, uint8_t version, uint8_t ecc) {
uint8_t size = modules->bitOffsetOrWidth;
// Draw the horizontal and vertical timing patterns
for (uint8_t i = 0; i < size; i++) {
setFunctionModule(modules, isFunction, 6, i, i % 2 == 0);
setFunctionModule(modules, isFunction, i, 6, i % 2 == 0);
}
// Draw 3 finder patterns (all corners except bottom right; overwrites some timing modules)
drawFinderPattern(modules, isFunction, 3, 3);
drawFinderPattern(modules, isFunction, size - 4, 3);
drawFinderPattern(modules, isFunction, 3, size - 4);
#if LOCK_VERSION == 0 || LOCK_VERSION > 1
if (version > 1) {
// Draw the numerous alignment patterns
uint8_t alignCount = version / 7 + 2;
uint8_t step;
if (version != 32) {
step = (version * 4 + alignCount * 2 + 1) / (2 * alignCount - 2) * 2; // ceil((size - 13) / (2*numAlign - 2)) * 2
} else { // C-C-C-Combo breaker!
step = 26;
}
uint8_t alignPositionIndex = alignCount - 1;
uint8_t alignPosition[alignCount];
alignPosition[0] = 6;
uint8_t size = version * 4 + 17;
for (uint8_t i = 0, pos = size - 7; i < alignCount - 1; i++, pos -= step) {
alignPosition[alignPositionIndex--] = pos;
}
for (uint8_t i = 0; i < alignCount; i++) {
for (uint8_t j = 0; j < alignCount; j++) {
if ((i == 0 && j == 0) || (i == 0 && j == alignCount - 1) || (i == alignCount - 1 && j == 0)) {
continue; // Skip the three finder corners
} else {
drawAlignmentPattern(modules, isFunction, alignPosition[i], alignPosition[j]);
}
}
}
}
#endif
// Draw configuration data
drawFormatBits(modules, isFunction, ecc, 0); // Dummy mask value; overwritten later in the constructor
drawVersion(modules, isFunction, version);
}
// Draws the given sequence of 8-bit codewords (data and error correction) onto the entire
// data area of this QR Code symbol. Function modules need to be marked off before this is called.
static void drawCodewords(BitBucket *modules, BitBucket *isFunction, BitBucket *codewords) {
uint32_t bitLength = codewords->bitOffsetOrWidth;
uint8_t *data = codewords->data;
uint8_t size = modules->bitOffsetOrWidth;
// Bit index into the data
uint32_t i = 0;
// Do the funny zigzag scan
for (int16_t right = size - 1; right >= 1; right -= 2) { // Index of right column in each column pair
if (right == 6) { right = 5; }
for (uint8_t vert = 0; vert < size; vert++) { // Vertical counter
for (int j = 0; j < 2; j++) {
uint8_t x = right - j; // Actual x coordinate
bool upwards = ((right & 2) == 0) ^ (x < 6);
uint8_t y = upwards ? size - 1 - vert : vert; // Actual y coordinate
if (!bb_getBit(isFunction, x, y) && i < bitLength) {
bb_setBit(modules, x, y, ((data[i >> 3] >> (7 - (i & 7))) & 1) != 0);
i++;
}
// If there are any remainder bits (0 to 7), they are already
// set to 0/false/white when the grid of modules was initialized
}
}
}
}
#pragma mark - Penalty Calculation
#define PENALTY_N1 3
#define PENALTY_N2 3
#define PENALTY_N3 40
#define PENALTY_N4 10
// Calculates and returns the penalty score based on state of this QR Code's current modules.
// This is used by the automatic mask choice algorithm to find the mask pattern that yields the lowest score.
// @TODO: This can be optimized by working with the bytes instead of bits.
static uint32_t getPenaltyScore(BitBucket *modules) {
uint32_t result = 0;
uint8_t size = modules->bitOffsetOrWidth;
// Adjacent modules in row having same color
for (uint8_t y = 0; y < size; y++) {
bool colorX = bb_getBit(modules, 0, y);
for (uint8_t x = 1, runX = 1; x < size; x++) {
bool cx = bb_getBit(modules, x, y);
if (cx != colorX) {
colorX = cx;
runX = 1;
} else {
runX++;
if (runX == 5) {
result += PENALTY_N1;
} else if (runX > 5) {
result++;
}
}
}
}
// Adjacent modules in column having same color
for (uint8_t x = 0; x < size; x++) {
bool colorY = bb_getBit(modules, x, 0);
for (uint8_t y = 1, runY = 1; y < size; y++) {
bool cy = bb_getBit(modules, x, y);
if (cy != colorY) {
colorY = cy;
runY = 1;
} else {
runY++;
if (runY == 5) {
result += PENALTY_N1;
} else if (runY > 5) {
result++;
}
}
}
}
uint16_t black = 0;
for (uint8_t y = 0; y < size; y++) {
uint16_t bitsRow = 0, bitsCol = 0;
for (uint8_t x = 0; x < size; x++) {
bool color = bb_getBit(modules, x, y);
// 2*2 blocks of modules having same color
if (x > 0 && y > 0) {
bool colorUL = bb_getBit(modules, x - 1, y - 1);
bool colorUR = bb_getBit(modules, x, y - 1);
bool colorL = bb_getBit(modules, x - 1, y);
if (color == colorUL && color == colorUR && color == colorL) {
result += PENALTY_N2;
}
}
// Finder-like pattern in rows and columns
bitsRow = ((bitsRow << 1) & 0x7FF) | color;
bitsCol = ((bitsCol << 1) & 0x7FF) | bb_getBit(modules, y, x);
// Needs 11 bits accumulated
if (x >= 10) {
if (bitsRow == 0x05D || bitsRow == 0x5D0) {
result += PENALTY_N3;
}
if (bitsCol == 0x05D || bitsCol == 0x5D0) {
result += PENALTY_N3;
}
}
// Balance of black and white modules
if (color) { black++; }
}
}
// Find smallest k such that (45-5k)% <= dark/total <= (55+5k)%
uint16_t total = size * size;
for (uint16_t k = 0; black * 20 < (9 - k) * total || black * 20 > (11 + k) * total; k++) {
result += PENALTY_N4;
}
return result;
}
#pragma mark - Reed-Solomon Generator
static uint8_t rs_multiply(uint8_t x, uint8_t y) {
// Russian peasant multiplication
// See: https://en.wikipedia.org/wiki/Ancient_Egyptian_multiplication
uint16_t z = 0;
for (int8_t i = 7; i >= 0; i--) {
z = (z << 1) ^ ((z >> 7) * 0x11D);
z ^= ((y >> i) & 1) * x;
}
return z;
}
static void rs_init(uint8_t degree, uint8_t *coeff) {
memset(coeff, 0, degree);
coeff[degree - 1] = 1;
// Compute the product polynomial (x - r^0) * (x - r^1) * (x - r^2) * ... * (x - r^{degree-1}),
// drop the highest term, and store the rest of the coefficients in order of descending powers.
// Note that r = 0x02, which is a generator element of this field GF(2^8/0x11D).
uint16_t root = 1;
for (uint8_t i = 0; i < degree; i++) {
// Multiply the current product by (x - r^i)
for (uint8_t j = 0; j < degree; j++) {
coeff[j] = rs_multiply(coeff[j], root);
if (j + 1 < degree) {
coeff[j] ^= coeff[j + 1];
}
}
root = (root << 1) ^ ((root >> 7) * 0x11D); // Multiply by 0x02 mod GF(2^8/0x11D)
}
}
static void rs_getRemainder(uint8_t degree, uint8_t *coeff, uint8_t *data, uint8_t length, uint8_t *result, uint8_t stride) {
// Compute the remainder by performing polynomial division
//for (uint8_t i = 0; i < degree; i++) { result[] = 0; }
//memset(result, 0, degree);
for (uint8_t i = 0; i < length; i++) {
uint8_t factor = data[i] ^ result[0];
for (uint8_t j = 1; j < degree; j++) {
result[(j - 1) * stride] = result[j * stride];
}
result[(degree - 1) * stride] = 0;
for (uint8_t j = 0; j < degree; j++) {
result[j * stride] ^= rs_multiply(coeff[j], factor);
}
}
}
#pragma mark - QrCode
static int8_t encodeDataCodewords(BitBucket *dataCodewords, const uint8_t *text, uint16_t length, uint8_t version) {
int8_t mode = MODE_BYTE;
if (isNumeric((char*)text, length)) {
mode = MODE_NUMERIC;
bb_appendBits(dataCodewords, 1 << MODE_NUMERIC, 4);
bb_appendBits(dataCodewords, length, getModeBits(version, MODE_NUMERIC));
uint16_t accumData = 0;
uint8_t accumCount = 0;
for (uint16_t i = 0; i < length; i++) {
accumData = accumData * 10 + ((char)(text[i]) - '0');
accumCount++;
if (accumCount == 3) {
bb_appendBits(dataCodewords, accumData, 10);
accumData = 0;
accumCount = 0;
}
}
// 1 or 2 digits remaining
if (accumCount > 0) {
bb_appendBits(dataCodewords, accumData, accumCount * 3 + 1);
}
} else if (isAlphanumeric((char*)text, length)) {
mode = MODE_ALPHANUMERIC;
bb_appendBits(dataCodewords, 1 << MODE_ALPHANUMERIC, 4);
bb_appendBits(dataCodewords, length, getModeBits(version, MODE_ALPHANUMERIC));
uint16_t accumData = 0;
uint8_t accumCount = 0;
for (uint16_t i = 0; i < length; i++) {
accumData = accumData * 45 + getAlphanumeric((char)(text[i]));
accumCount++;
if (accumCount == 2) {
bb_appendBits(dataCodewords, accumData, 11);
accumData = 0;
accumCount = 0;
}
}
// 1 character remaining
if (accumCount > 0) {
bb_appendBits(dataCodewords, accumData, 6);
}
} else {
bb_appendBits(dataCodewords, 1 << MODE_BYTE, 4);
bb_appendBits(dataCodewords, length, getModeBits(version, MODE_BYTE));
for (uint16_t i = 0; i < length; i++) {
bb_appendBits(dataCodewords, (char)(text[i]), 8);
}
}
//bb_setBits(dataCodewords, length, 4, getModeBits(version, mode));
return mode;
}
static void performErrorCorrection(uint8_t version, uint8_t ecc, BitBucket *data) {
// See: http://www.thonky.com/qr-code-tutorial/structure-final-message
#if LOCK_VERSION == 0
uint8_t numBlocks = NUM_ERROR_CORRECTION_BLOCKS[ecc][version - 1];
uint16_t totalEcc = NUM_ERROR_CORRECTION_CODEWORDS[ecc][version - 1];
uint16_t moduleCount = NUM_RAW_DATA_MODULES[version - 1];
#else
uint8_t numBlocks = NUM_ERROR_CORRECTION_BLOCKS[ecc];
uint16_t totalEcc = NUM_ERROR_CORRECTION_CODEWORDS[ecc];
uint16_t moduleCount = NUM_RAW_DATA_MODULES;
#endif
uint8_t blockEccLen = totalEcc / numBlocks;
uint8_t numShortBlocks = numBlocks - moduleCount / 8 % numBlocks;
uint8_t shortBlockLen = moduleCount / 8 / numBlocks;
uint8_t shortDataBlockLen = shortBlockLen - blockEccLen;
uint8_t result[data->capacityBytes];
memset(result, 0, sizeof(result));
uint8_t coeff[blockEccLen];
rs_init(blockEccLen, coeff);
uint16_t offset = 0;
uint8_t *dataBytes = data->data;
// Interleave all short blocks
for (uint8_t i = 0; i < shortDataBlockLen; i++) {
uint16_t index = i;
uint8_t stride = shortDataBlockLen;
for (uint8_t blockNum = 0; blockNum < numBlocks; blockNum++) {
result[offset++] = dataBytes[index];
#if LOCK_VERSION == 0 || LOCK_VERSION >= 5
if (blockNum == numShortBlocks) { stride++; }
#endif
index += stride;
}
}
// Version less than 5 only have short blocks
#if LOCK_VERSION == 0 || LOCK_VERSION >= 5
{
// Interleave long blocks
uint16_t index = shortDataBlockLen * (numShortBlocks + 1);
uint8_t stride = shortDataBlockLen;
for (uint8_t blockNum = 0; blockNum < numBlocks - numShortBlocks; blockNum++) {
result[offset++] = dataBytes[index];
if (blockNum == 0) { stride++; }
index += stride;
}
}
#endif
// Add all ecc blocks, interleaved
uint8_t blockSize = shortDataBlockLen;
for (uint8_t blockNum = 0; blockNum < numBlocks; blockNum++) {
#if LOCK_VERSION == 0 || LOCK_VERSION >= 5
if (blockNum == numShortBlocks) { blockSize++; }
#endif
rs_getRemainder(blockEccLen, coeff, dataBytes, blockSize, &result[offset + blockNum], numBlocks);
dataBytes += blockSize;
}
memcpy(data->data, result, data->capacityBytes);
data->bitOffsetOrWidth = moduleCount;
}
// We store the Format bits tightly packed into a single byte (each of the 4 modes is 2 bits)
// The format bits can be determined by ECC_FORMAT_BITS >> (2 * ecc)
static const uint8_t ECC_FORMAT_BITS = (0x02 << 6) | (0x03 << 4) | (0x00 << 2) | (0x01 << 0);
#pragma mark - Public QRCode functions
uint16_t qrcode_getBufferSize(uint8_t version) {
return bb_getGridSizeBytes(4 * version + 17);
}
// @TODO: Return error if data is too big.
int8_t qrcode_initBytes(QRCode *qrcode, uint8_t *modules, uint8_t version, uint8_t ecc, uint8_t *data, uint16_t length) {
uint8_t size = version * 4 + 17;
qrcode->version = version;
qrcode->size = size;
qrcode->ecc = ecc;
qrcode->modules = modules;
uint8_t eccFormatBits = (ECC_FORMAT_BITS >> (2 * ecc)) & 0x03;
#if LOCK_VERSION == 0
uint16_t moduleCount = NUM_RAW_DATA_MODULES[version - 1];
uint16_t dataCapacity = moduleCount / 8 - NUM_ERROR_CORRECTION_CODEWORDS[eccFormatBits][version - 1];
#else
version = LOCK_VERSION;
uint16_t moduleCount = NUM_RAW_DATA_MODULES;
uint16_t dataCapacity = moduleCount / 8 - NUM_ERROR_CORRECTION_CODEWORDS[eccFormatBits];
#endif
struct BitBucket codewords;
uint8_t codewordBytes[bb_getBufferSizeBytes(moduleCount)];
bb_initBuffer(&codewords, codewordBytes, (int32_t)sizeof(codewordBytes));
// Place the data code words into the buffer
int8_t mode = encodeDataCodewords(&codewords, data, length, version);
if (mode < 0) { return -1; }
qrcode->mode = mode;
// Add terminator and pad up to a byte if applicable
uint32_t padding = (dataCapacity * 8) - codewords.bitOffsetOrWidth;
if (padding > 4) { padding = 4; }
bb_appendBits(&codewords, 0, padding);
bb_appendBits(&codewords, 0, (8 - codewords.bitOffsetOrWidth % 8) % 8);
// Pad with alternate bytes until data capacity is reached
for (uint8_t padByte = 0xEC; codewords.bitOffsetOrWidth < (dataCapacity * 8); padByte ^= 0xEC ^ 0x11) {
bb_appendBits(&codewords, padByte, 8);
}
BitBucket modulesGrid;
bb_initGrid(&modulesGrid, modules, size);
BitBucket isFunctionGrid;
uint8_t isFunctionGridBytes[bb_getGridSizeBytes(size)];
bb_initGrid(&isFunctionGrid, isFunctionGridBytes, size);
// Draw function patterns, draw all codewords, do masking
drawFunctionPatterns(&modulesGrid, &isFunctionGrid, version, eccFormatBits);
performErrorCorrection(version, eccFormatBits, &codewords);
drawCodewords(&modulesGrid, &isFunctionGrid, &codewords);
// Find the best (lowest penalty) mask
uint8_t mask = 0;
int32_t minPenalty = INT32_MAX;
for (uint8_t i = 0; i < 8; i++) {
drawFormatBits(&modulesGrid, &isFunctionGrid, eccFormatBits, i);
applyMask(&modulesGrid, &isFunctionGrid, i);
int penalty = getPenaltyScore(&modulesGrid);
if (penalty < minPenalty) {
mask = i;
minPenalty = penalty;
}
applyMask(&modulesGrid, &isFunctionGrid, i); // Undoes the mask due to XOR
}
qrcode->mask = mask;
// Overwrite old format bits
drawFormatBits(&modulesGrid, &isFunctionGrid, eccFormatBits, mask);
// Apply the final choice of mask
applyMask(&modulesGrid, &isFunctionGrid, mask);
return 0;
}
int8_t qrcode_initText(QRCode *qrcode, uint8_t *modules, uint8_t version, uint8_t ecc, const char *data) {
return qrcode_initBytes(qrcode, modules, version, ecc, (uint8_t*)data, strlen(data));
}
bool qrcode_getModule(QRCode *qrcode, uint8_t x, uint8_t y) {
if (x < 0 || x >= qrcode->size || y < 0 || y >= qrcode->size) {
return false;
}
uint32_t offset = y * qrcode->size + x;
return (qrcode->modules[offset >> 3] & (1 << (7 - (offset & 0x07)))) != 0;
}
/*
uint8_t qrcode_getHexLength(QRCode *qrcode) {
return ((qrcode->size * qrcode->size) + 7) / 4;
}
void qrcode_getHex(QRCode *qrcode, char *result) {
}
*/
+99
View File
@@ -0,0 +1,99 @@
/**
* The MIT License (MIT)
*
* This library is written and maintained by Richard Moore.
* Major parts were derived from Project Nayuki's library.
*
* Copyright (c) 2017 Richard Moore (https://github.com/ricmoo/QRCode)
* Copyright (c) 2017 Project Nayuki (https://www.nayuki.io/page/qr-code-generator-library)
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
/**
* Special thanks to Nayuki (https://www.nayuki.io/) from which this library was
* heavily inspired and compared against.
*
* See: https://github.com/nayuki/QR-Code-generator/tree/master/cpp
*/
#ifndef __QRCODEGEN_H_
#define __QRCODEGEN_H_
#ifndef __cplusplus
typedef unsigned char bool;
static const bool false = 0;
static const bool true = 1;
#endif
#include <stdint.h>
// QR Code Format Encoding
#define MODE_NUMERIC 0
#define MODE_ALPHANUMERIC 1
#define MODE_BYTE 2
// Error Correction Code Levels
#define ECC_LOW 0
#define ECC_MEDIUM 1
#define ECC_QUARTILE 2
#define ECC_HIGH 3
// If set to non-zero, this library can ONLY produce QR codes at that version
// This saves a lot of dynamic memory, as the codeword tables are skipped
#ifndef LOCK_VERSION
#define LOCK_VERSION 0
#endif
typedef struct QRCode {
uint8_t version;
uint8_t size;
uint8_t ecc;
uint8_t mode;
uint8_t mask;
uint8_t *modules;
} QRCode;
#ifdef __cplusplus
extern "C"{
#endif /* __cplusplus */
uint16_t qrcode_getBufferSize(uint8_t version);
int8_t qrcode_initText(QRCode *qrcode, uint8_t *modules, uint8_t version, uint8_t ecc, const char *data);
int8_t qrcode_initBytes(QRCode *qrcode, uint8_t *modules, uint8_t version, uint8_t ecc, uint8_t *data, uint16_t length);
bool qrcode_getModule(QRCode *qrcode, uint8_t x, uint8_t y);
#ifdef __cplusplus
}
#endif /* __cplusplus */
#endif /* __QRCODEGEN_H_ */
+3 -1
View File
@@ -253,14 +253,16 @@ void GeoMapView::focus() {
nav_.display_modal("No map", "No world_map.bin file in\n/ADSB/ directory", ABORT, nullptr);
}
void GeoMapView::update_position(float lat, float lon, uint16_t angle) {
void GeoMapView::update_position(float lat, float lon, uint16_t angle, int32_t altitude) {
lat_ = lat;
lon_ = lon;
altitude_ = altitude;
// Stupid hack to avoid an event loop
geopos.set_report_change(false);
geopos.set_lat(lat_);
geopos.set_lon(lon_);
geopos.set_altitude(altitude_);
geopos.set_report_change(true);
geomap.set_angle(angle);
+1 -1
View File
@@ -177,7 +177,7 @@ public:
void focus() override;
void update_position(float lat, float lon, uint16_t angle);
void update_position(float lat, float lon, uint16_t angle, int32_t altitude);
std::string title() const override { return "Map view"; };
+100
View File
@@ -0,0 +1,100 @@
/*
* Copyright (C) 2015 Jared Boone, ShareBrained Technology, Inc.
* Copyright (C) 2017 Furrtek
*
* This file is part of PortaPack.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; see the file COPYING. If not, write to
* the Free Software Foundation, Inc., 51 Franklin Street,
* Boston, MA 02110-1301, USA.
*/
#include "ui_qrcode.hpp"
#include "qrcodegen.hpp"
#include "portapack.hpp"
#include <cstring>
#include <stdio.h>
using namespace portapack;
#include "string_format.hpp"
#include "complex.hpp"
namespace ui {
QRCodeImage::QRCodeImage(
Rect parent_rect
) : Widget { parent_rect }
{
}
void QRCodeImage::paint(Painter& painter) {
// The structure to manage the QR code
QRCode qrcode;
int qr_version = 10; // bigger versions aren't handled very well
// Allocate a chunk of memory to store the QR code
uint8_t qrcodeBytes[qrcode_getBufferSize(qr_version)];
qrcode_initText(&qrcode, qrcodeBytes, qr_version, ECC_HIGH, qr_text_);
display.fill_rectangle(Rect(92, 97, 63, 63), Color::white());
for (uint8_t y = 0; y < qrcode.size; y++) {
for (uint8_t x = 0; x < qrcode.size; x++) {
if (qrcode_getModule(&qrcode, x, y)) {
display.draw_pixel(Point(95+x,100+y), Color::black());
}
}
}
}
void QRCodeView::focus() {
button_close.focus();
}
QRCodeView::~QRCodeView() {
if (on_close_)
on_close_();
}
QRCodeView::QRCodeView(
NavigationView& nav,
const char * qr_text,
const std::function<void(void)> on_close
) : nav_ (nav),
on_close_(on_close)
{
add_children({
&text_qr,
&qr_code,
&button_close});
text_qr.set(qr_text);
qr_code.set_text(qr_text);
button_close.on_select = [&nav](Button&){
nav.pop();
};
}
} /* namespace ui */
+90
View File
@@ -0,0 +1,90 @@
/*
* Copyright (C) 2015 Jared Boone, ShareBrained Technology, Inc.
* Copyright (C) 2017 Furrtek
*
* This file is part of PortaPack.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; see the file COPYING. If not, write to
* the Free Software Foundation, Inc., 51 Franklin Street,
* Boston, MA 02110-1301, USA.
*/
#ifndef __QRCODE_H__
#define __QRCODE_H__
#include "ui.hpp"
#include "ui_navigation.hpp"
#include "ui_font_fixed_8x16.hpp"
#include "qrcodegen.hpp"
#include "portapack.hpp"
namespace ui {
class QRCodeImage : public Widget {
public:
QRCodeImage(Rect parent_rect);
void set_text(const char * qr_text) {
qr_text_ = qr_text;
}
void paint(Painter& painter) override;
private:
const char * qr_text_ ;
};
class QRCodeView : public View {
public:
QRCodeView(
NavigationView& nav,
const char * qr_text,
const std::function<void(void)> on_close = nullptr
);
~QRCodeView();
QRCodeView(const QRCodeView&) = delete;
QRCodeView(QRCodeView&&) = delete;
QRCodeView& operator=(const QRCodeView&) = delete;
QRCodeView& operator=(QRCodeView&&) = delete;
std::string title() const override { return "QR code"; };
void focus() override;
private:
NavigationView& nav_;
std::function<void(void)> on_close_ { nullptr };
QRCodeImage qr_code {
{ 50, 100, 100, 100 }
};
Text text_qr {
{ 0 * 8, 10 * 16, 32 * 8, 1 * 8 },
"-"
};
Button button_close {
{ 9 * 8, 15 * 16, 12 * 8, 3 * 16 },
"Back"
};
};
} /* namespace ui */
#endif
+249 -252
View File
@@ -45,255 +45,252 @@
using namespace sd_card;
namespace ui {
namespace ui
{
enum modal_t {
INFO = 0,
YESNO,
YESCANCEL,
ABORT
};
class NavigationView : public View {
public:
std::function<void(const View&)> on_view_changed { };
NavigationView() = default;
NavigationView(const NavigationView&) = delete;
NavigationView(NavigationView&&) = delete;
NavigationView& operator=(const NavigationView&) = delete;
NavigationView& operator=(NavigationView&&) = delete;
bool is_top() const;
template<class T, class... Args>
T* push(Args&&... args) {
return reinterpret_cast<T*>(push_view(std::unique_ptr<View>(new T(*this, std::forward<Args>(args)...))));
}
template<class T, class... Args>
T* replace(Args&&... args) {
pop();
return reinterpret_cast<T*>(push_view(std::unique_ptr<View>(new T(*this, std::forward<Args>(args)...))));
}
void push(View* v);
void replace(View* v);
void pop();
void pop_modal();
void display_modal(const std::string& title, const std::string& message);
void display_modal(const std::string& title, const std::string& message, const modal_t type, const std::function<void(bool)> on_choice = nullptr);
void focus() override;
private:
std::vector<std::unique_ptr<View>> view_stack { };
Widget* modal_view { nullptr };
Widget* view() const;
void free_view();
void update_view();
View* push_view(std::unique_ptr<View> new_view);
};
class SystemStatusView : public View {
public:
std::function<void(void)> on_back { };
SystemStatusView(NavigationView& nav);
void set_back_enabled(bool new_value);
void set_title_image_enabled(bool new_value);
void set_title(const std::string new_value);
private:
static constexpr auto default_title = "";
NavigationView& nav_;
Rectangle backdrop {
{ 0 * 8, 0 * 16, 240, 16 },
Color::dark_grey()
enum modal_t
{
INFO = 0,
YESNO,
YESCANCEL,
ABORT
};
ImageButton button_back {
{ 2, 0 * 16, 16, 16 },
&bitmap_icon_previous,
Color::white(),
Color::dark_grey()
class NavigationView : public View
{
public:
std::function<void(const View &)> on_view_changed{};
NavigationView() = default;
NavigationView(const NavigationView &) = delete;
NavigationView(NavigationView &&) = delete;
NavigationView &operator=(const NavigationView &) = delete;
NavigationView &operator=(NavigationView &&) = delete;
bool is_top() const;
template <class T, class... Args>
T *push(Args &&...args)
{
return reinterpret_cast<T *>(push_view(std::unique_ptr<View>(new T(*this, std::forward<Args>(args)...))));
}
template <class T, class... Args>
T *replace(Args &&...args)
{
pop();
return reinterpret_cast<T *>(push_view(std::unique_ptr<View>(new T(*this, std::forward<Args>(args)...))));
}
void push(View *v);
void replace(View *v);
void pop();
void pop_modal();
void display_modal(const std::string &title, const std::string &message);
void display_modal(const std::string &title, const std::string &message, const modal_t type, const std::function<void(bool)> on_choice = nullptr);
void focus() override;
private:
std::vector<std::unique_ptr<View>> view_stack{};
Widget *modal_view{nullptr};
Widget *view() const;
void free_view();
void update_view();
View *push_view(std::unique_ptr<View> new_view);
};
Text title {
{ 20, 0, 14 * 8, 1 * 16 },
default_title,
};
class SystemStatusView : public View
{
public:
std::function<void(void)> on_back{};
ImageButton button_title {
{2, 0, 80, 16},
&bitmap_titlebar_image,
Color::white(),
Color::dark_grey()
};
SystemStatusView(NavigationView &nav);
ImageButton button_speaker {
{ 17 * 8, 0, 2 * 8, 1 * 16 },
&bitmap_icon_speaker_mute,
Color::light_grey(),
Color::dark_grey()
};
ImageButton button_stealth {
{ 19 * 8, 0, 2 * 8, 1 * 16 },
&bitmap_icon_stealth,
Color::light_grey(),
Color::dark_grey()
};
/*ImageButton button_textentry {
void set_back_enabled(bool new_value);
void set_title_image_enabled(bool new_value);
void set_title(const std::string new_value);
private:
static constexpr auto default_title = "";
NavigationView &nav_;
Rectangle backdrop{
{0 * 8, 0 * 16, 240, 16},
Color::dark_grey()};
ImageButton button_back{
{2, 0 * 16, 16, 16},
&bitmap_icon_previous,
Color::white(),
Color::dark_grey()};
Text title{
{20, 0, 14 * 8, 1 * 16},
default_title,
};
ImageButton button_title{
{2, 0, 80, 16},
&bitmap_titlebar_image,
Color::white(),
Color::dark_grey()};
ImageButton button_speaker{
{17 * 8, 0, 2 * 8, 1 * 16},
&bitmap_icon_speaker_mute,
Color::light_grey(),
Color::dark_grey()};
ImageButton button_stealth{
{19 * 8, 0, 2 * 8, 1 * 16},
&bitmap_icon_stealth,
Color::light_grey(),
Color::dark_grey()};
/*ImageButton button_textentry {
{ 170, 0, 2 * 8, 1 * 16 },
&bitmap_icon_unistroke,
Color::white(),
Color::dark_grey()
};*/
ImageButton button_camera {
{ 21 * 8, 0, 2 * 8, 1 * 16 },
&bitmap_icon_camera,
Color::white(),
Color::dark_grey()
ImageButton button_camera{
{21 * 8, 0, 2 * 8, 1 * 16},
&bitmap_icon_camera,
Color::white(),
Color::dark_grey()};
ImageButton button_sleep{
{23 * 8, 0, 2 * 8, 1 * 16},
&bitmap_icon_sleep,
Color::white(),
Color::dark_grey()};
ImageButton button_bias_tee{
{25 * 8, 0, 12, 1 * 16},
&bitmap_icon_biast_off,
Color::light_grey(),
Color::dark_grey()};
ImageButton button_clock_status{
{27 * 8, 0 * 16, 2 * 8, 1 * 16},
&bitmap_icon_clk_int,
Color::light_grey(),
Color::dark_grey()};
SDCardStatusView sd_card_status_view{
{28 * 8, 0 * 16, 2 * 8, 1 * 16}};
void on_speaker();
void on_stealth();
void on_bias_tee();
//void on_textentry();
void on_camera();
void on_title();
void refresh();
void on_clk();
MessageHandlerRegistration message_handler_refresh{
Message::ID::StatusRefresh,
[this](const Message *const p)
{
(void)p;
this->refresh();
}};
};
ImageButton button_sleep {
{ 23 * 8, 0, 2 * 8, 1 * 16 },
&bitmap_icon_sleep,
Color::white(),
Color::dark_grey()
};
ImageButton button_bias_tee {
{ 25 * 8, 0, 12, 1 * 16 },
&bitmap_icon_biast_off,
Color::light_grey(),
Color::dark_grey()
};
ImageButton button_clock_status {
{ 27 * 8, 0 * 16, 2 * 8, 1 * 16 },
&bitmap_icon_clk_int,
Color::light_grey(),
Color::dark_grey()
};
SDCardStatusView sd_card_status_view {
{ 28 * 8, 0 * 16, 2 * 8, 1 * 16 }
class InformationView : public View
{
public:
InformationView(NavigationView &nav);
void refresh();
private:
static constexpr auto version_string = "v1.4.3";
NavigationView &nav_;
Rectangle backdrop{
{0, 0 * 16, 240, 16},
{33, 33, 33}};
Text version{
{2, 0, 11 * 8, 16},
version_string};
LiveDateTime ltime{
{86, 0, 19 * 8, 16}};
};
void on_speaker();
void on_stealth();
void on_bias_tee();
//void on_textentry();
void on_camera();
void on_title();
void refresh();
void on_clk();
MessageHandlerRegistration message_handler_refresh {
Message::ID::StatusRefresh,
[this](const Message* const p) {
(void)p;
this->refresh();
}
};
};
class BMPView : public View
{
public:
BMPView(NavigationView &nav);
void paint(Painter &) override;
void focus() override;
class InformationView : public View {
public:
InformationView(NavigationView& nav);
void refresh();
private:
static constexpr auto version_string = "v1.4.2";
NavigationView& nav_;
private:
Text text_info{
{4 * 8, 284, 20 * 8, 16},
"Version " VERSION_STRING};
Rectangle backdrop {
{ 0, 0 * 16, 240, 16 },
{33, 33, 33}
Button button_done{
{240, 0, 1, 1},
""};
};
Text version {
{2, 0, 11 * 8, 16},
version_string
class ReceiversMenuView : public BtnGridView
{
public:
ReceiversMenuView(NavigationView &nav);
std::string title() const override { return "Receivers"; };
};
LiveDateTime ltime {
{86, 0, 19 * 8, 16}
class TransmittersMenuView : public BtnGridView
{
public:
TransmittersMenuView(NavigationView &nav);
std::string title() const override { return "Transmitters"; };
};
};
class BMPView : public View {
public:
BMPView(NavigationView& nav);
void paint(Painter&) override;
void focus() override;
private:
Text text_info {
{ 4*8, 284, 20 * 8, 16 },
"Version " VERSION_STRING
class UtilitiesMenuView : public BtnGridView
{
public:
UtilitiesMenuView(NavigationView &nav);
std::string title() const override { return "Utilities"; };
};
Button button_done {
{ 240, 0, 1, 1 },
""
class SystemMenuView : public BtnGridView
{
public:
SystemMenuView(NavigationView &nav);
private:
void hackrf_mode(NavigationView &nav);
};
};
class ReceiversMenuView : public BtnGridView {
public:
ReceiversMenuView(NavigationView& nav);
std::string title() const override { return "Receivers"; };
};
class SystemView : public View
{
public:
SystemView(
Context &context,
const Rect parent_rect);
class TransmittersMenuView : public BtnGridView {
public:
TransmittersMenuView(NavigationView& nav);
std::string title() const override { return "Transmitters"; };
};
Context &context() const override;
class UtilitiesMenuView : public BtnGridView {
public:
UtilitiesMenuView(NavigationView& nav);
std::string title() const override { return "Utilities"; };
};
private:
SystemStatusView status_view{navigation_view};
InformationView info_view{navigation_view};
NavigationView navigation_view{};
Context &context_;
};
class SystemMenuView : public BtnGridView {
public:
SystemMenuView(NavigationView& nav);
private:
void hackrf_mode(NavigationView& nav);
};
class SystemView : public View {
public:
SystemView(
Context& context,
const Rect parent_rect
);
Context& context() const override;
private:
SystemStatusView status_view { navigation_view };
InformationView info_view { navigation_view };
NavigationView navigation_view { };
Context& context_;
};
/*class NotImplementedView : public View {
/*class NotImplementedView : public View {
public:
NotImplementedView(NavigationView& nav);
@@ -311,43 +308,43 @@ private:
};
};*/
class ModalMessageView : public View {
public:
ModalMessageView(
NavigationView& nav,
const std::string& title,
const std::string& message,
const modal_t type,
const std::function<void(bool)> on_choice
);
void paint(Painter& painter) override;
void focus() override;
class ModalMessageView : public View
{
public:
ModalMessageView(
NavigationView &nav,
const std::string &title,
const std::string &message,
const modal_t type,
const std::function<void(bool)> on_choice);
std::string title() const override { return title_; };
void paint(Painter &painter) override;
void focus() override;
private:
const std::string title_;
const std::string message_;
const modal_t type_;
const std::function<void(bool)> on_choice_;
std::string title() const override { return title_; };
Button button_ok {
{ 10 * 8, 14 * 16, 10 * 8, 48 },
"OK",
private:
const std::string title_;
const std::string message_;
const modal_t type_;
const std::function<void(bool)> on_choice_;
Button button_ok{
{10 * 8, 14 * 16, 10 * 8, 48},
"OK",
};
Button button_yes{
{5 * 8, 14 * 16, 8 * 8, 48},
"YES",
};
Button button_no{
{17 * 8, 14 * 16, 8 * 8, 48},
"NO",
};
};
Button button_yes {
{ 5 * 8, 14 * 16, 8 * 8, 48 },
"YES",
};
Button button_no {
{ 17 * 8, 14 * 16, 8 * 8, 48 },
"NO",
};
};
} /* namespace ui */
#endif/*__UI_NAVIGATION_H__*/
#endif /*__UI_NAVIGATION_H__*/
+13
View File
@@ -98,6 +98,19 @@ void RecordView::focus() {
}
void RecordView::set_sampling_rate(const size_t new_sampling_rate) {
/* We are changing "REC" icon background to yellow in BW rec Options >600kHz
where we are NOT recording full IQ .C16 files (recorded files are decimated ones).
Those decimated recorded files,has not the full IQ samples .
are ok as recorded spectrum indication, but they should not be used by Replay app.
We keep original black background in all the correct IQ .C16 files BW's Options */
if (new_sampling_rate > 4800000) { // > BW >600kHz (fs=8*BW), (750kHz ...2750kHz)
button_record.set_background(ui::Color::yellow());
} else {
button_record.set_background(ui::Color::black());
}
if( new_sampling_rate != sampling_rate ) {
stop();
+4 -4
View File
@@ -62,8 +62,8 @@ void SSB::execute(const buffer_s16_t& audio, const buffer_c8_t& buffer) {
//default: break;
//}
i *= 64.0f;
q *= 64.0f;
i *= 256.0f; // Original 64.0f, now x 4 (+12 dB's SSB BB modulation)
q *= 256.0f; // Original 64.0f, now x 4 (+12 dB's SSB BB modulation)
switch (mode) {
case Mode::LSB: re = q; im = i; break;
case Mode::USB: re = i; im = q; break;
@@ -123,9 +123,9 @@ void AM::execute(const buffer_s16_t& audio, const buffer_c8_t& buffer) {
}
q = sample / 32768.0f;
q *= 64.0f;
q *= 256.0f; // Original 64.0f,now x4 (+12 dB's BB_modulation in AM & DSB)
switch (mode) {
case Mode::AM: re = q + 20; im = q + 20; break;
case Mode::AM: re = q + 80; im = q + 80; break; // Original DC add +20_DC_level=carrier,now x4 (+12dB's AM carrier)
case Mode::DSB: re = q; im = q; break;
default: break;
}
+1 -1
View File
@@ -1,4 +1,4 @@
#!/usr/bin/env python
#!/usr/bin/env python3
# Copyright (C) 2017 Furrtek
#
+2 -1
View File
@@ -1,7 +1,8 @@
# Make airlines.db
Licensed under GNU GPL v3 (license)[../../../LICENSE]
Licensed under [GNU GPL v3](../../../LICENSE)
USAGE:
- Copy file from: https://raw.githubusercontent.com/kx1t/planefence-airlinecodes/main/airlinecodes.txt
- Run Python 3 script: `./make_airlines_db.py`
- Copy file to /ADSB folder on SDCARD
+1191 -69
View File
File diff suppressed because it is too large Load Diff
+8
View File
@@ -0,0 +1,8 @@
# Make icao24.db
Licensed under [GNU GPL v3](../../../LICENSE)
USAGE:
- Copy file from: https://opensky-network.org/datasets/metadata/aircraftDatabase.csv
- Run Python 3 script: `./make_icao24_db.py`
- Copy file to /ADSB folder on SDCARD
File diff suppressed because it is too large Load Diff
+63
View File
@@ -0,0 +1,63 @@
#!/usr/bin/env python3
# Copyright (C) 2021 ArjanOnwezen
#
# 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.
#
# -------------------------------------------------------------------------------------
# Create icao24.db, used for ADS-B receiver application, using
# https://opensky-network.org/datasets/metadata/aircraftDatabase.csv
# as a source.
# -------------------------------------------------------------------------------------
import csv
import unicodedata
icao24_codes=bytearray()
data=bytearray()
row_count=0
database=open("icao24.db", "wb")
with open('aircraftDatabase.csv', 'rt') as csv_file:
sorted_lines=sorted(csv_file.readlines()[1:])
for row in csv.reader(sorted_lines, quotechar='"', delimiter=',', quoting=csv.QUOTE_ALL, skipinitialspace=True):
# only store in case enough info is available
if len(row) == 27 and len(row[0]) == 6 and len(row[1]) > 0:
icao24_code=row[0][:6].upper()
registration=row[1][:8].encode('ascii', 'ignore')
manufacturer=row[3][:32].encode('ascii', 'ignore')
model=row[4][:32].encode('ascii', 'ignore')
# in case icao aircraft type isn't, use ac type like BALL for balloon
if len(row[8]) == 3:
actype=row[8][:3].encode('ascii', 'ignore')
else:
actype=row[5][:4].encode('ascii', 'ignore')
owner=row[13][:32].encode('ascii', 'ignore')
operator=row[9][:32].encode('ascii', 'ignore')
#padding
icao24_codes=icao24_codes+bytearray(icao24_code+'\0', encoding='ascii')
registration_padding=bytearray('\0' * (9 - len(registration)), encoding='ascii')
manufacturer_padding=bytearray('\0' * (33 - len(manufacturer)), encoding='ascii')
model_padding=bytearray('\0' * (33 - len(model)), encoding='ascii')
actype_padding=bytearray('\0' * (5 - len(actype)), encoding='ascii')
owner_padding=bytearray('\0' * (33 - len(owner)), encoding='ascii')
operator_padding=bytearray('\0' * (33 - len(operator)), encoding='ascii')
data=data+bytearray(registration+registration_padding+manufacturer+manufacturer_padding+model+model_padding+actype+actype_padding+owner+owner_padding+operator+operator_padding)
row_count+=1
database.write(icao24_codes+data)
print("Total of", row_count, "ICAO codes stored in database")