mirror of
https://github.com/portapack-mayhem/mayhem-firmware.git
synced 2025-08-23 13:08:13 +00:00
SYNC
This commit is contained in:
@@ -20,357 +20,3 @@
|
||||
*/
|
||||
|
||||
#include "ais_baseband.hpp"
|
||||
|
||||
#include <cstdint>
|
||||
#include <cstddef>
|
||||
#include <cstdlib>
|
||||
#include <string>
|
||||
|
||||
#include "crc.hpp"
|
||||
|
||||
// TODO: Move string formatting elsewhere!!!
|
||||
#include "ui_widget.hpp"
|
||||
|
||||
namespace baseband {
|
||||
|
||||
template<typename T, typename BitRemap>
|
||||
class FieldReader {
|
||||
public:
|
||||
constexpr FieldReader(
|
||||
const T& data
|
||||
) : data { data }
|
||||
{
|
||||
}
|
||||
|
||||
uint32_t read(const size_t start_bit, const size_t length) const {
|
||||
uint32_t value = 0;
|
||||
for(size_t i=start_bit; i<(start_bit + length); i++) {
|
||||
value = (value << 1) | data[bit_remap(i)];
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
private:
|
||||
const T& data;
|
||||
const BitRemap bit_remap { };
|
||||
};
|
||||
|
||||
namespace ais {
|
||||
|
||||
struct BitRemap {
|
||||
size_t operator()(const size_t bit_index) const {
|
||||
return bit_index ^ 7;
|
||||
}
|
||||
};
|
||||
|
||||
struct CRCBitRemap {
|
||||
size_t operator()(const size_t bit_index) const {
|
||||
return bit_index;
|
||||
}
|
||||
};
|
||||
|
||||
using FieldReader = baseband::FieldReader<std::bitset<1024>, BitRemap>;
|
||||
using CRCFieldReader = baseband::FieldReader<std::bitset<1024>, CRCBitRemap>;
|
||||
|
||||
struct PacketLengthRange {
|
||||
constexpr PacketLengthRange(
|
||||
) : min_bytes { 0 },
|
||||
max_bytes { 0 }
|
||||
{
|
||||
}
|
||||
|
||||
constexpr PacketLengthRange(
|
||||
const uint16_t min_bits,
|
||||
const uint16_t max_bits
|
||||
) : min_bytes { static_cast<uint8_t>(min_bits / 8U) },
|
||||
max_bytes { static_cast<uint8_t>(max_bits / 8U) }
|
||||
{
|
||||
// static_assert((min_bits & 7) == 0, "minimum bits not a multiple of 8");
|
||||
// static_assert((max_bits & 7) == 0, "minimum bits not a multiple of 8");
|
||||
}
|
||||
|
||||
bool contains(const size_t bit_count) const {
|
||||
return !is_above(bit_count) && !is_below(bit_count);
|
||||
}
|
||||
|
||||
bool is_above(const size_t bit_count) const {
|
||||
return (min() > bit_count);
|
||||
}
|
||||
|
||||
bool is_below(const size_t bit_count) const {
|
||||
return (max() < bit_count);
|
||||
}
|
||||
|
||||
size_t min() const {
|
||||
return min_bytes * 8;
|
||||
}
|
||||
|
||||
size_t max() const {
|
||||
return max_bytes * 8;
|
||||
}
|
||||
|
||||
private:
|
||||
const uint8_t min_bytes;
|
||||
const uint8_t max_bytes;
|
||||
};
|
||||
|
||||
static constexpr std::array<PacketLengthRange, 64> packet_length_range { {
|
||||
{ 0, 0 }, // 0
|
||||
{ 168, 168 }, // 1
|
||||
{ 168, 168 }, // 2
|
||||
{ 168, 168 }, // 3
|
||||
{ 168, 168 }, // 4
|
||||
{ 424, 424 }, // 5
|
||||
{ 0, 0 }, // 6
|
||||
{ 0, 0 }, // 7
|
||||
{ 0, 1008 }, // 8
|
||||
{ 0, 0 }, // 9
|
||||
{ 0, 0 }, // 10
|
||||
{ 0, 0 }, // 11
|
||||
{ 0, 0 }, // 12
|
||||
{ 0, 0 }, // 13
|
||||
{ 0, 0 }, // 14
|
||||
{ 0, 0 }, // 15
|
||||
{ 0, 0 }, // 16
|
||||
{ 0, 0 }, // 17
|
||||
{ 168, 168 }, // 18
|
||||
{ 0, 0 }, // 19
|
||||
{ 72, 160 }, // 20
|
||||
{ 272, 360 }, // 21
|
||||
{ 168, 168 }, // 22
|
||||
{ 160, 160 }, // 23
|
||||
{ 160, 168 }, // 24
|
||||
{ 0, 168 }, // 25
|
||||
{ 0, 0 }, // 26
|
||||
{ 0, 0 }, // 27
|
||||
{ 0, 0 }, // 28
|
||||
{ 0, 0 }, // 29
|
||||
{ 0, 0 }, // 30
|
||||
{ 0, 0 }, // 31
|
||||
} };
|
||||
|
||||
struct PacketLengthValidator {
|
||||
bool operator()(const uint_fast8_t message_id, const size_t length) {
|
||||
return packet_length_range[message_id].contains(length);
|
||||
}
|
||||
};
|
||||
|
||||
struct PacketTooLong {
|
||||
bool operator()(const uint_fast8_t message_id, const size_t length) {
|
||||
return packet_length_range[message_id].is_below(length);
|
||||
}
|
||||
};
|
||||
|
||||
struct CRCCheck {
|
||||
bool operator()(const std::bitset<1024>& payload, const size_t data_length) {
|
||||
CRCFieldReader field_crc { payload };
|
||||
CRC<uint16_t> ais_fcs { 0x1021 };
|
||||
|
||||
uint16_t crc_calculated = 0xffff;
|
||||
|
||||
for(size_t i=0; i<data_length + 16; i+=8) {
|
||||
if( i == data_length ) {
|
||||
crc_calculated ^= 0xffff;
|
||||
}
|
||||
const uint8_t byte = field_crc.read(i, 8);
|
||||
crc_calculated = ais_fcs.calculate_byte(crc_calculated, byte);
|
||||
}
|
||||
|
||||
return crc_calculated == 0x0000;
|
||||
}
|
||||
};
|
||||
|
||||
static int32_t ais_latitude_normalized(
|
||||
const FieldReader& field,
|
||||
const size_t start_bit
|
||||
) {
|
||||
// Shifting and dividing is to sign-extend the source field.
|
||||
// TODO: There's probably a more elegant way to do it.
|
||||
return static_cast<int32_t>(field.read(start_bit, 27) << 5) / 32;
|
||||
}
|
||||
|
||||
static int32_t ais_longitude_normalized(
|
||||
const FieldReader& field,
|
||||
const size_t start_bit
|
||||
) {
|
||||
// Shifting and dividing is to sign-extend the source field.
|
||||
// TODO: There's probably a more elegant way to do it.
|
||||
return static_cast<int32_t>(field.read(start_bit, 28) << 4) / 16;
|
||||
}
|
||||
|
||||
static std::string ais_format_latlon_normalized(const int32_t normalized) {
|
||||
const int32_t t = (normalized * 5) / 3;
|
||||
const int32_t degrees = t / (100 * 10000);
|
||||
const int32_t fraction = std::abs(t) % (100 * 10000);
|
||||
return ui::to_string_dec_int(degrees) + "." + ui::to_string_dec_int(fraction, 6, '0');
|
||||
}
|
||||
|
||||
static std::string ais_format_latitude(
|
||||
const FieldReader& field,
|
||||
const size_t start_bit
|
||||
) {
|
||||
const auto value = static_cast<int32_t>(field.read(start_bit, 27) << 5) / 32;
|
||||
return ais_format_latlon_normalized(value);
|
||||
}
|
||||
|
||||
static std::string ais_format_longitude(
|
||||
const FieldReader& field,
|
||||
const size_t start_bit
|
||||
) {
|
||||
const auto value = static_cast<int32_t>(field.read(start_bit, 28) << 4) / 16;
|
||||
return ais_format_latlon_normalized(value);
|
||||
}
|
||||
|
||||
struct ais_datetime {
|
||||
uint16_t year;
|
||||
uint8_t month;
|
||||
uint8_t day;
|
||||
uint8_t hour;
|
||||
uint8_t minute;
|
||||
uint8_t second;
|
||||
};
|
||||
|
||||
static ais_datetime ais_get_datetime(
|
||||
const FieldReader& field,
|
||||
const size_t start_bit
|
||||
) {
|
||||
return {
|
||||
static_cast<uint16_t>(field.read(start_bit + 0, 14)),
|
||||
static_cast<uint8_t >(field.read(start_bit + 14, 4)),
|
||||
static_cast<uint8_t >(field.read(start_bit + 18, 5)),
|
||||
static_cast<uint8_t >(field.read(start_bit + 23, 5)),
|
||||
static_cast<uint8_t >(field.read(start_bit + 28, 6)),
|
||||
static_cast<uint8_t >(field.read(start_bit + 34, 6)),
|
||||
};
|
||||
}
|
||||
|
||||
static std::string ais_format_datetime(
|
||||
const FieldReader& field,
|
||||
const size_t start_bit
|
||||
) {
|
||||
const auto datetime = ais_get_datetime(field, start_bit);
|
||||
return ui::to_string_dec_uint(datetime.year, 4) + "/" +
|
||||
ui::to_string_dec_uint(datetime.month, 2, '0') + "/" +
|
||||
ui::to_string_dec_uint(datetime.day, 2, '0') + " " +
|
||||
ui::to_string_dec_uint(datetime.hour, 2, '0') + ":" +
|
||||
ui::to_string_dec_uint(datetime.minute, 2, '0') + ":" +
|
||||
ui::to_string_dec_uint(datetime.second, 2, '0');
|
||||
}
|
||||
|
||||
static char ais_char_to_ascii(const uint8_t c) {
|
||||
return (c ^ 32) + 32;
|
||||
}
|
||||
|
||||
static std::string ais_read_text(
|
||||
const FieldReader& field,
|
||||
const size_t start_bit,
|
||||
const size_t character_count
|
||||
) {
|
||||
std::string result;
|
||||
const size_t character_length = 6;
|
||||
const size_t end_bit = start_bit + character_count * character_length;
|
||||
for(size_t i=start_bit; i<end_bit; i+=character_length) {
|
||||
result += ais_char_to_ascii(field.read(i, character_length));
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
static std::string ais_format_navigational_status(const unsigned int value) {
|
||||
switch(value) {
|
||||
case 0: return "under way w/engine";
|
||||
case 1: return "at anchor";
|
||||
case 2: return "not under command";
|
||||
case 3: return "restricted maneuv";
|
||||
case 4: return "constrained draught";
|
||||
case 5: return "moored";
|
||||
case 6: return "aground";
|
||||
case 7: return "fishing";
|
||||
case 8: return "sailing";
|
||||
case 9: case 10: case 13: return "reserved";
|
||||
case 11: return "towing astern";
|
||||
case 12: return "towing ahead/along";
|
||||
case 14: return "SART/MOB/EPIRB";
|
||||
case 15: return "undefined";
|
||||
default: return "unexpected";
|
||||
}
|
||||
}
|
||||
|
||||
decoded_packet packet_decode(const std::bitset<1024>& payload, const size_t payload_length) {
|
||||
// TODO: Unstuff here, not in baseband!
|
||||
|
||||
// Subtract end flag (8 bits) - one unstuffing bit (occurs during end flag).
|
||||
const size_t data_and_fcs_length = payload_length - 7;
|
||||
|
||||
if( data_and_fcs_length < 38 ) {
|
||||
return { "short " + ui::to_string_dec_uint(data_and_fcs_length, 3), "" };
|
||||
}
|
||||
|
||||
const size_t extra_bits = data_and_fcs_length & 7;
|
||||
if( extra_bits != 0 ) {
|
||||
return { "extra bits " + ui::to_string_dec_uint(data_and_fcs_length, 3), "" };
|
||||
}
|
||||
|
||||
FieldReader field { payload };
|
||||
|
||||
const auto message_id = field.read(0, 6);
|
||||
|
||||
const size_t data_length = data_and_fcs_length - 16;
|
||||
PacketLengthValidator packet_length_valid;
|
||||
if( !packet_length_valid(message_id, data_length) ) {
|
||||
return { "bad length " + ui::to_string_dec_uint(data_length, 3), "" };
|
||||
}
|
||||
|
||||
CRCCheck crc_ok;
|
||||
if( !crc_ok(payload, data_length) ) {
|
||||
return { "crc", "" };
|
||||
}
|
||||
|
||||
const auto source_id = field.read(8, 30);
|
||||
std::string result { ui::to_string_dec_uint(message_id, 2) + " " + ui::to_string_dec_uint(source_id, 10) };
|
||||
|
||||
switch(message_id) {
|
||||
case 1:
|
||||
case 2:
|
||||
case 3:
|
||||
{
|
||||
const auto navigational_status = field.read(38, 4);
|
||||
result += " " + ais_format_navigational_status(navigational_status);
|
||||
result += " " + ais_format_latlon_normalized(ais_latitude_normalized(field, 89));
|
||||
result += " " + ais_format_latlon_normalized(ais_longitude_normalized(field, 61));
|
||||
}
|
||||
break;
|
||||
|
||||
case 4:
|
||||
{
|
||||
result += " " + ais_format_datetime(field, 38);
|
||||
result += " " + ais_format_latlon_normalized(ais_latitude_normalized(field, 107));
|
||||
result += " " + ais_format_latlon_normalized(ais_longitude_normalized(field, 79));
|
||||
}
|
||||
break;
|
||||
|
||||
case 5:
|
||||
{
|
||||
const auto call_sign = ais_read_text(field, 70, 7);
|
||||
const auto name = ais_read_text(field, 112, 20);
|
||||
const auto destination = ais_read_text(field, 302, 20);
|
||||
result += " \"" + call_sign + "\" \"" + name + "\" \"" + destination + "\"";
|
||||
}
|
||||
break;
|
||||
|
||||
case 21:
|
||||
{
|
||||
const auto name = ais_read_text(field, 43, 20);
|
||||
result += " \"" + name + "\" " + ais_format_latitude(field, 192) + " " + ais_format_longitude(field, 164);
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return { "OK", result };
|
||||
}
|
||||
|
||||
} /* namespace ais */
|
||||
} /* namespace baseband */
|
||||
|
@@ -36,31 +36,19 @@ namespace ais {
|
||||
// cleaning up ISI.
|
||||
|
||||
// Translate+RRC filter
|
||||
// sample=76.8k, deviation=2400, b=0.5, symbol=9600
|
||||
// Length: 32 taps, 4 symbols, 1 cycle of sinusoid
|
||||
constexpr std::array<std::complex<float>, 32> rrc_taps_76k8_4t_p { {
|
||||
{ 5.3368736990e-03f, 0.0000000000e+00f }, { 4.6850211151e-03f, 9.3190864122e-04f },
|
||||
{ 1.7970187432e-03f, 7.4434953528e-04f }, { -2.5478608559e-03f, -1.7024261963e-03f },
|
||||
{ -6.6710920858e-03f, -6.6710920858e-03f }, { -8.6960320791e-03f, -1.3014531722e-02f },
|
||||
{ -7.5474785839e-03f, -1.8221225159e-02f }, { -3.8115552023e-03f, -1.9161981995e-02f },
|
||||
{ -8.1697309033e-19f, -1.3342183083e-02f }, { 3.6940784220e-05f, -1.8571386338e-04f },
|
||||
{ -7.5474785839e-03f, 1.8221225159e-02f }, { -2.4954265902e-02f, 3.7346698152e-02f },
|
||||
{ -5.1450054092e-02f, 5.1450054092e-02f }, { -8.3018814119e-02f, 5.5471398140e-02f },
|
||||
{ -1.1321218147e-01f, 4.6894020990e-02f }, { -1.3498960022e-01f, 2.6851100952e-02f },
|
||||
{ -1.4292666316e-01f, 1.7503468055e-17f }, { -1.3498960022e-01f, -2.6851100952e-02f },
|
||||
{ -1.1321218147e-01f, -4.6894020990e-02f }, { -8.3018814119e-02f, -5.5471398140e-02f },
|
||||
{ -5.1450054092e-02f, -5.1450054092e-02f }, { -2.4954265902e-02f, -3.7346698152e-02f },
|
||||
{ -7.5474785839e-03f, -1.8221225159e-02f }, { 3.6940784220e-05f, 1.8571386338e-04f },
|
||||
{ 2.4509192710e-18f, 1.3342183083e-02f }, { -3.8115552023e-03f, 1.9161981995e-02f },
|
||||
{ -7.5474785839e-03f, 1.8221225159e-02f }, { -8.6960320791e-03f, 1.3014531722e-02f },
|
||||
{ -6.6710920858e-03f, 6.6710920858e-03f }, { -2.5478608559e-03f, 1.7024261963e-03f },
|
||||
{ 1.7970187432e-03f, -7.4434953528e-04f }, { 4.6850211151e-03f, -9.3190864122e-04f },
|
||||
// sample=38.4k, deviation=2400, b=0.5, symbol=9600
|
||||
// Length: 16 taps, 4 symbols, 1 cycles of sinusoid
|
||||
constexpr std::array<std::complex<float>, 16> rrc_taps_38k4_4t_p { {
|
||||
{ 1.0619794019e-02f, 0.0000000000e+00f }, { 3.5758705854e-03f, 1.4811740938e-03f },
|
||||
{ -1.3274742629e-02f, -1.3274742629e-02f }, { -1.5018657262e-02f, -3.6258246051e-02f },
|
||||
{ 0.0000000000e+00f, -2.6549484581e-02f }, { -1.5018657262e-02f, 3.6258246051e-02f },
|
||||
{ -1.0237997393e-01f, 1.0237997393e-01f }, { -2.2527985355e-01f, 9.3313970669e-02f },
|
||||
{ -2.8440842032e-01f, 0.0000000000e+00f }, { -2.2527985355e-01f, -9.3313970669e-02f },
|
||||
{ -1.0237997393e-01f, -1.0237997393e-01f }, { -1.5018657262e-02f, -3.6258246051e-02f },
|
||||
{ 0.0000000000e+00f, 2.6549484581e-02f }, { -1.5018657262e-02f, 3.6258246051e-02f },
|
||||
{ -1.3274742629e-02f, 1.3274742629e-02f }, { 3.5758705854e-03f, -1.4811740938e-03f },
|
||||
} };
|
||||
|
||||
using decoded_packet = std::pair<std::string, std::string>;
|
||||
|
||||
decoded_packet packet_decode(const std::bitset<1024>& data, const size_t data_length);
|
||||
|
||||
} /* namespace ais */
|
||||
} /* namespace baseband */
|
||||
|
||||
|
220
firmware/common/ais_packet.cpp
Normal file
220
firmware/common/ais_packet.cpp
Normal file
@@ -0,0 +1,220 @@
|
||||
/*
|
||||
* Copyright (C) 2014 Jared Boone, ShareBrained Technology, Inc.
|
||||
*
|
||||
* 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 "ais_packet.hpp"
|
||||
|
||||
#include "crc.hpp"
|
||||
|
||||
#include <cstdlib>
|
||||
|
||||
namespace ais {
|
||||
|
||||
struct PacketLengthRange {
|
||||
constexpr PacketLengthRange(
|
||||
) : min_bytes { 0 },
|
||||
max_bytes { 0 }
|
||||
{
|
||||
}
|
||||
|
||||
constexpr PacketLengthRange(
|
||||
const uint16_t min_bits,
|
||||
const uint16_t max_bits
|
||||
) : min_bytes { static_cast<uint8_t>(min_bits / 8U) },
|
||||
max_bytes { static_cast<uint8_t>(max_bits / 8U) }
|
||||
{
|
||||
// static_assert((min_bits & 7) == 0, "minimum bits not a multiple of 8");
|
||||
// static_assert((max_bits & 7) == 0, "minimum bits not a multiple of 8");
|
||||
}
|
||||
|
||||
constexpr bool contains(const size_t bit_count) const {
|
||||
return !is_above(bit_count) && !is_below(bit_count);
|
||||
}
|
||||
|
||||
constexpr bool is_above(const size_t bit_count) const {
|
||||
return (min() > bit_count);
|
||||
}
|
||||
|
||||
constexpr bool is_below(const size_t bit_count) const {
|
||||
return (max() < bit_count);
|
||||
}
|
||||
|
||||
constexpr size_t min() const {
|
||||
return min_bytes * 8;
|
||||
}
|
||||
|
||||
constexpr size_t max() const {
|
||||
return max_bytes * 8;
|
||||
}
|
||||
|
||||
private:
|
||||
const uint8_t min_bytes;
|
||||
const uint8_t max_bytes;
|
||||
};
|
||||
|
||||
static constexpr std::array<PacketLengthRange, 64> packet_length_range { {
|
||||
{ 0, 0 }, // 0
|
||||
{ 168, 168 }, // 1
|
||||
{ 168, 168 }, // 2
|
||||
{ 168, 168 }, // 3
|
||||
{ 168, 168 }, // 4
|
||||
{ 424, 424 }, // 5
|
||||
{ 0, 0 }, // 6
|
||||
{ 0, 0 }, // 7
|
||||
{ 0, 1008 }, // 8
|
||||
{ 0, 0 }, // 9
|
||||
{ 0, 0 }, // 10
|
||||
{ 0, 0 }, // 11
|
||||
{ 0, 0 }, // 12
|
||||
{ 0, 0 }, // 13
|
||||
{ 0, 0 }, // 14
|
||||
{ 0, 0 }, // 15
|
||||
{ 0, 0 }, // 16
|
||||
{ 0, 0 }, // 17
|
||||
{ 168, 168 }, // 18
|
||||
{ 0, 0 }, // 19
|
||||
{ 72, 160 }, // 20
|
||||
{ 272, 360 }, // 21
|
||||
{ 168, 168 }, // 22
|
||||
{ 160, 160 }, // 23
|
||||
{ 160, 168 }, // 24
|
||||
{ 0, 168 }, // 25
|
||||
{ 0, 0 }, // 26
|
||||
{ 0, 0 }, // 27
|
||||
{ 0, 0 }, // 28
|
||||
{ 0, 0 }, // 29
|
||||
{ 0, 0 }, // 30
|
||||
{ 0, 0 }, // 31
|
||||
} };
|
||||
|
||||
struct PacketLengthValidator {
|
||||
constexpr bool operator()(const uint_fast8_t message_id, const size_t length) const {
|
||||
return packet_length_range[message_id].contains(length);
|
||||
}
|
||||
};
|
||||
|
||||
struct PacketTooLong {
|
||||
constexpr bool operator()(const uint_fast8_t message_id, const size_t length) const {
|
||||
return packet_length_range[message_id].is_below(length);
|
||||
}
|
||||
};
|
||||
|
||||
static constexpr char char_to_ascii(const uint8_t c) {
|
||||
return (c ^ 32) + 32;
|
||||
}
|
||||
|
||||
size_t Packet::length() const {
|
||||
return packet_.size();
|
||||
}
|
||||
|
||||
bool Packet::is_valid() const {
|
||||
return length_valid() && crc_ok();
|
||||
}
|
||||
|
||||
Timestamp Packet::received_at() const {
|
||||
return packet_.timestamp();
|
||||
}
|
||||
|
||||
uint32_t Packet::message_id() const {
|
||||
return field_.read(0, 6);
|
||||
}
|
||||
|
||||
MMSI Packet::user_id() const {
|
||||
return field_.read(8, 30);
|
||||
}
|
||||
|
||||
MMSI Packet::source_id() const {
|
||||
return field_.read(8, 30);
|
||||
}
|
||||
|
||||
uint32_t Packet::read(const size_t start_bit, const size_t length) const {
|
||||
return field_.read(start_bit, length);
|
||||
}
|
||||
|
||||
std::string Packet::text(
|
||||
const size_t start_bit,
|
||||
const size_t character_count
|
||||
) const {
|
||||
std::string result;
|
||||
result.reserve(character_count);
|
||||
|
||||
const size_t character_length = 6;
|
||||
const size_t end_bit = start_bit + character_count * character_length;
|
||||
for(size_t i=start_bit; i<end_bit; i+=character_length) {
|
||||
result += char_to_ascii(field_.read(i, character_length));
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
DateTime Packet::datetime(const size_t start_bit) const {
|
||||
return {
|
||||
static_cast<uint16_t>(field_.read(start_bit + 0, 14)),
|
||||
static_cast<uint8_t >(field_.read(start_bit + 14, 4)),
|
||||
static_cast<uint8_t >(field_.read(start_bit + 18, 5)),
|
||||
static_cast<uint8_t >(field_.read(start_bit + 23, 5)),
|
||||
static_cast<uint8_t >(field_.read(start_bit + 28, 6)),
|
||||
static_cast<uint8_t >(field_.read(start_bit + 34, 6)),
|
||||
};
|
||||
}
|
||||
|
||||
Latitude Packet::latitude(const size_t start_bit) const {
|
||||
return field_.read(start_bit, 27);
|
||||
}
|
||||
|
||||
Longitude Packet::longitude(const size_t start_bit) const {
|
||||
return field_.read(start_bit, 28);
|
||||
}
|
||||
|
||||
bool Packet::crc_ok() const {
|
||||
CRCReader field_crc { packet_ };
|
||||
CRC<uint16_t> ais_fcs { 0x1021, 0xffff, 0xffff };
|
||||
|
||||
for(size_t i=0; i<data_length(); i+=8) {
|
||||
ais_fcs.process_byte(field_crc.read(i, 8));
|
||||
}
|
||||
|
||||
return (ais_fcs.checksum() == field_crc.read(data_length(), fcs_length));
|
||||
}
|
||||
|
||||
size_t Packet::data_and_fcs_length() const {
|
||||
// Subtract end flag (8 bits) - one unstuffing bit (occurs during end flag).
|
||||
return length() - 7;
|
||||
}
|
||||
|
||||
size_t Packet::data_length() const {
|
||||
return data_and_fcs_length() - fcs_length;
|
||||
}
|
||||
|
||||
bool Packet::length_valid() const {
|
||||
const size_t extra_bits = data_and_fcs_length() & 7;
|
||||
if( extra_bits != 0 ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const PacketLengthValidator packet_length_valid;
|
||||
if( !packet_length_valid(message_id(), data_length()) ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
} /* namespace ais */
|
145
firmware/common/ais_packet.hpp
Normal file
145
firmware/common/ais_packet.hpp
Normal file
@@ -0,0 +1,145 @@
|
||||
/*
|
||||
* Copyright (C) 2014 Jared Boone, ShareBrained Technology, Inc.
|
||||
*
|
||||
* 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 __AIS_PACKET_H__
|
||||
#define __AIS_PACKET_H__
|
||||
|
||||
#include "baseband_packet.hpp"
|
||||
#include "field_reader.hpp"
|
||||
|
||||
#include <cstdint>
|
||||
#include <cstddef>
|
||||
#include <string>
|
||||
|
||||
namespace ais {
|
||||
|
||||
struct DateTime {
|
||||
uint16_t year;
|
||||
uint8_t month;
|
||||
uint8_t day;
|
||||
uint8_t hour;
|
||||
uint8_t minute;
|
||||
uint8_t second;
|
||||
};
|
||||
|
||||
template<size_t FieldSize, int32_t DegMax, uint32_t NAValue>
|
||||
struct LatLonBase {
|
||||
constexpr LatLonBase(
|
||||
) : LatLonBase { raw_not_available }
|
||||
{
|
||||
}
|
||||
|
||||
constexpr LatLonBase(
|
||||
const int32_t raw
|
||||
) : raw_ { raw }
|
||||
{
|
||||
}
|
||||
|
||||
constexpr LatLonBase(
|
||||
const LatLonBase& other
|
||||
) : raw_ { other.raw_ }
|
||||
{
|
||||
}
|
||||
|
||||
int32_t normalized() const {
|
||||
return static_cast<int32_t>(raw() << sign_extend_shift) / (1 << sign_extend_shift);
|
||||
}
|
||||
|
||||
int32_t raw() const {
|
||||
return raw_;
|
||||
}
|
||||
|
||||
bool is_not_available() const {
|
||||
return raw() == raw_not_available;
|
||||
}
|
||||
|
||||
bool is_valid() const {
|
||||
return (normalized() >= raw_valid_min) && (normalized() <= raw_valid_max);
|
||||
}
|
||||
|
||||
private:
|
||||
int32_t raw_;
|
||||
|
||||
static constexpr size_t sign_extend_shift = 32 - FieldSize;
|
||||
|
||||
static constexpr int32_t raw_not_available = NAValue;
|
||||
static constexpr int32_t raw_valid_min = -DegMax * 60 * 10000;
|
||||
static constexpr int32_t raw_valid_max = DegMax * 60 * 10000;
|
||||
};
|
||||
|
||||
using Latitude = LatLonBase<27, 90, 0x3412140>;
|
||||
using Longitude = LatLonBase<28, 180, 0x6791AC0>;
|
||||
|
||||
using RateOfTurn = int8_t;
|
||||
using SpeedOverGround = uint16_t;
|
||||
using CourseOverGround = uint16_t;
|
||||
using TrueHeading = uint16_t;
|
||||
|
||||
using MMSI = uint32_t;
|
||||
|
||||
class Packet {
|
||||
public:
|
||||
constexpr Packet(
|
||||
const baseband::Packet& packet
|
||||
) : packet_ { packet },
|
||||
field_ { packet_ }
|
||||
{
|
||||
}
|
||||
|
||||
size_t length() const;
|
||||
|
||||
bool is_valid() const;
|
||||
|
||||
Timestamp received_at() const;
|
||||
|
||||
uint32_t message_id() const;
|
||||
MMSI user_id() const;
|
||||
MMSI source_id() const;
|
||||
|
||||
uint32_t read(const size_t start_bit, const size_t length) const;
|
||||
|
||||
std::string text(const size_t start_bit, const size_t character_count) const;
|
||||
|
||||
DateTime datetime(const size_t start_bit) const;
|
||||
|
||||
Latitude latitude(const size_t start_bit) const;
|
||||
Longitude longitude(const size_t start_bit) const;
|
||||
|
||||
bool crc_ok() const;
|
||||
|
||||
private:
|
||||
using Reader = FieldReader<baseband::Packet, BitRemapByteReverse>;
|
||||
using CRCReader = FieldReader<baseband::Packet, BitRemapNone>;
|
||||
|
||||
const baseband::Packet packet_;
|
||||
const Reader field_;
|
||||
|
||||
const size_t fcs_length = 16;
|
||||
|
||||
size_t data_and_fcs_length() const;
|
||||
size_t data_length() const;
|
||||
|
||||
bool length_valid() const;
|
||||
};
|
||||
|
||||
} /* namespace ais */
|
||||
|
||||
#endif/*__AIS_PACKET_H__*/
|
@@ -35,9 +35,6 @@ enum class Direction {
|
||||
Transmit = 1,
|
||||
};
|
||||
|
||||
buffer_t wait_for_rx_buffer();
|
||||
buffer_t wait_for_tx_buffer();
|
||||
|
||||
} /* namespace baseband */
|
||||
|
||||
#endif/*__BASEBAND_H__*/
|
||||
|
72
firmware/common/baseband_packet.hpp
Normal file
72
firmware/common/baseband_packet.hpp
Normal file
@@ -0,0 +1,72 @@
|
||||
/*
|
||||
* Copyright (C) 2015 Jared Boone, ShareBrained Technology, Inc.
|
||||
*
|
||||
* 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 __BASEBAND_PACKET_H__
|
||||
#define __BASEBAND_PACKET_H__
|
||||
|
||||
#include "baseband.hpp"
|
||||
|
||||
#include <cstddef>
|
||||
#include <bitset>
|
||||
|
||||
namespace baseband {
|
||||
|
||||
class Packet {
|
||||
public:
|
||||
void set_timestamp(const Timestamp& value) {
|
||||
timestamp_ = value;
|
||||
}
|
||||
|
||||
Timestamp timestamp() const {
|
||||
return timestamp_;
|
||||
}
|
||||
|
||||
void add(const bool symbol) {
|
||||
if( count < capacity() ) {
|
||||
data[count++] = symbol;
|
||||
}
|
||||
}
|
||||
|
||||
uint_fast8_t operator[](const size_t index) const {
|
||||
return (index < size()) ? data[index] : 0;
|
||||
}
|
||||
|
||||
size_t size() const {
|
||||
return count;
|
||||
}
|
||||
|
||||
size_t capacity() const {
|
||||
return data.size();
|
||||
}
|
||||
|
||||
void clear() {
|
||||
count = 0;
|
||||
}
|
||||
|
||||
private:
|
||||
std::bitset<1408> data;
|
||||
Timestamp timestamp_ { };
|
||||
size_t count { 0 };
|
||||
};
|
||||
|
||||
} /* namespace baseband */
|
||||
|
||||
#endif/*__BASEBAND_PACKET_H__*/
|
373
firmware/common/baseband_sgpio.cpp
Normal file
373
firmware/common/baseband_sgpio.cpp
Normal file
@@ -0,0 +1,373 @@
|
||||
/*
|
||||
* Copyright (C) 2014 Jared Boone, ShareBrained Technology, Inc.
|
||||
*
|
||||
* 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 "baseband_sgpio.hpp"
|
||||
|
||||
#include "baseband.hpp"
|
||||
#include "utility.hpp"
|
||||
|
||||
namespace baseband {
|
||||
|
||||
/*
|
||||
|
||||
struct PinConfig {
|
||||
P_OUT_CFG p_out_cfg;
|
||||
P_OE_CFG p_oe_cfg { P_OE_CFG::GPIO_OE };
|
||||
|
||||
constexpr SGPIOPinConfig(
|
||||
P_OUT_CFG p_out_cfg
|
||||
) :
|
||||
p_out_cfg(p_out_cfg)
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
static constexpr bool slice_mode_multislice = false;
|
||||
|
||||
static constexpr P_OUT_CFG output_multiplexing_mode =
|
||||
slice_mode_multislice ? P_OUT_CFG::DOUT_DOUTM8C : P_OUT_CFG::DOUT_DOUTM8A;
|
||||
|
||||
static constexpr std::array<PinConfig, 16> pin_config { {
|
||||
[PIN_D0] = { output_multiplexing_mode, SLICE_A },
|
||||
[PIN_D1] = { output_multiplexing_mode, SLICE_I },
|
||||
[PIN_D2] = { output_multiplexing_mode, },
|
||||
[PIN_D3] = { output_multiplexing_mode, },
|
||||
[PIN_D4] = { output_multiplexing_mode, },
|
||||
[PIN_D5] = { output_multiplexing_mode, },
|
||||
[PIN_D6] = { output_multiplexing_mode, },
|
||||
[PIN_D7] = { output_multiplexing_mode, },
|
||||
[PIN_CLKIN] = { P_OUT_CFG::DOUT_DOUTM1, },
|
||||
[PIN_CAPTURE] = { P_OUT_CFG::DOUT_DOUTM1, },
|
||||
[PIN_DISABLE] = { P_OUT_CFG::GPIO_OUT, },
|
||||
[PIN_DIRECTION] = { P_OUT_CFG::GPIO_OUT, },
|
||||
[PIN_INVERT] = { P_OUT_CFG::GPIO_OUT, },
|
||||
[PIN_DECIM0] = { P_OUT_CFG::GPIO_OUT, },
|
||||
[PIN_DECIM1] = { P_OUT_CFG::DOUT_DOUTM1, },
|
||||
[PIN_DECIM2] = { P_OUT_CFG::GPIO_OUT, },
|
||||
} };
|
||||
*/
|
||||
/*
|
||||
static constexpr std::array<LPC_SGPIO_OUT_MUX_CFG_Type, 16> out_mux_cfg_receive {
|
||||
{ },
|
||||
};
|
||||
|
||||
static constexpr std::array<LPC_SGPIO_OUT_MUX_CFG_Type, 16> out_mux_cfg_transmit {
|
||||
{ },
|
||||
};
|
||||
*/
|
||||
|
||||
enum class P_OUT_CFG : uint8_t {
|
||||
DOUT_DOUTM1 = 0x0,
|
||||
DOUT_DOUTM2A = 0x1,
|
||||
DOUT_DOUTM2B = 0x2,
|
||||
DOUT_DOUTM2C = 0x3,
|
||||
GPIO_OUT = 0x4,
|
||||
DOUT_DOUTM4A = 0x5,
|
||||
DOUT_DOUTM4B = 0x6,
|
||||
DOUT_DOUTM4C = 0x7,
|
||||
CLK_OUT = 0x8,
|
||||
DOUT_DOUTM8A = 0x9,
|
||||
DOUT_DOUTM8B = 0xa,
|
||||
DOUT_DOUTM8C = 0xb,
|
||||
};
|
||||
|
||||
enum class P_OE_CFG : uint8_t {
|
||||
GPIO_OE = 0x0,
|
||||
DOUT_OEM1 = 0x4,
|
||||
DOUT_OEM2 = 0x5,
|
||||
DOUT_OEM4 = 0x6,
|
||||
DOUT_OEM8 = 0x7,
|
||||
};
|
||||
|
||||
enum class CONCAT_ORDER : uint8_t {
|
||||
SELF_LOOP = 0x0,
|
||||
TWO_SLICES = 0x1,
|
||||
FOUR_SLICES = 0x2,
|
||||
EIGHT_SLICES = 0x3,
|
||||
};
|
||||
|
||||
enum class CONCAT_ENABLE : uint8_t {
|
||||
EXTERNAL_DATA_PIN = 0x0,
|
||||
CONCATENATE_DATA = 0x1,
|
||||
};
|
||||
|
||||
enum class CLK_CAPTURE_MODE : uint8_t {
|
||||
RISING_CLOCK_EDGE = 0,
|
||||
FALLING_CLOCK_EDGE = 1,
|
||||
};
|
||||
|
||||
enum class PARALLEL_MODE : uint8_t {
|
||||
SHIFT_1_BIT_PER_CLOCK = 0x0,
|
||||
SHIFT_2_BITS_PER_CLOCK = 0x1,
|
||||
SHIFT_4_BITS_PER_CLOCK = 0x2,
|
||||
SHIFT_1_BYTE_PER_CLOCK = 0x3,
|
||||
};
|
||||
|
||||
enum {
|
||||
PIN_D0 = 0,
|
||||
PIN_D1 = 1,
|
||||
PIN_D2 = 2,
|
||||
PIN_D3 = 3,
|
||||
PIN_D4 = 4,
|
||||
PIN_D5 = 5,
|
||||
PIN_D6 = 6,
|
||||
PIN_D7 = 7,
|
||||
PIN_CLKIN = 8,
|
||||
PIN_CAPTURE = 9,
|
||||
PIN_DISABLE = 10,
|
||||
PIN_DIRECTION = 11,
|
||||
PIN_INVERT = 12,
|
||||
PIN_DECIM0 = 13,
|
||||
PIN_DECIM1 = 14,
|
||||
PIN_DECIM2 = 15,
|
||||
};
|
||||
|
||||
enum class Slice : uint8_t {
|
||||
A = 0,
|
||||
B = 1,
|
||||
C = 2,
|
||||
D = 3,
|
||||
E = 4,
|
||||
F = 5,
|
||||
G = 6,
|
||||
H = 7,
|
||||
I = 8,
|
||||
J = 9,
|
||||
K = 10,
|
||||
L = 11,
|
||||
M = 12,
|
||||
N = 13,
|
||||
O = 14,
|
||||
P = 15,
|
||||
};
|
||||
|
||||
constexpr bool slice_mode_multislice = false;
|
||||
|
||||
constexpr uint8_t pos_count_multi_slice = 0x1f;
|
||||
constexpr uint8_t pos_count_single_slice = 0x03;
|
||||
|
||||
constexpr Slice slice_order[] {
|
||||
Slice::A,
|
||||
Slice::I,
|
||||
Slice::E,
|
||||
Slice::J,
|
||||
Slice::C,
|
||||
Slice::K,
|
||||
Slice::F,
|
||||
Slice::L,
|
||||
Slice::B,
|
||||
Slice::M,
|
||||
Slice::G,
|
||||
Slice::N,
|
||||
Slice::D,
|
||||
Slice::O,
|
||||
Slice::H,
|
||||
Slice::P,
|
||||
};
|
||||
|
||||
constexpr uint32_t gpio_outreg(const Direction direction) {
|
||||
return ((direction == Direction::Transmit) ? (1U << 11) : 0U) | (1U << 10);
|
||||
}
|
||||
|
||||
constexpr uint32_t gpio_oenreg(const Direction direction) {
|
||||
return
|
||||
(0U << PIN_DECIM2)
|
||||
| (1U << PIN_DECIM1)
|
||||
| (0U << PIN_DECIM0)
|
||||
| (0U << PIN_INVERT)
|
||||
| (1U << PIN_DIRECTION)
|
||||
| (1U << PIN_DISABLE)
|
||||
| (0U << PIN_CAPTURE)
|
||||
| (0U << PIN_CLKIN)
|
||||
| ((direction == Direction::Transmit) ? 0xffU : 0x00U)
|
||||
;
|
||||
}
|
||||
|
||||
constexpr uint32_t out_mux_cfg(const P_OUT_CFG out, const P_OE_CFG oe) {
|
||||
return
|
||||
(toUType(out) << 0)
|
||||
| (toUType(oe) << 4)
|
||||
;
|
||||
}
|
||||
|
||||
constexpr uint32_t data_sgpio_mux_cfg(
|
||||
const CONCAT_ENABLE concat_enable,
|
||||
const CONCAT_ORDER concat_order
|
||||
) {
|
||||
return
|
||||
(1U << 0)
|
||||
| (0U << 1)
|
||||
| (0U << 3)
|
||||
| (3U << 5)
|
||||
| (1U << 7)
|
||||
| (0U << 9)
|
||||
| (toUType(concat_enable) << 11)
|
||||
| (toUType(concat_order) << 12)
|
||||
;
|
||||
}
|
||||
|
||||
constexpr uint32_t data_slice_mux_cfg(
|
||||
const PARALLEL_MODE parallel_mode,
|
||||
const CLK_CAPTURE_MODE clk_capture_mode
|
||||
) {
|
||||
return
|
||||
(0U << 0)
|
||||
| (toUType(clk_capture_mode) << 1)
|
||||
| (1U << 2)
|
||||
| (0U << 3)
|
||||
| (0U << 4)
|
||||
| (toUType(parallel_mode) << 6)
|
||||
| (0U << 8)
|
||||
;
|
||||
}
|
||||
|
||||
constexpr uint32_t pos(
|
||||
const uint32_t pos,
|
||||
const uint32_t pos_reset
|
||||
) {
|
||||
return
|
||||
(pos << 0)
|
||||
| (pos_reset << 8)
|
||||
;
|
||||
}
|
||||
constexpr uint32_t data_pos(
|
||||
const bool multi_slice
|
||||
) {
|
||||
return pos(
|
||||
(multi_slice ? pos_count_multi_slice : pos_count_single_slice),
|
||||
(multi_slice ? pos_count_multi_slice : pos_count_single_slice)
|
||||
);
|
||||
}
|
||||
|
||||
constexpr CONCAT_ENABLE data_concat_enable(
|
||||
const bool input_slice,
|
||||
const bool single_slice
|
||||
) {
|
||||
return (input_slice || single_slice)
|
||||
? CONCAT_ENABLE::EXTERNAL_DATA_PIN
|
||||
: CONCAT_ENABLE::CONCATENATE_DATA
|
||||
;
|
||||
}
|
||||
|
||||
constexpr CONCAT_ORDER data_concat_order(
|
||||
const bool input_slice,
|
||||
const bool single_slice
|
||||
) {
|
||||
return (input_slice || single_slice)
|
||||
? CONCAT_ORDER::SELF_LOOP
|
||||
: CONCAT_ORDER::EIGHT_SLICES
|
||||
;
|
||||
}
|
||||
|
||||
constexpr CLK_CAPTURE_MODE data_clk_capture_mode(
|
||||
const Direction direction
|
||||
) {
|
||||
return (direction == Direction::Transmit)
|
||||
? CLK_CAPTURE_MODE::RISING_CLOCK_EDGE
|
||||
: CLK_CAPTURE_MODE::FALLING_CLOCK_EDGE
|
||||
;
|
||||
}
|
||||
|
||||
constexpr P_OUT_CFG data_p_out_cfg(
|
||||
const bool multi_slice
|
||||
) {
|
||||
return (multi_slice)
|
||||
? P_OUT_CFG::DOUT_DOUTM8C
|
||||
: P_OUT_CFG::DOUT_DOUTM8A
|
||||
;
|
||||
}
|
||||
|
||||
void SGPIO::configure(const Direction direction) {
|
||||
disable_all_slice_counters();
|
||||
|
||||
LPC_SGPIO->GPIO_OUTREG = gpio_outreg(direction);
|
||||
LPC_SGPIO->GPIO_OENREG = gpio_oenreg(direction);
|
||||
LPC_SGPIO->OUT_MUX_CFG[ 8] = out_mux_cfg(P_OUT_CFG::DOUT_DOUTM1, P_OE_CFG::GPIO_OE);
|
||||
LPC_SGPIO->OUT_MUX_CFG[ 9] = out_mux_cfg(P_OUT_CFG::DOUT_DOUTM1, P_OE_CFG::GPIO_OE);
|
||||
LPC_SGPIO->OUT_MUX_CFG[10] = out_mux_cfg(P_OUT_CFG::GPIO_OUT, P_OE_CFG::GPIO_OE);
|
||||
LPC_SGPIO->OUT_MUX_CFG[11] = out_mux_cfg(P_OUT_CFG::GPIO_OUT, P_OE_CFG::GPIO_OE);
|
||||
LPC_SGPIO->OUT_MUX_CFG[12] = out_mux_cfg(P_OUT_CFG::GPIO_OUT, P_OE_CFG::GPIO_OE);
|
||||
LPC_SGPIO->OUT_MUX_CFG[13] = out_mux_cfg(P_OUT_CFG::GPIO_OUT, P_OE_CFG::GPIO_OE);
|
||||
LPC_SGPIO->OUT_MUX_CFG[14] = out_mux_cfg(P_OUT_CFG::DOUT_DOUTM1, P_OE_CFG::GPIO_OE);
|
||||
LPC_SGPIO->OUT_MUX_CFG[15] = out_mux_cfg(P_OUT_CFG::GPIO_OUT, P_OE_CFG::GPIO_OE);
|
||||
|
||||
const auto data_out_mux_cfg = out_mux_cfg(data_p_out_cfg(slice_mode_multislice), P_OE_CFG::GPIO_OE);
|
||||
for(size_t i=0; i<8; i++) {
|
||||
LPC_SGPIO->OUT_MUX_CFG[i] = data_out_mux_cfg;
|
||||
}
|
||||
|
||||
const auto slice_gpdma = Slice::H;
|
||||
|
||||
const size_t slice_count = slice_mode_multislice ? 8 : 1;
|
||||
const auto clk_capture_mode = data_clk_capture_mode(direction);
|
||||
const auto single_slice = !slice_mode_multislice;
|
||||
|
||||
uint32_t slice_enable_mask = 0;
|
||||
for(size_t i=0; i<slice_count; i++) {
|
||||
const auto slice = slice_order[i];
|
||||
const auto slice_index = toUType(slice);
|
||||
const auto input_slice = (i == 0) && (direction != Direction::Transmit);
|
||||
const auto concat_order = data_concat_order(input_slice, single_slice);
|
||||
const auto concat_enable = data_concat_enable(input_slice, single_slice);
|
||||
|
||||
LPC_SGPIO->SGPIO_MUX_CFG[slice_index] = data_sgpio_mux_cfg(
|
||||
concat_enable,
|
||||
concat_order
|
||||
);
|
||||
LPC_SGPIO->SLICE_MUX_CFG[slice_index] = data_slice_mux_cfg(
|
||||
PARALLEL_MODE::SHIFT_1_BYTE_PER_CLOCK,
|
||||
clk_capture_mode
|
||||
);
|
||||
|
||||
LPC_SGPIO->PRESET[slice_index] = 0;
|
||||
LPC_SGPIO->COUNT[slice_index] = 0;
|
||||
LPC_SGPIO->POS[slice_index] = data_pos(slice_mode_multislice);
|
||||
LPC_SGPIO->REG[slice_index] = 0;
|
||||
LPC_SGPIO->REG_SS[slice_index] = 0;
|
||||
|
||||
slice_enable_mask |= (1U << slice_index);
|
||||
}
|
||||
|
||||
if( !slice_mode_multislice ) {
|
||||
const auto slice_index = toUType(slice_gpdma);
|
||||
|
||||
LPC_SGPIO->SGPIO_MUX_CFG[slice_index] = data_sgpio_mux_cfg(
|
||||
CONCAT_ENABLE::CONCATENATE_DATA,
|
||||
CONCAT_ORDER::SELF_LOOP
|
||||
);
|
||||
LPC_SGPIO->SLICE_MUX_CFG[slice_index] = data_slice_mux_cfg(
|
||||
PARALLEL_MODE::SHIFT_1_BIT_PER_CLOCK,
|
||||
clk_capture_mode
|
||||
);
|
||||
|
||||
LPC_SGPIO->PRESET[slice_index] = 0;
|
||||
LPC_SGPIO->COUNT[slice_index] = 0;
|
||||
LPC_SGPIO->POS[slice_index] = pos(0x1f, 0x1f);
|
||||
LPC_SGPIO->REG[slice_index] = 0x11111111;
|
||||
LPC_SGPIO->REG_SS[slice_index] = 0x11111111;
|
||||
|
||||
slice_enable_mask |= (1 << slice_index);
|
||||
}
|
||||
|
||||
set_slice_counter_enables(slice_enable_mask);
|
||||
}
|
||||
|
||||
} /* namespace baseband */
|
67
firmware/common/baseband_sgpio.hpp
Normal file
67
firmware/common/baseband_sgpio.hpp
Normal file
@@ -0,0 +1,67 @@
|
||||
/*
|
||||
* Copyright (C) 2014 Jared Boone, ShareBrained Technology, Inc.
|
||||
*
|
||||
* 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 __BASEBAND_GPIO_H__
|
||||
#define __BASEBAND_GPIO_H__
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
#include "baseband.hpp"
|
||||
|
||||
#include "hal.h"
|
||||
|
||||
namespace baseband {
|
||||
|
||||
class SGPIO {
|
||||
public:
|
||||
void init() {
|
||||
}
|
||||
|
||||
void configure(const Direction direction);
|
||||
|
||||
void streaming_enable() {
|
||||
/* TODO: Any reason not to control from general GPIO facility? */
|
||||
LPC_SGPIO->GPIO_OUTREG &= ~(1U << 10);
|
||||
}
|
||||
|
||||
void streaming_disable() {
|
||||
/* TODO: Any reason not to control from general GPIO facility? */
|
||||
LPC_SGPIO->GPIO_OUTREG |= (1U << 10);
|
||||
}
|
||||
|
||||
bool streaming_is_enabled() const {
|
||||
/* TODO: Any reason not to control from general GPIO facility? */
|
||||
return (LPC_SGPIO->GPIO_OUTREG >> 10) & 1;
|
||||
}
|
||||
|
||||
private:
|
||||
void disable_all_slice_counters() {
|
||||
set_slice_counter_enables(0);
|
||||
}
|
||||
|
||||
void set_slice_counter_enables(const uint16_t enable_mask) {
|
||||
LPC_SGPIO->CTRL_ENABLE = enable_mask;
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif/*__BASEBAND_GPIO_H__*/
|
@@ -31,12 +31,12 @@ public:
|
||||
history = (history << 1) | (bit & 1);
|
||||
}
|
||||
|
||||
uint32_t value() const {
|
||||
uint64_t value() const {
|
||||
return history;
|
||||
}
|
||||
|
||||
private:
|
||||
uint32_t history { 0 };
|
||||
uint64_t history { 0 };
|
||||
};
|
||||
|
||||
class BitPattern {
|
||||
@@ -49,24 +49,24 @@ public:
|
||||
}
|
||||
|
||||
constexpr BitPattern(
|
||||
const uint32_t code,
|
||||
const uint64_t code,
|
||||
const size_t code_length,
|
||||
const size_t maximum_hanning_distance = 0
|
||||
) : code_ { code },
|
||||
mask_ { (1U << code_length) - 1U },
|
||||
mask_ { (1ULL << code_length) - 1ULL },
|
||||
maximum_hanning_distance_ { maximum_hanning_distance }
|
||||
{
|
||||
}
|
||||
|
||||
bool operator()(const BitHistory& history, const size_t) const {
|
||||
const auto delta_bits = (history.value() ^ code_) & mask_;
|
||||
const size_t count = __builtin_popcountl(delta_bits);
|
||||
const size_t count = __builtin_popcountll(delta_bits);
|
||||
return (count <= maximum_hanning_distance_);
|
||||
}
|
||||
|
||||
private:
|
||||
uint32_t code_;
|
||||
uint32_t mask_;
|
||||
uint64_t code_;
|
||||
uint64_t mask_;
|
||||
size_t maximum_hanning_distance_;
|
||||
};
|
||||
|
||||
|
@@ -25,30 +25,77 @@
|
||||
#include <cstdint>
|
||||
#include <cstddef>
|
||||
|
||||
/* LPC43xx RTC structure. Avoiding using the ChibiOS-defined structure because
|
||||
* it pulls in all sorts of dependencies and initialization and other stuff that
|
||||
* the M0 needs to remain in control of.
|
||||
*
|
||||
* But yes, this is a hack, and something better is needed. It's too tangled of
|
||||
* a knot to tackle at the moment, though...
|
||||
*/
|
||||
#if defined(LPC43XX_M4)
|
||||
#include "lpc43xx_m4.h"
|
||||
|
||||
struct Timestamp {
|
||||
uint32_t tv_date { 0 };
|
||||
uint32_t tv_time { 0 };
|
||||
|
||||
static Timestamp now() {
|
||||
// Code stolen from LPC43xx rtc_lld.c
|
||||
Timestamp timestamp;
|
||||
do {
|
||||
timestamp.tv_time = LPC_RTC->CTIME0;
|
||||
timestamp.tv_date = LPC_RTC->CTIME1;
|
||||
} while( (timestamp.tv_time != LPC_RTC->CTIME0) || (timestamp.tv_date != LPC_RTC->CTIME1) );
|
||||
return timestamp;
|
||||
}
|
||||
};
|
||||
#endif
|
||||
|
||||
#if defined(LPC43XX_M0)
|
||||
#include "lpc43xx_cpp.hpp"
|
||||
|
||||
using Timestamp = lpc43xx::rtc::RTC;
|
||||
#endif
|
||||
|
||||
template<typename T>
|
||||
struct buffer_t {
|
||||
T* const p;
|
||||
const size_t count;
|
||||
const uint32_t sampling_rate;
|
||||
const Timestamp timestamp;
|
||||
|
||||
constexpr buffer_t(
|
||||
T* const p,
|
||||
const size_t count
|
||||
) : p { p },
|
||||
count { count },
|
||||
sampling_rate { 0 }
|
||||
) : p { nullptr },
|
||||
count { 0 },
|
||||
sampling_rate { 0 },
|
||||
timestamp { }
|
||||
{
|
||||
};
|
||||
}
|
||||
|
||||
constexpr buffer_t(
|
||||
const buffer_t<T>& other
|
||||
) : p { other.p },
|
||||
count { other.count },
|
||||
sampling_rate { other.sampling_rate },
|
||||
timestamp { other.timestamp }
|
||||
{
|
||||
}
|
||||
|
||||
constexpr buffer_t(
|
||||
T* const p,
|
||||
const size_t count,
|
||||
const uint32_t sampling_rate
|
||||
const uint32_t sampling_rate = 0,
|
||||
const Timestamp timestamp = { }
|
||||
) : p { p },
|
||||
count { count },
|
||||
sampling_rate { sampling_rate }
|
||||
sampling_rate { sampling_rate },
|
||||
timestamp { timestamp }
|
||||
{
|
||||
};
|
||||
}
|
||||
|
||||
operator bool() const {
|
||||
return (p != nullptr);
|
||||
}
|
||||
};
|
||||
|
||||
#endif/*__BUFFER_H__*/
|
||||
|
@@ -66,8 +66,8 @@ public:
|
||||
|
||||
private:
|
||||
union {
|
||||
int8_t _v[2];
|
||||
uint16_t _rep;
|
||||
value_type _v[2];
|
||||
rep_type _rep;
|
||||
};
|
||||
};
|
||||
|
||||
@@ -101,14 +101,28 @@ public:
|
||||
void real(int16_t v) { _v[0] = v; }
|
||||
void imag(int16_t v) { _v[1] = v; }
|
||||
|
||||
template<class X>
|
||||
complex<int16_t>& operator+=(const complex<X>& other) {
|
||||
_v[0] += other.real();
|
||||
_v[1] += other.imag();
|
||||
return *this;
|
||||
}
|
||||
|
||||
constexpr uint32_t __rep() const {
|
||||
return _rep;
|
||||
}
|
||||
|
||||
constexpr operator std::complex<float>() const {
|
||||
return {
|
||||
static_cast<float>(_v[0]),
|
||||
static_cast<float>(_v[1])
|
||||
};
|
||||
}
|
||||
|
||||
private:
|
||||
union {
|
||||
int16_t _v[2];
|
||||
uint32_t _rep;
|
||||
value_type _v[2];
|
||||
rep_type _rep;
|
||||
};
|
||||
};
|
||||
|
||||
|
@@ -25,61 +25,74 @@
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
|
||||
/* Inspired by
|
||||
* http://www.barrgroup.com/Embedded-Systems/How-To/CRC-Calculation-C-Code
|
||||
*
|
||||
* ...then munged into a shape resembling boost::crc_basic.
|
||||
* http://www.boost.org/doc/libs/release/libs/crc/
|
||||
*/
|
||||
|
||||
template<typename T>
|
||||
class CRC {
|
||||
public:
|
||||
constexpr CRC(
|
||||
const T polynomial/*,
|
||||
const T initial*/
|
||||
) : polynomial { polynomial }/*,
|
||||
initial { initial }*/
|
||||
const T truncated_polynomial,
|
||||
const T initial_remainder = 0,
|
||||
const T final_xor_value = 0
|
||||
) : truncated_polynomial { truncated_polynomial },
|
||||
initial_remainder { initial_remainder },
|
||||
final_xor_value { final_xor_value },
|
||||
remainder { initial_remainder }
|
||||
{
|
||||
// CRC LSB must always be 1
|
||||
}
|
||||
/*
|
||||
template<typename U>
|
||||
T calculate(const U& bits, const size_t length) {
|
||||
if( length > bits.size() ) {
|
||||
// Exception.
|
||||
return 0;
|
||||
}
|
||||
|
||||
T crc = 0;
|
||||
|
||||
for(size_t i=0; i<length; i++) {
|
||||
crc = feed_bit(crc, bits[i]);
|
||||
}
|
||||
|
||||
return crc;
|
||||
T get_initial_remainder() const {
|
||||
return initial_remainder;
|
||||
}
|
||||
*/
|
||||
T calculate_byte(const T crc_in, const uint8_t data) {
|
||||
/* Inspired by
|
||||
* http://www.barrgroup.com/Embedded-Systems/How-To/CRC-Calculation-C-Code
|
||||
*/
|
||||
T remainder = crc_in;
|
||||
|
||||
void reset(T new_initial_remainder) {
|
||||
remainder = new_initial_remainder;
|
||||
}
|
||||
|
||||
void reset() {
|
||||
remainder = initial_remainder;
|
||||
}
|
||||
|
||||
void process_bit(bool bit) {
|
||||
remainder ^= bit << (width() - 1);
|
||||
if( remainder & top_bit() ) {
|
||||
remainder = (remainder << 1) ^ truncated_polynomial;
|
||||
} else {
|
||||
remainder = (remainder << 1);
|
||||
}
|
||||
}
|
||||
|
||||
void process_byte(const uint8_t data) {
|
||||
remainder ^= data << (width() - 8);
|
||||
for(size_t bit=8; bit>0; --bit) {
|
||||
for(size_t bit=0; bit<8; bit++) {
|
||||
if( remainder & top_bit() ) {
|
||||
remainder = (remainder << 1) ^ polynomial;
|
||||
remainder = (remainder << 1) ^ truncated_polynomial;
|
||||
} else {
|
||||
remainder = (remainder << 1);
|
||||
}
|
||||
}
|
||||
return remainder;
|
||||
}
|
||||
/*
|
||||
T calculate(const uint8_t* const data, const size_t length) {
|
||||
T remainder = initial;
|
||||
for(size_t byte=0; byte<length; ++byte) {
|
||||
remainder = calculate_byte(remainder, data[byte]);
|
||||
|
||||
void process_bytes(const uint8_t* const data, const size_t length) {
|
||||
for(size_t i=0; i<length; i++) {
|
||||
process_byte(data[i]);
|
||||
}
|
||||
return remainder;
|
||||
}
|
||||
*/
|
||||
|
||||
T checksum() const {
|
||||
return remainder ^ final_xor_value;
|
||||
}
|
||||
|
||||
private:
|
||||
const T polynomial;
|
||||
// const T initial;
|
||||
const T truncated_polynomial;
|
||||
const T initial_remainder;
|
||||
const T final_xor_value;
|
||||
T remainder;
|
||||
|
||||
static constexpr size_t width() {
|
||||
return 8 * sizeof(T);
|
||||
@@ -88,16 +101,6 @@ private:
|
||||
static constexpr T top_bit() {
|
||||
return 1U << (width() - 1);
|
||||
}
|
||||
/*
|
||||
T feed_bit(const T crc_in, const uint_fast8_t bit) {
|
||||
T crc_out = crc_in ^ (bit & 1);
|
||||
if( crc_in & top_bit() ) {
|
||||
return ((crc_in << 1) | (bit & 1)) ^ polynomial;
|
||||
} else {
|
||||
return (crc_in << 1) | (bit & 1);
|
||||
}
|
||||
}
|
||||
*/
|
||||
};
|
||||
|
||||
|
||||
|
@@ -31,7 +31,6 @@
|
||||
|
||||
#include "dsp_types.hpp"
|
||||
#include "complex.hpp"
|
||||
#include "sine_table.hpp"
|
||||
#include "hal.h"
|
||||
|
||||
namespace std {
|
||||
@@ -110,15 +109,25 @@ void fft_swap_in_place(std::array<T, N>& data) {
|
||||
template<typename T, size_t N>
|
||||
void fft_c_preswapped(std::array<T, N>& data) {
|
||||
static_assert(power_of_two(N), "only defined for N == power of two");
|
||||
constexpr auto K = log_2(N);
|
||||
|
||||
constexpr size_t K_max = 8;
|
||||
static_assert(K <= K_max, "No FFT twiddle factors for K > 8");
|
||||
static constexpr std::array<std::complex<float>, K_max> wp_table { {
|
||||
{ -2.0f, 0.0f },
|
||||
{ -1.0f, -1.0f },
|
||||
{ -0.2928932188134524756f, -0.7071067811865475244f },
|
||||
{ -0.076120467488713243872f, -0.38268343236508977173f },
|
||||
{ -0.019214719596769550874f, -0.19509032201612826785f },
|
||||
{ -0.0048152733278031137552f, -0.098017140329560601994f },
|
||||
{ -0.0012045437948276072852f, -0.049067674327418014255f },
|
||||
{ -0.00030118130379577988423f, -0.024541228522912288032f },
|
||||
} };
|
||||
|
||||
/* Provide data to this function, pre-swapped. */
|
||||
for(size_t mmax = 1; N > mmax; mmax <<= 1) {
|
||||
const float theta = -pi / mmax;
|
||||
const float wtemp = sin_f32(0.5f * theta);
|
||||
const T wp {
|
||||
-2.0f * wtemp * wtemp,
|
||||
sin_f32(theta)
|
||||
};
|
||||
for(size_t k = 0; k < log_2(N); k++) {
|
||||
const size_t mmax = 1 << k;
|
||||
const auto wp = wp_table[k];
|
||||
T w { 1.0f, 0.0f };
|
||||
for(size_t m = 0; m < mmax; ++m) {
|
||||
for(size_t i = m; i < N; i += mmax * 2) {
|
||||
|
22
firmware/common/dsp_fir_taps.cpp
Normal file
22
firmware/common/dsp_fir_taps.cpp
Normal file
@@ -0,0 +1,22 @@
|
||||
/*
|
||||
* Copyright (C) 2015 Jared Boone, ShareBrained Technology, Inc.
|
||||
*
|
||||
* 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 "dsp_fir_taps.hpp"
|
230
firmware/common/dsp_fir_taps.hpp
Normal file
230
firmware/common/dsp_fir_taps.hpp
Normal file
@@ -0,0 +1,230 @@
|
||||
/*
|
||||
* Copyright (C) 2015 Jared Boone, ShareBrained Technology, Inc.
|
||||
*
|
||||
* 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 __DSP_FIR_TAPS_H__
|
||||
#define __DSP_FIR_TAPS_H__
|
||||
|
||||
#include <cstdint>
|
||||
#include <array>
|
||||
|
||||
#include "complex.hpp"
|
||||
|
||||
template<size_t N>
|
||||
struct fir_taps_real {
|
||||
float pass_frequency_normalized;
|
||||
float stop_frequency_normalized;
|
||||
std::array<int16_t, N> taps;
|
||||
};
|
||||
|
||||
// NBFM 16K0F3E emission type /////////////////////////////////////////////
|
||||
|
||||
// IFIR image-reject filter: fs=3072000, pass=8000, stop=344000, decim=8, fout=384000
|
||||
constexpr fir_taps_real<24> taps_16k0_decim_0 {
|
||||
.pass_frequency_normalized = 8000.0f / 3072000.0f,
|
||||
.stop_frequency_normalized = 344000.0f / 3072000.0f,
|
||||
.taps = { {
|
||||
1, 67, 165, 340, 599, 944, 1361, 1820,
|
||||
2278, 2684, 2988, 3152, 3152, 2988, 2684, 2278,
|
||||
1820, 1361, 944, 599, 340, 165, 67, 1,
|
||||
} },
|
||||
};
|
||||
|
||||
// IFIR prototype filter: fs=384000, pass=8000, stop=40000, decim=8, fout=48000
|
||||
constexpr fir_taps_real<32> taps_16k0_decim_1 {
|
||||
.pass_frequency_normalized = 8000.0f / 384000.0f,
|
||||
.stop_frequency_normalized = 40000.0f / 384000.0f,
|
||||
.taps = { {
|
||||
-26, -125, -180, -275, -342, -359, -286, -90,
|
||||
250, 733, 1337, 2011, 2688, 3289, 3740, 3982,
|
||||
3982, 3740, 3289, 2688, 2011, 1337, 733, 250,
|
||||
-90, -286, -359, -342, -275, -180, -125, -26,
|
||||
} },
|
||||
};
|
||||
|
||||
// Channel filter: fs=48000, pass=8000, stop=12400, decim=1, fout=48000
|
||||
constexpr fir_taps_real<32> taps_16k0_channel {
|
||||
.pass_frequency_normalized = 8000.0f / 48000.0f,
|
||||
.stop_frequency_normalized = 12400.0f / 48000.0f,
|
||||
.taps = { {
|
||||
-73, -285, -376, -8, 609, 538, -584, -1387,
|
||||
-148, 2173, 1959, -2146, -5267, -297, 12915, 24737,
|
||||
24737, 12915, -297, -5267, -2146, 1959, 2173, -148,
|
||||
-1387, -584, 538, 609, -8, -376, -285, -73,
|
||||
} },
|
||||
};
|
||||
|
||||
// NBFM 11K0F3E emission type /////////////////////////////////////////////
|
||||
|
||||
// IFIR image-reject filter: fs=3072000, pass=5500, stop=341500, decim=8, fout=384000
|
||||
constexpr fir_taps_real<24> taps_11k0_decim_0 {
|
||||
.pass_frequency_normalized = 5500.0f / 3072000.0f,
|
||||
.stop_frequency_normalized = 341500.0f / 3072000.0f,
|
||||
.taps = { {
|
||||
38, 102, 220, 406, 668, 1004, 1397, 1822,
|
||||
2238, 2603, 2875, 3020, 3020, 2875, 2603, 2238,
|
||||
1822, 1397, 1004, 668, 406, 220, 102, 38,
|
||||
} },
|
||||
};
|
||||
|
||||
// IFIR prototype filter: fs=384000, pass=5500, stop=42500, decim=8, fout=48000
|
||||
constexpr fir_taps_real<32> taps_11k0_decim_1 {
|
||||
.pass_frequency_normalized = 5500.0f / 384000.0f,
|
||||
.stop_frequency_normalized = 42500.0f / 384000.0f,
|
||||
.taps = { {
|
||||
-42, -87, -157, -234, -298, -318, -255, -75,
|
||||
246, 713, 1306, 1976, 2656, 3265, 3724, 3971,
|
||||
3971, 3724, 3265, 2656, 1976, 1306, 713, 246,
|
||||
-75, -255, -318, -298, -234, -157, -87, -42,
|
||||
} },
|
||||
};
|
||||
|
||||
// Channel filter: fs=48000, pass=5500, stop=8900, decim=1, fout=48000
|
||||
constexpr fir_taps_real<32> taps_11k0_channel {
|
||||
.pass_frequency_normalized = 5500.0f / 48000.0f,
|
||||
.stop_frequency_normalized = 8900.0f / 48000.0f,
|
||||
.taps = { {
|
||||
-68, -345, -675, -867, -582, 247, 1222, 1562,
|
||||
634, -1379, -3219, -3068, 310, 6510, 13331, 17795,
|
||||
17795, 13331, 6510, 310, -3068, -3219, -1379, 634,
|
||||
1562, 1222, 247, -582, -867, -675, -345, -68,
|
||||
} },
|
||||
};
|
||||
|
||||
/// NBFM 8K50F3E emission type ////////////////////////////////////////////
|
||||
|
||||
// IFIR image-reject filter: fs=3072000, pass=4250, stop=340250, decim=8, fout=384000
|
||||
constexpr fir_taps_real<24> taps_4k25_decim_0 {
|
||||
.pass_frequency_normalized = 4250.0f / 3072000.0f,
|
||||
.stop_frequency_normalized = 340250.0f / 3072000.0f,
|
||||
.taps = { {
|
||||
38, 103, 222, 409, 671, 1006, 1399, 1821,
|
||||
2236, 2599, 2868, 3012, 3012, 2868, 2599, 2236,
|
||||
1821, 1399, 1006, 671, 409, 222, 103, 38,
|
||||
} },
|
||||
};
|
||||
|
||||
// IFIR prototype filter: fs=384000, pass=4250, stop=43750, decim=8, fout=48000
|
||||
constexpr fir_taps_real<32> taps_4k25_decim_1 {
|
||||
.pass_frequency_normalized = 4250.0f / 384000.0f,
|
||||
.stop_frequency_normalized = 43750.0f / 384000.0f,
|
||||
.taps = { {
|
||||
-33, -74, -139, -214, -280, -306, -254, -87,
|
||||
222, 682, 1274, 1951, 2644, 3268, 3741, 3996,
|
||||
3996, 3741, 3268, 2644, 1951, 1274, 682, 222,
|
||||
-87, -254, -306, -280, -214, -139, -74, -33,
|
||||
} },
|
||||
};
|
||||
|
||||
// Channel filter: fs=48000, pass=4250, stop=7900, decim=1, fout=48000
|
||||
constexpr fir_taps_real<32> taps_4k25_channel {
|
||||
.pass_frequency_normalized = 4250.0f / 48000.0f,
|
||||
.stop_frequency_normalized = 7900.0f / 48000.0f,
|
||||
.taps = { {
|
||||
-58, -14, 153, 484, 871, 1063, 770, -141,
|
||||
-1440, -2488, -2435, -614, 3035, 7771, 12226, 14927,
|
||||
14927, 12226, 7771, 3035, -614, -2435, -2488, -1440,
|
||||
-141, 770, 1063, 871, 484, 153, -14, -58,
|
||||
} },
|
||||
};
|
||||
|
||||
// DSB AM 6K00A3E emission type ///////////////////////////////////////////
|
||||
|
||||
// IFIR image-reject filter: fs=3072000, pass=3000, stop=339000, decim=8, fout=384000
|
||||
constexpr fir_taps_real<24> taps_6k0_decim_0 {
|
||||
.pass_frequency_normalized = 3000.0f / 3072000.0f,
|
||||
.stop_frequency_normalized = 339000.0f / 3072000.0f,
|
||||
.taps = { {
|
||||
39, 104, 224, 412, 674, 1008, 1400, 1821,
|
||||
2234, 2594, 2863, 3006, 3006, 2863, 2594, 2234,
|
||||
1821, 1400, 1008, 674, 412, 224, 104, 39,
|
||||
} },
|
||||
};
|
||||
|
||||
// IFIR prototype filter: fs=384000, pass=3000, stop=45000, decim=8, fout=48000
|
||||
constexpr fir_taps_real<32> taps_6k0_decim_1 {
|
||||
.pass_frequency_normalized = 3000.0f / 384000.0f,
|
||||
.stop_frequency_normalized = 45000.0f / 384000.0f,
|
||||
.taps = { {
|
||||
-26, -63, -123, -195, -263, -295, -253, -99,
|
||||
199, 651, 1242, 1927, 2633, 3273, 3760, 4023,
|
||||
4023, 3760, 3273, 2633, 1927, 1242, 651, 199,
|
||||
-99, -253, -295, -263, -195, -123, -63, -26,
|
||||
} },
|
||||
};
|
||||
|
||||
// Channel filter: fs=48000, pass=3000, stop=6700, decim=1, fout=48000
|
||||
constexpr fir_taps_real<32> taps_6k0_channel {
|
||||
.pass_frequency_normalized = 3000.0f / 48000.0f,
|
||||
.stop_frequency_normalized = 6700.0f / 48000.0f,
|
||||
.taps = { {
|
||||
95, 178, 247, 208, -21, -474, -1080, -1640,
|
||||
-1857, -1411, -83, 2134, 4978, 7946, 10413, 11815,
|
||||
11815, 10413, 7946, 4978, 2134, -83, -1411, -1857,
|
||||
-1640, -1080, -474, -21, 208, 247, 178, 95,
|
||||
} },
|
||||
};
|
||||
|
||||
// WFM 200KF8E emission type //////////////////////////////////////////////
|
||||
|
||||
// IFIR image-reject filter: fs=3072000, pass=100000, stop=484000, decim=4, fout=768000
|
||||
constexpr fir_taps_real<24> taps_200k_wfm_decim_0 = {
|
||||
.pass_frequency_normalized = 100000.0f / 3072000.0f,
|
||||
.stop_frequency_normalized = 484000.0f / 3072000.0f,
|
||||
.taps = { {
|
||||
48, -18, -151, -364, -557, -548, -139, 789,
|
||||
2187, 3800, 5230, 6071, 6071, 5230, 3800, 2187,
|
||||
789, -139, -548, -557, -364, -151, -18, 48,
|
||||
} },
|
||||
};
|
||||
|
||||
// IFIR prototype filter: fs=768000, pass=100000, stop=284000, decim=2, fout=384000
|
||||
constexpr fir_taps_real<16> taps_200k_wfm_decim_1 = {
|
||||
.pass_frequency_normalized = 100000.0f / 768000.0f,
|
||||
.stop_frequency_normalized = 284000.0f / 768000.0f,
|
||||
.taps = { {
|
||||
-67, -123, 388, 622, -1342, -2185, 4599, 14486,
|
||||
14486, 4599, -2185, -1342, 622, 388, -123, -67,
|
||||
} },
|
||||
};
|
||||
|
||||
/* Wideband audio filter */
|
||||
/* 96kHz int16_t input
|
||||
* -> FIR filter, <15kHz (0.156fs) pass, >19kHz (0.198fs) stop
|
||||
* -> 48kHz int16_t output, gain of 1.0 (I think).
|
||||
* Padded to multiple of four taps for unrolled FIR code.
|
||||
* sum(abs(taps)): 125270
|
||||
*/
|
||||
constexpr fir_taps_real<64> taps_64_lp_156_198 {
|
||||
.pass_frequency_normalized = 0.156f,
|
||||
.stop_frequency_normalized = 0.196f,
|
||||
.taps = { {
|
||||
-27, 166, 104, -36, -174, -129, 109, 287,
|
||||
148, -232, -430, -130, 427, 597, 49, -716,
|
||||
-778, 137, 1131, 957, -493, -1740, -1121, 1167,
|
||||
2733, 1252, -2633, -4899, -1336, 8210, 18660, 23254,
|
||||
18660, 8210, -1336, -4899, -2633, 1252, 2733, 1167,
|
||||
-1121, -1740, -493, 957, 1131, 137, -778, -716,
|
||||
49, 597, 427, -130, -430, -232, 148, 287,
|
||||
109, -129, -174, -36, 104, 166, -27, 0,
|
||||
} },
|
||||
};
|
||||
|
||||
#endif/*__DSP_FIR_TAPS_H__*/
|
@@ -30,5 +30,7 @@
|
||||
using buffer_c8_t = buffer_t<complex8_t>;
|
||||
using buffer_c16_t = buffer_t<complex16_t>;
|
||||
using buffer_s16_t = buffer_t<int16_t>;
|
||||
using buffer_c32_t = buffer_t<complex32_t>;
|
||||
using buffer_f32_t = buffer_t<float>;
|
||||
|
||||
#endif/*__DSP_TYPES_H__*/
|
||||
|
96
firmware/common/ert_packet.cpp
Normal file
96
firmware/common/ert_packet.cpp
Normal file
@@ -0,0 +1,96 @@
|
||||
/*
|
||||
* Copyright (C) 2015 Jared Boone, ShareBrained Technology, Inc.
|
||||
*
|
||||
* 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 "ert_packet.hpp"
|
||||
|
||||
#include "crc.hpp"
|
||||
|
||||
namespace ert {
|
||||
|
||||
size_t Packet::length() const {
|
||||
return decoder_.symbols_count();
|
||||
}
|
||||
|
||||
bool Packet::is_valid() const {
|
||||
return true;
|
||||
}
|
||||
|
||||
Timestamp Packet::received_at() const {
|
||||
return packet_.timestamp();
|
||||
}
|
||||
|
||||
Packet::Type Packet::type() const {
|
||||
return type_;
|
||||
}
|
||||
|
||||
ID Packet::id() const {
|
||||
if( type() == Type::SCM ) {
|
||||
const auto msb = reader_.read(0, 2);
|
||||
const auto lsb = reader_.read(35, 24);
|
||||
return (msb << 24) | lsb;
|
||||
}
|
||||
if( type() == Type::IDM ) {
|
||||
return reader_.read(5 * 8, 32);
|
||||
}
|
||||
return invalid_id;
|
||||
}
|
||||
|
||||
Consumption Packet::consumption() const {
|
||||
if( type() == Type::SCM ) {
|
||||
return reader_.read(11, 24);
|
||||
}
|
||||
if( type() == Type::IDM ) {
|
||||
return reader_.read(25 * 8, 32);
|
||||
}
|
||||
return invalid_consumption;
|
||||
}
|
||||
|
||||
ManchesterFormatted Packet::symbols_formatted() const {
|
||||
return format_manchester(decoder_);
|
||||
}
|
||||
|
||||
bool Packet::crc_ok() const {
|
||||
switch(type()) {
|
||||
case Type::SCM: return crc_ok_scm();
|
||||
case Type::IDM: return crc_ok_idm();
|
||||
default: return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool Packet::crc_ok_scm() const {
|
||||
CRC<uint16_t> ert_bch { 0x6f63 };
|
||||
size_t start_bit = 5;
|
||||
ert_bch.process_byte(reader_.read(0, start_bit));
|
||||
for(size_t i=start_bit; i<length(); i+=8) {
|
||||
ert_bch.process_byte(reader_.read(i, 8));
|
||||
}
|
||||
return ert_bch.checksum() == 0x0000;
|
||||
}
|
||||
|
||||
bool Packet::crc_ok_idm() const {
|
||||
CRC<uint16_t> ert_crc_ccitt { 0x1021, 0xffff, 0x1d0f };
|
||||
for(size_t i=0; i<length(); i+=8) {
|
||||
ert_crc_ccitt.process_byte(reader_.read(i, 8));
|
||||
}
|
||||
return ert_crc_ccitt.checksum() == 0x0000;
|
||||
}
|
||||
|
||||
} /* namespace ert */
|
86
firmware/common/ert_packet.hpp
Normal file
86
firmware/common/ert_packet.hpp
Normal file
@@ -0,0 +1,86 @@
|
||||
/*
|
||||
* Copyright (C) 2015 Jared Boone, ShareBrained Technology, Inc.
|
||||
*
|
||||
* 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 __ERT_PACKET_H__
|
||||
#define __ERT_PACKET_H__
|
||||
|
||||
#include <cstdint>
|
||||
#include <cstddef>
|
||||
|
||||
#include "field_reader.hpp"
|
||||
#include "baseband_packet.hpp"
|
||||
#include "manchester.hpp"
|
||||
|
||||
namespace ert {
|
||||
|
||||
using ID = uint32_t;
|
||||
using Consumption = uint32_t;
|
||||
|
||||
class Packet {
|
||||
public:
|
||||
enum class Type : uint32_t {
|
||||
Unknown = 0,
|
||||
IDM = 1,
|
||||
SCM = 2,
|
||||
};
|
||||
|
||||
Packet(
|
||||
const Type type,
|
||||
const baseband::Packet& packet
|
||||
) : packet_ { packet },
|
||||
decoder_ { packet_ },
|
||||
reader_ { decoder_ },
|
||||
type_ { type }
|
||||
{
|
||||
}
|
||||
|
||||
size_t length() const;
|
||||
|
||||
bool is_valid() const;
|
||||
|
||||
Timestamp received_at() const;
|
||||
|
||||
Type type() const;
|
||||
ID id() const;
|
||||
Consumption consumption() const;
|
||||
|
||||
ManchesterFormatted symbols_formatted() const;
|
||||
|
||||
bool crc_ok() const;
|
||||
|
||||
private:
|
||||
using Reader = FieldReader<ManchesterDecoder, BitRemapNone>;
|
||||
|
||||
const baseband::Packet packet_;
|
||||
const ManchesterDecoder decoder_;
|
||||
const Reader reader_;
|
||||
const Type type_;
|
||||
|
||||
const ID invalid_id = 0;
|
||||
const Consumption invalid_consumption = 0;
|
||||
|
||||
bool crc_ok_idm() const;
|
||||
bool crc_ok_scm() const;
|
||||
};
|
||||
|
||||
} /* namespace ert */
|
||||
|
||||
#endif/*__ERT_PACKET_H__*/
|
24
firmware/common/event.cpp
Normal file
24
firmware/common/event.cpp
Normal file
@@ -0,0 +1,24 @@
|
||||
/*
|
||||
* Copyright (C) 2014 Jared Boone, ShareBrained Technology, Inc.
|
||||
*
|
||||
* 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 "event.hpp"
|
||||
|
||||
#include "ch.h"
|
27
firmware/common/event.hpp
Normal file
27
firmware/common/event.hpp
Normal file
@@ -0,0 +1,27 @@
|
||||
/*
|
||||
* Copyright (C) 2014 Jared Boone, ShareBrained Technology, Inc.
|
||||
*
|
||||
* 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 __EVENT_H__
|
||||
#define __EVENT_H__
|
||||
|
||||
#include "ch.h"
|
||||
|
||||
#endif/*__EVENT_H__*/
|
65
firmware/common/field_reader.hpp
Normal file
65
firmware/common/field_reader.hpp
Normal file
@@ -0,0 +1,65 @@
|
||||
/*
|
||||
* Copyright (C) 2015 Jared Boone, ShareBrained Technology, Inc.
|
||||
*
|
||||
* 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 __FIELD_READER_H__
|
||||
#define __FIELD_READER_H__
|
||||
|
||||
#include <cstdint>
|
||||
#include <cstddef>
|
||||
|
||||
struct BitRemapNone {
|
||||
constexpr size_t operator()(const size_t& bit_index) const {
|
||||
return bit_index;
|
||||
}
|
||||
};
|
||||
|
||||
struct BitRemapByteReverse {
|
||||
constexpr size_t operator()(const size_t bit_index) const {
|
||||
return bit_index ^ 7;
|
||||
}
|
||||
};
|
||||
|
||||
template<typename T, typename BitRemap>
|
||||
class FieldReader {
|
||||
public:
|
||||
constexpr FieldReader(
|
||||
const T& data
|
||||
) : data { data }
|
||||
{
|
||||
}
|
||||
|
||||
/* The "start_bit" winds up being the MSB of the returned field value. */
|
||||
/* The BitRemap functor determines which bits are read from the source
|
||||
* packet. */
|
||||
uint32_t read(const size_t start_bit, const size_t length) const {
|
||||
uint32_t value = 0;
|
||||
for(size_t i=start_bit; i<(start_bit + length); i++) {
|
||||
value = (value << 1) | data[bit_remap(i)];
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
private:
|
||||
const T& data;
|
||||
const BitRemap bit_remap { };
|
||||
};
|
||||
|
||||
#endif/*__FIELD_READER_H__*/
|
@@ -42,6 +42,10 @@ public:
|
||||
_in = _out = 0;
|
||||
}
|
||||
|
||||
void reset_in() {
|
||||
_in = _out;
|
||||
}
|
||||
|
||||
void reset_out() {
|
||||
_out = _in;
|
||||
}
|
||||
@@ -59,19 +63,21 @@ public:
|
||||
}
|
||||
|
||||
bool is_full() const {
|
||||
return len() > mask();
|
||||
return unused() == 0;
|
||||
}
|
||||
/*
|
||||
|
||||
bool in(const T& val) {
|
||||
const bool is_not_full = !is_full();
|
||||
if( is_not_full ) {
|
||||
_data[_in & mask()] = val;
|
||||
smp_wmb();
|
||||
_in++;
|
||||
if( is_full() ) {
|
||||
return false;
|
||||
}
|
||||
return is_not_full;
|
||||
|
||||
_data[_in & mask()] = val;
|
||||
smp_wmb();
|
||||
_in += 1;
|
||||
|
||||
return true;
|
||||
}
|
||||
*/
|
||||
|
||||
size_t in(const T* const buf, size_t len) {
|
||||
const size_t l = unused();
|
||||
if( len > l ) {
|
||||
@@ -93,7 +99,7 @@ public:
|
||||
_in += len + recsize();
|
||||
return len;
|
||||
}
|
||||
/*
|
||||
|
||||
bool out(T& val) {
|
||||
if( is_empty() ) {
|
||||
return false;
|
||||
@@ -105,13 +111,33 @@ public:
|
||||
|
||||
return true;
|
||||
}
|
||||
*/
|
||||
|
||||
size_t out(T* const buf, size_t len) {
|
||||
len = out_peek(buf, len);
|
||||
_out += len;
|
||||
return len;
|
||||
}
|
||||
|
||||
bool skip() {
|
||||
if( is_empty() ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
size_t len = peek_n();
|
||||
_out += len + recsize();
|
||||
return true;
|
||||
}
|
||||
|
||||
size_t peek_r(void* const buf, size_t len) {
|
||||
if( is_empty() ) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
size_t n;
|
||||
len = out_copy_r((T*)buf, len, &n);
|
||||
return len;
|
||||
}
|
||||
|
||||
size_t out_r(void* const buf, size_t len) {
|
||||
if( is_empty() ) {
|
||||
return 0;
|
||||
@@ -199,8 +225,8 @@ private:
|
||||
}
|
||||
|
||||
T _data[size()];
|
||||
size_t _in;
|
||||
size_t _out;
|
||||
volatile size_t _in;
|
||||
volatile size_t _out;
|
||||
};
|
||||
|
||||
#endif/*__FIFO_H__*/
|
||||
|
@@ -54,7 +54,7 @@ void Channel::configure(
|
||||
const LLI& first_lli,
|
||||
const uint32_t config
|
||||
) const {
|
||||
disable_force();
|
||||
disable();
|
||||
clear_interrupts();
|
||||
|
||||
LPC_GPDMA_Channel_Type* const channel = &LPC_GPDMA->CH[number];
|
||||
@@ -69,7 +69,6 @@ void Channel::configure(
|
||||
|
||||
extern "C" {
|
||||
|
||||
LOCATE_IN_RAM
|
||||
CH_IRQ_HANDLER(DMA_IRQHandler) {
|
||||
CH_IRQ_PROLOGUE();
|
||||
|
||||
|
@@ -313,7 +313,7 @@ public:
|
||||
return LPC_GPDMA->CH[number].CONFIG & (1U << 0);
|
||||
}
|
||||
|
||||
void disable_force() const {
|
||||
void disable() const {
|
||||
LPC_GPDMA->CH[number].CONFIG &= ~(1U << 0);
|
||||
}
|
||||
|
||||
@@ -349,7 +349,7 @@ public:
|
||||
|
||||
void disable() const {
|
||||
for(const auto& channel : channels) {
|
||||
channel.disable_force();
|
||||
channel.disable();
|
||||
}
|
||||
LPC_GPDMA->CONFIG &= ~(1U << 0);
|
||||
}
|
||||
|
@@ -28,12 +28,12 @@
|
||||
#include "hal.h"
|
||||
|
||||
struct PinConfig {
|
||||
const uint32_t mode : 3;
|
||||
const uint32_t pd : 1;
|
||||
const uint32_t pu : 1;
|
||||
const uint32_t fast : 1;
|
||||
const uint32_t input : 1;
|
||||
const uint32_t ifilt : 1;
|
||||
const uint32_t mode;
|
||||
const uint32_t pd;
|
||||
const uint32_t pu;
|
||||
const uint32_t fast;
|
||||
const uint32_t input;
|
||||
const uint32_t ifilt;
|
||||
|
||||
constexpr operator uint16_t() {
|
||||
return
|
||||
|
@@ -43,6 +43,20 @@ void lcd_reset() {
|
||||
chThdSleepMilliseconds(120);
|
||||
}
|
||||
|
||||
void lcd_sleep_in() {
|
||||
io.lcd_data_write_command_and_data(0x10, {});
|
||||
chThdSleepMilliseconds(5);
|
||||
}
|
||||
|
||||
void lcd_sleep_out() {
|
||||
io.lcd_data_write_command_and_data(0x11, {});
|
||||
chThdSleepMilliseconds(120);
|
||||
}
|
||||
|
||||
void lcd_display_on() {
|
||||
io.lcd_data_write_command_and_data(0x29, {});
|
||||
}
|
||||
|
||||
void lcd_init() {
|
||||
// LCDs are configured for IM[2:0] = 001
|
||||
// 8080-I system, 16-bit parallel bus
|
||||
@@ -149,12 +163,8 @@ void lcd_init() {
|
||||
0x47, 0x04, 0x0C, 0x0B, 0x29, 0x2F, 0x05
|
||||
});
|
||||
|
||||
// Exit Sleep
|
||||
io.lcd_data_write_command_and_data(0x11, {});
|
||||
chThdSleepMilliseconds(120);
|
||||
|
||||
// Display on
|
||||
io.lcd_data_write_command_and_data(0x29, {});
|
||||
lcd_sleep_out();
|
||||
lcd_display_on();
|
||||
|
||||
// Turn on Tearing Effect Line (TE) output signal.
|
||||
io.lcd_data_write_command_and_data(0x35, { 0b00000000 });
|
||||
@@ -232,6 +242,14 @@ void ILI9341::shutdown() {
|
||||
lcd_reset();
|
||||
}
|
||||
|
||||
void ILI9341::sleep() {
|
||||
lcd_sleep_in();
|
||||
}
|
||||
|
||||
void ILI9341::wake() {
|
||||
lcd_sleep_out();
|
||||
}
|
||||
|
||||
void ILI9341::fill_rectangle(ui::Rect r, const ui::Color c) {
|
||||
const auto r_clipped = r.intersect(screen_rect());
|
||||
if( !r_clipped.is_empty() ) {
|
||||
|
@@ -44,6 +44,9 @@ public:
|
||||
void init();
|
||||
void shutdown();
|
||||
|
||||
void sleep();
|
||||
void wake();
|
||||
|
||||
void fill_rectangle(ui::Rect r, const ui::Color c);
|
||||
void draw_line(const ui::Point start, const ui::Point end, const ui::Color color);
|
||||
void fill_circle(
|
||||
|
@@ -30,6 +30,21 @@
|
||||
|
||||
namespace lpc43xx {
|
||||
|
||||
#if defined(LPC43XX_M4)
|
||||
namespace m4 {
|
||||
|
||||
static inline bool flag_saturation() {
|
||||
return __get_APSR() & (1U << 27);
|
||||
}
|
||||
|
||||
static inline void clear_flag_saturation() {
|
||||
uint32_t flags = 1;
|
||||
__asm volatile ("MSR APSR_nzcvqg, %0" : : "r" (flags));
|
||||
}
|
||||
|
||||
} /* namespace m4 */
|
||||
#endif
|
||||
|
||||
namespace creg {
|
||||
|
||||
static_assert(offsetof(LPC_CREG_Type, CREG0) == 0x004, "CREG0 offset wrong");
|
||||
@@ -43,6 +58,16 @@ static_assert(offsetof(LPC_CREG_Type, USB1FLADJ) == 0x600, "USB1FLADJ offset wro
|
||||
|
||||
namespace m4txevent {
|
||||
|
||||
#if defined(LPC43XX_M0)
|
||||
inline void enable() {
|
||||
nvicEnableVector(M4CORE_IRQn, CORTEX_PRIORITY_MASK(LPC43XX_M4TXEVENT_IRQ_PRIORITY));
|
||||
}
|
||||
|
||||
inline void disable() {
|
||||
nvicDisableVector(M4CORE_IRQn);
|
||||
}
|
||||
#endif
|
||||
|
||||
#if defined(LPC43XX_M4)
|
||||
inline void assert() {
|
||||
__SEV();
|
||||
@@ -57,6 +82,16 @@ inline void clear() {
|
||||
|
||||
namespace m0apptxevent {
|
||||
|
||||
#if defined(LPC43XX_M4)
|
||||
inline void enable() {
|
||||
nvicEnableVector(M0CORE_IRQn, CORTEX_PRIORITY_MASK(LPC43XX_M0APPTXEVENT_IRQ_PRIORITY));
|
||||
}
|
||||
|
||||
inline void disable() {
|
||||
nvicDisableVector(M0CORE_IRQn);
|
||||
}
|
||||
#endif
|
||||
|
||||
#if defined(LPC43XX_M0)
|
||||
inline void assert() {
|
||||
__SEV();
|
||||
|
71
firmware/common/manchester.cpp
Normal file
71
firmware/common/manchester.cpp
Normal file
@@ -0,0 +1,71 @@
|
||||
/*
|
||||
* Copyright (C) 2015 Jared Boone, ShareBrained Technology, Inc.
|
||||
*
|
||||
* 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 "manchester.hpp"
|
||||
|
||||
#include "string_format.hpp"
|
||||
|
||||
ManchesterDecoder::DecodedSymbol ManchesterDecoder::operator[](const size_t index) const {
|
||||
const size_t encoded_index = index * 2;
|
||||
if( (encoded_index + 1) < packet.size() ) {
|
||||
const auto value = packet[encoded_index + sense];
|
||||
const auto error = packet[encoded_index + 0] == packet[encoded_index + 1];
|
||||
return { value, error };
|
||||
} else {
|
||||
return { 0, 1 };
|
||||
}
|
||||
}
|
||||
|
||||
size_t ManchesterDecoder::symbols_count() const {
|
||||
return packet.size() / 2;
|
||||
}
|
||||
|
||||
ManchesterFormatted format_manchester(
|
||||
const ManchesterDecoder& decoder
|
||||
) {
|
||||
const size_t payload_length_decoded = decoder.symbols_count();
|
||||
const size_t payload_length_hex_characters = (payload_length_decoded + 3) / 4;
|
||||
const size_t payload_length_symbols_rounded = payload_length_hex_characters * 4;
|
||||
|
||||
std::string hex_data;
|
||||
std::string hex_error;
|
||||
hex_data.reserve(payload_length_hex_characters);
|
||||
hex_error.reserve(payload_length_hex_characters);
|
||||
|
||||
uint_fast8_t data = 0;
|
||||
uint_fast8_t error = 0;
|
||||
for(size_t i=0; i<payload_length_symbols_rounded; i++) {
|
||||
const auto symbol = decoder[i];
|
||||
|
||||
data <<= 1;
|
||||
data |= symbol.value;
|
||||
|
||||
error <<= 1;
|
||||
error |= symbol.error;
|
||||
|
||||
if( (i & 3) == 3 ) {
|
||||
hex_data += to_string_hex(data & 0xf, 1);
|
||||
hex_error += to_string_hex(error & 0xf, 1);
|
||||
}
|
||||
}
|
||||
|
||||
return { hex_data, hex_error };
|
||||
}
|
70
firmware/common/manchester.hpp
Normal file
70
firmware/common/manchester.hpp
Normal file
@@ -0,0 +1,70 @@
|
||||
/*
|
||||
* Copyright (C) 2015 Jared Boone, ShareBrained Technology, Inc.
|
||||
*
|
||||
* 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 __MANCHESTER_H__
|
||||
#define __MANCHESTER_H__
|
||||
|
||||
#include <cstdint>
|
||||
#include <cstddef>
|
||||
#include <bitset>
|
||||
#include <string>
|
||||
|
||||
#include "baseband_packet.hpp"
|
||||
|
||||
class ManchesterDecoder {
|
||||
public:
|
||||
struct DecodedSymbol {
|
||||
uint_fast8_t value;
|
||||
uint_fast8_t error;
|
||||
};
|
||||
|
||||
constexpr ManchesterDecoder(
|
||||
const baseband::Packet& packet,
|
||||
const size_t sense = 0
|
||||
) : packet { packet },
|
||||
sense { sense }
|
||||
{
|
||||
}
|
||||
|
||||
DecodedSymbol operator[](const size_t index) const;
|
||||
|
||||
size_t symbols_count() const;
|
||||
|
||||
private:
|
||||
const baseband::Packet& packet;
|
||||
const size_t sense;
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
T operator|(const T& l, const ManchesterDecoder::DecodedSymbol& r) {
|
||||
return l | r.value;
|
||||
}
|
||||
|
||||
struct ManchesterFormatted {
|
||||
const std::string data;
|
||||
const std::string errors;
|
||||
};
|
||||
|
||||
ManchesterFormatted format_manchester(
|
||||
const ManchesterDecoder& decoder
|
||||
);
|
||||
|
||||
#endif/*__MANCHESTER_H__*/
|
@@ -27,6 +27,11 @@
|
||||
#include <array>
|
||||
#include <functional>
|
||||
|
||||
#include "baseband_packet.hpp"
|
||||
#include "ert_packet.hpp"
|
||||
#include "dsp_fir_taps.hpp"
|
||||
#include "fifo.hpp"
|
||||
|
||||
#include "utility.hpp"
|
||||
|
||||
#include "ch.h"
|
||||
@@ -34,27 +39,33 @@
|
||||
class Message {
|
||||
public:
|
||||
static constexpr size_t MAX_SIZE = 276;
|
||||
|
||||
enum class ID : uint16_t {
|
||||
|
||||
enum class ID : uint32_t {
|
||||
/* Assign consecutive IDs. IDs are used to index array. */
|
||||
RSSIStatistics = 0,
|
||||
BasebandStatistics = 1,
|
||||
ChannelStatistics = 2,
|
||||
DisplayFrameSync = 3,
|
||||
ChannelSpectrum = 4,
|
||||
AudioStatistics = 5,
|
||||
BasebandConfiguration = 6,
|
||||
TPMSPacket = 7,
|
||||
AudioStatistics = 4,
|
||||
BasebandConfiguration = 5,
|
||||
TPMSPacket = 6,
|
||||
Shutdown = 8,
|
||||
AISPacket = 9,
|
||||
TXDone = 10,
|
||||
SDCardStatus = 11,
|
||||
Retune = 12,
|
||||
ReadyForSwitch = 13,
|
||||
AFSKData = 14,
|
||||
ModuleID = 15,
|
||||
FIFOSignal = 16,
|
||||
FIFOData = 17,
|
||||
AISPacket = 7,
|
||||
ERTPacket = 9,
|
||||
UpdateSpectrum = 10,
|
||||
NBFMConfigure = 11,
|
||||
WFMConfigure = 12,
|
||||
AMConfigure = 13,
|
||||
ChannelSpectrumConfig = 14,
|
||||
SpectrumStreamingConfig = 15,
|
||||
DisplaySleep = 16,
|
||||
TXDone = 17,
|
||||
Retune = 18,
|
||||
ReadyForSwitch = 19,
|
||||
AFSKData = 20,
|
||||
ModuleID = 21,
|
||||
FIFOSignal = 22,
|
||||
FIFOData = 23,
|
||||
MAX
|
||||
};
|
||||
|
||||
@@ -122,13 +133,23 @@ struct ChannelStatistics {
|
||||
class ChannelStatisticsMessage : public Message {
|
||||
public:
|
||||
constexpr ChannelStatisticsMessage(
|
||||
) : Message { ID::ChannelStatistics }
|
||||
const ChannelStatistics& statistics
|
||||
) : Message { ID::ChannelStatistics },
|
||||
statistics { statistics }
|
||||
{
|
||||
}
|
||||
|
||||
ChannelStatistics statistics;
|
||||
};
|
||||
|
||||
class DisplayFrameSyncMessage : public Message {
|
||||
public:
|
||||
constexpr DisplayFrameSyncMessage(
|
||||
) : Message { ID::DisplayFrameSync }
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
struct AudioStatistics {
|
||||
int32_t rms_db;
|
||||
int32_t max_db;
|
||||
@@ -152,125 +173,119 @@ struct AudioStatistics {
|
||||
}
|
||||
};
|
||||
|
||||
class DisplaySleepMessage : public Message {
|
||||
public:
|
||||
constexpr DisplaySleepMessage(
|
||||
) : Message { ID::DisplaySleep }
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
class AudioStatisticsMessage : public Message {
|
||||
public:
|
||||
constexpr AudioStatisticsMessage(
|
||||
const AudioStatistics& statistics
|
||||
) : Message { ID::AudioStatistics },
|
||||
statistics { }
|
||||
statistics { statistics }
|
||||
{
|
||||
}
|
||||
|
||||
AudioStatistics statistics;
|
||||
};
|
||||
|
||||
typedef enum {
|
||||
RX_NBAM_AUDIO = 0,
|
||||
RX_NBFM_AUDIO,
|
||||
RX_WBFM_AUDIO,
|
||||
RX_AIS,
|
||||
RX_WBSPECTRUM,
|
||||
RX_TPMS,
|
||||
RX_AFSK,
|
||||
RX_SIGFOX,
|
||||
|
||||
TX_RDS,
|
||||
TX_LCR,
|
||||
TX_TONE,
|
||||
TX_JAMMER,
|
||||
TX_XYLOS,
|
||||
|
||||
PLAY_AUDIO,
|
||||
|
||||
NONE,
|
||||
SWITCH = 0xFF
|
||||
} mode_type;
|
||||
|
||||
struct BasebandConfiguration {
|
||||
mode_type mode;
|
||||
int32_t mode;
|
||||
uint32_t sampling_rate;
|
||||
size_t decimation_factor;
|
||||
|
||||
constexpr BasebandConfiguration(
|
||||
mode_type mode = NONE,
|
||||
uint32_t sampling_rate = 0,
|
||||
int32_t mode,
|
||||
uint32_t sampling_rate,
|
||||
size_t decimation_factor = 1
|
||||
) : mode { mode },
|
||||
sampling_rate { sampling_rate },
|
||||
decimation_factor { decimation_factor }
|
||||
{
|
||||
}
|
||||
|
||||
constexpr BasebandConfiguration(
|
||||
) : BasebandConfiguration { -1, 0, 1 }
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
class BasebandConfigurationMessage : public Message {
|
||||
public:
|
||||
constexpr BasebandConfigurationMessage(
|
||||
BasebandConfiguration configuration
|
||||
const BasebandConfiguration& configuration
|
||||
) : Message { ID::BasebandConfiguration },
|
||||
configuration(configuration)
|
||||
configuration { configuration }
|
||||
{
|
||||
}
|
||||
|
||||
BasebandConfiguration configuration;
|
||||
};
|
||||
|
||||
class SpectrumStreamingConfigMessage : public Message {
|
||||
public:
|
||||
enum class Mode : uint32_t {
|
||||
Stopped = 0,
|
||||
Running = 1,
|
||||
};
|
||||
|
||||
constexpr SpectrumStreamingConfigMessage(
|
||||
Mode mode
|
||||
) : Message { ID::SpectrumStreamingConfig },
|
||||
mode { mode }
|
||||
{
|
||||
}
|
||||
|
||||
Mode mode { Mode::Stopped };
|
||||
};
|
||||
|
||||
struct ChannelSpectrum {
|
||||
std::array<uint8_t, 256> db { { 0 } };
|
||||
size_t db_count { 256 };
|
||||
uint32_t sampling_rate { 0 };
|
||||
uint32_t channel_filter_pass_frequency { 0 };
|
||||
uint32_t channel_filter_stop_frequency { 0 };
|
||||
};
|
||||
|
||||
class ChannelSpectrumMessage : public Message {
|
||||
using ChannelSpectrumFIFO = FIFO<ChannelSpectrum, 2>;
|
||||
|
||||
class ChannelSpectrumConfigMessage : public Message {
|
||||
public:
|
||||
constexpr ChannelSpectrumMessage(
|
||||
) : Message { ID::ChannelSpectrum }
|
||||
constexpr ChannelSpectrumConfigMessage(
|
||||
ChannelSpectrumFIFO* fifo
|
||||
) : Message { ID::ChannelSpectrumConfig },
|
||||
fifo { fifo }
|
||||
{
|
||||
}
|
||||
|
||||
ChannelSpectrum spectrum;
|
||||
};
|
||||
|
||||
#include <bitset>
|
||||
|
||||
struct AISPacket {
|
||||
std::bitset<1024> payload;
|
||||
size_t bits_received { 0 };
|
||||
ChannelSpectrumFIFO* fifo { nullptr };
|
||||
};
|
||||
|
||||
class AISPacketMessage : public Message {
|
||||
public:
|
||||
constexpr AISPacketMessage(
|
||||
) : Message { ID::AISPacket }
|
||||
const baseband::Packet& packet
|
||||
) : Message { ID::AISPacket },
|
||||
packet { packet }
|
||||
{
|
||||
}
|
||||
|
||||
AISPacket packet;
|
||||
};
|
||||
|
||||
struct TPMSPacket {
|
||||
std::bitset<1024> payload;
|
||||
size_t bits_received { 0 };
|
||||
baseband::Packet packet;
|
||||
};
|
||||
|
||||
class TPMSPacketMessage : public Message {
|
||||
public:
|
||||
constexpr TPMSPacketMessage(
|
||||
) : Message { ID::TPMSPacket }
|
||||
const baseband::Packet& packet
|
||||
) : Message { ID::TPMSPacket },
|
||||
packet { packet }
|
||||
{
|
||||
}
|
||||
|
||||
TPMSPacket packet;
|
||||
};
|
||||
|
||||
class AFSKDataMessage : public Message {
|
||||
public:
|
||||
constexpr AFSKDataMessage(
|
||||
) : Message { ID::AFSKData }
|
||||
{
|
||||
}
|
||||
|
||||
int16_t data[128] = {0};
|
||||
baseband::Packet packet;
|
||||
};
|
||||
|
||||
class ShutdownMessage : public Message {
|
||||
@@ -281,16 +296,88 @@ public:
|
||||
}
|
||||
};
|
||||
|
||||
class SDCardStatusMessage : public Message {
|
||||
class ERTPacketMessage : public Message {
|
||||
public:
|
||||
constexpr SDCardStatusMessage(
|
||||
bool is_mounted
|
||||
) : Message { ID::SDCardStatus },
|
||||
is_mounted { is_mounted }
|
||||
constexpr ERTPacketMessage(
|
||||
const ert::Packet::Type type,
|
||||
const baseband::Packet& packet
|
||||
) : Message { ID::ERTPacket },
|
||||
type { type },
|
||||
packet { packet }
|
||||
{
|
||||
}
|
||||
|
||||
const bool is_mounted;
|
||||
ert::Packet::Type type;
|
||||
|
||||
baseband::Packet packet;
|
||||
};
|
||||
|
||||
class UpdateSpectrumMessage : public Message {
|
||||
public:
|
||||
constexpr UpdateSpectrumMessage(
|
||||
) : Message { ID::UpdateSpectrum }
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
class NBFMConfigureMessage : public Message {
|
||||
public:
|
||||
constexpr NBFMConfigureMessage(
|
||||
const fir_taps_real<24> decim_0_filter,
|
||||
const fir_taps_real<32> decim_1_filter,
|
||||
const fir_taps_real<32> channel_filter,
|
||||
const size_t deviation
|
||||
) : Message { ID::NBFMConfigure },
|
||||
decim_0_filter(decim_0_filter),
|
||||
decim_1_filter(decim_1_filter),
|
||||
channel_filter(channel_filter),
|
||||
deviation { deviation }
|
||||
{
|
||||
}
|
||||
|
||||
const fir_taps_real<24> decim_0_filter;
|
||||
const fir_taps_real<32> decim_1_filter;
|
||||
const fir_taps_real<32> channel_filter;
|
||||
const size_t deviation;
|
||||
};
|
||||
|
||||
class WFMConfigureMessage : public Message {
|
||||
public:
|
||||
constexpr WFMConfigureMessage(
|
||||
const fir_taps_real<24> decim_0_filter,
|
||||
const fir_taps_real<16> decim_1_filter,
|
||||
const fir_taps_real<64> audio_filter,
|
||||
const size_t deviation
|
||||
) : Message { ID::WFMConfigure },
|
||||
decim_0_filter(decim_0_filter),
|
||||
decim_1_filter(decim_1_filter),
|
||||
audio_filter(audio_filter),
|
||||
deviation { deviation }
|
||||
{
|
||||
}
|
||||
|
||||
const fir_taps_real<24> decim_0_filter;
|
||||
const fir_taps_real<16> decim_1_filter;
|
||||
const fir_taps_real<64> audio_filter;
|
||||
const size_t deviation;
|
||||
};
|
||||
|
||||
class AMConfigureMessage : public Message {
|
||||
public:
|
||||
constexpr AMConfigureMessage(
|
||||
const fir_taps_real<24> decim_0_filter,
|
||||
const fir_taps_real<32> decim_1_filter,
|
||||
const fir_taps_real<32> channel_filter
|
||||
) : Message { ID::AMConfigure },
|
||||
decim_0_filter(decim_0_filter),
|
||||
decim_1_filter(decim_1_filter),
|
||||
channel_filter(channel_filter)
|
||||
{
|
||||
}
|
||||
|
||||
const fir_taps_real<24> decim_0_filter;
|
||||
const fir_taps_real<32> decim_1_filter;
|
||||
const fir_taps_real<32> channel_filter;
|
||||
};
|
||||
|
||||
class TXDoneMessage : public Message {
|
||||
|
@@ -47,6 +47,25 @@ public:
|
||||
return push(&message, sizeof(message));
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
bool push_and_wait(const T& message) {
|
||||
const bool result = push(message);
|
||||
if( result ) {
|
||||
// TODO: More graceful method of waiting for empty? Maybe sleep for a bit?
|
||||
while( !is_empty() );
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
Message* peek(std::array<uint8_t, Message::MAX_SIZE>& buf) {
|
||||
Message* const p = reinterpret_cast<Message*>(buf.data());
|
||||
return fifo.peek_r(buf.data(), buf.size()) ? p : nullptr;
|
||||
}
|
||||
|
||||
bool skip() {
|
||||
return fifo.skip();
|
||||
}
|
||||
|
||||
Message* pop(std::array<uint8_t, Message::MAX_SIZE>& buf) {
|
||||
Message* const p = reinterpret_cast<Message*>(buf.data());
|
||||
return fifo.out_r(buf.data(), buf.size()) ? p : nullptr;
|
||||
|
42
firmware/common/optional.hpp
Normal file
42
firmware/common/optional.hpp
Normal file
@@ -0,0 +1,42 @@
|
||||
/*
|
||||
* Copyright (C) 2016 Jared Boone, ShareBrained Technology, Inc.
|
||||
*
|
||||
* 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 __OPTIONAL_H__
|
||||
#define __OPTIONAL_H__
|
||||
|
||||
#include <utility>
|
||||
|
||||
template<typename T>
|
||||
class Optional {
|
||||
public:
|
||||
constexpr Optional() : value_ { }, valid_ { false } { };
|
||||
constexpr Optional(const T& value) : value_ { value }, valid_ { true } { };
|
||||
constexpr Optional(T&& value) : value_ { std::move(value) }, valid_ { true } { };
|
||||
|
||||
bool is_valid() const { return valid_; };
|
||||
T value() const { return value_; };
|
||||
|
||||
private:
|
||||
T value_;
|
||||
bool valid_;
|
||||
};
|
||||
|
||||
#endif/*__OPTIONAL_H__*/
|
@@ -23,32 +23,14 @@
|
||||
|
||||
#include "hal.h"
|
||||
|
||||
#include "utility.hpp"
|
||||
|
||||
#include <algorithm>
|
||||
#include <utility>
|
||||
|
||||
namespace portapack {
|
||||
namespace persistent_memory {
|
||||
|
||||
/* TODO: This is widely applicable. Factor this to somewhere useful. */
|
||||
template<class T>
|
||||
struct range_t {
|
||||
const T minimum;
|
||||
const T maximum;
|
||||
|
||||
const T& clip(const T& value) const {
|
||||
return std::max(std::min(value, maximum), minimum);
|
||||
}
|
||||
|
||||
void reset_if_outside(T& value, const T& reset_value) const {
|
||||
if( (value < minimum ) ||
|
||||
(value > maximum ) ) {
|
||||
value = reset_value;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
using tuned_frequency_range_t = range_t<rf::Frequency>;
|
||||
constexpr tuned_frequency_range_t tuned_frequency_range { rf::tuning_range.min, rf::tuning_range.max };
|
||||
constexpr rf::Frequency tuned_frequency_reset_value { 858750000 };
|
||||
|
||||
using ppb_range_t = range_t<ppb_t>;
|
||||
@@ -93,12 +75,12 @@ static_assert(sizeof(data_t) <= 0x100, "Persistent memory structure too large fo
|
||||
static data_t* const data = reinterpret_cast<data_t*>(LPC_BACKUP_REG_BASE);
|
||||
|
||||
rf::Frequency tuned_frequency() {
|
||||
tuned_frequency_range.reset_if_outside(data->tuned_frequency, tuned_frequency_reset_value);
|
||||
rf::tuning_range.reset_if_outside(data->tuned_frequency, tuned_frequency_reset_value);
|
||||
return data->tuned_frequency;
|
||||
}
|
||||
|
||||
void set_tuned_frequency(const rf::Frequency new_value) {
|
||||
data->tuned_frequency = tuned_frequency_range.clip(new_value);
|
||||
data->tuned_frequency = rf::tuning_range.clip(new_value);
|
||||
}
|
||||
|
||||
ppb_t correction_ppb() {
|
||||
|
108
firmware/common/simd.hpp
Normal file
108
firmware/common/simd.hpp
Normal file
@@ -0,0 +1,108 @@
|
||||
/*
|
||||
* Copyright (C) 2015 Jared Boone, ShareBrained Technology, Inc.
|
||||
*
|
||||
* 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 __SIMD_H__
|
||||
#define __SIMD_H__
|
||||
|
||||
#if defined(LPC43XX_M4)
|
||||
|
||||
#include <hal.h>
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
struct vec4_s8 {
|
||||
union {
|
||||
int8_t v[4];
|
||||
uint32_t w;
|
||||
};
|
||||
};
|
||||
|
||||
struct vec2_s16 {
|
||||
constexpr vec2_s16(
|
||||
) : v { 0, 0 }
|
||||
{
|
||||
}
|
||||
|
||||
constexpr vec2_s16(
|
||||
const int16_t v0,
|
||||
const int16_t v1
|
||||
) : v { v0, v1 }
|
||||
{
|
||||
}
|
||||
|
||||
constexpr vec2_s16(
|
||||
const vec2_s16& other
|
||||
) : w { other.w }
|
||||
{
|
||||
}
|
||||
|
||||
vec2_s16& operator=(const vec2_s16& other) {
|
||||
w = other.w;
|
||||
return *this;
|
||||
}
|
||||
|
||||
union {
|
||||
int16_t v[2];
|
||||
uint32_t w;
|
||||
};
|
||||
};
|
||||
|
||||
static inline vec4_s8 rev16(const vec4_s8 v) {
|
||||
vec4_s8 result;
|
||||
result.w = __REV16(v.w);
|
||||
return result;
|
||||
}
|
||||
|
||||
static inline vec4_s8 pkhbt(const vec4_s8 v1, const vec4_s8 v2, const size_t sh = 0) {
|
||||
vec4_s8 result;
|
||||
result.w = __PKHBT(v1.w, v2.w, sh);
|
||||
return result;
|
||||
}
|
||||
|
||||
static inline vec2_s16 pkhbt(const vec2_s16 v1, const vec2_s16 v2, const size_t sh = 0) {
|
||||
vec2_s16 result;
|
||||
result.w = __PKHBT(v1.w, v2.w, sh);
|
||||
return result;
|
||||
}
|
||||
|
||||
static inline vec2_s16 pkhtb(const vec2_s16 v1, const vec2_s16 v2, const size_t sh = 0) {
|
||||
vec2_s16 result;
|
||||
result.w = __PKHTB(v1.w, v2.w, sh);
|
||||
return result;
|
||||
}
|
||||
|
||||
static inline vec2_s16 sxtb16(const vec4_s8 v, const size_t sh = 0) {
|
||||
vec2_s16 result;
|
||||
result.w = __SXTB16(v.w, sh);
|
||||
return result;
|
||||
}
|
||||
|
||||
static inline int32_t smlsd(const vec2_s16 v1, const vec2_s16 v2, const int32_t accum) {
|
||||
return __SMLSD(v1.w, v2.w, accum);
|
||||
}
|
||||
|
||||
static inline int32_t smlad(const vec2_s16 v1, const vec2_s16 v2, const int32_t accum) {
|
||||
return __SMLAD(v1.w, v2.w, accum);
|
||||
}
|
||||
|
||||
#endif /* defined(LPC43XX_M4) */
|
||||
|
||||
#endif/*__SIMD_H__*/
|
@@ -36,9 +36,7 @@ Rect Rect::intersect(const Rect& o) const {
|
||||
const auto y1 = std::max(top(), o.top());
|
||||
const auto y2 = std::min(bottom(), o.bottom());
|
||||
if( (x2 >= x1) && (y2 > y1) ) {
|
||||
return {
|
||||
x1, y1,
|
||||
static_cast<Dim>(x2 - x1), static_cast<Dim>(y2 - y1) };
|
||||
return { x1, y1, x2 - x1, y2 - y1 };
|
||||
} else {
|
||||
return { };
|
||||
}
|
||||
@@ -54,7 +52,7 @@ Rect& Rect::operator+=(const Rect& p) {
|
||||
pos = { x1, y1 };
|
||||
const auto x2 = std::max(right(), p.right());
|
||||
const auto y2 = std::max(bottom(), p.bottom());
|
||||
size = { static_cast<Dim>(x2 - x1), static_cast<Dim>(y2 - y1) };
|
||||
size = { x2 - x1, y2 - y1 };
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
@@ -115,23 +115,23 @@ struct Point {
|
||||
}
|
||||
|
||||
constexpr Point(
|
||||
Coord x,
|
||||
Coord y
|
||||
) : x { x },
|
||||
y { y }
|
||||
int x,
|
||||
int y
|
||||
) : x { static_cast<Coord>(x) },
|
||||
y { static_cast<Coord>(y) }
|
||||
{
|
||||
}
|
||||
|
||||
Point operator-() const {
|
||||
return { static_cast<Coord>(-x), static_cast<Coord>(-y) };
|
||||
return { -x, -y };
|
||||
}
|
||||
|
||||
Point operator+(const Point& p) const {
|
||||
return { static_cast<Coord>(x + p.x), static_cast<Coord>(y + p.y) };
|
||||
return { x + p.x, y + p.y };
|
||||
}
|
||||
|
||||
Point operator-(const Point& p) const {
|
||||
return { static_cast<Coord>(x - p.x), static_cast<Coord>(y - p.y) };
|
||||
return { x - p.x, y - p.y };
|
||||
}
|
||||
|
||||
Point& operator+=(const Point& p) {
|
||||
@@ -171,10 +171,7 @@ private:
|
||||
/* Clockwise rotate (in screen coordinates), with a gain in
|
||||
* magnitude of sqrt(2).
|
||||
*/
|
||||
return {
|
||||
static_cast<Coord>(x - y),
|
||||
static_cast<Coord>(x + y)
|
||||
};
|
||||
return { x - y, x + y };
|
||||
}
|
||||
#endif
|
||||
};
|
||||
@@ -190,10 +187,10 @@ struct Size {
|
||||
}
|
||||
|
||||
constexpr Size(
|
||||
Dim w,
|
||||
Dim h
|
||||
) : w { w },
|
||||
h { h }
|
||||
int w,
|
||||
int h
|
||||
) : w { static_cast<Dim>(w) },
|
||||
h { static_cast<Dim>(h) }
|
||||
{
|
||||
}
|
||||
|
||||
@@ -213,8 +210,8 @@ struct Rect {
|
||||
}
|
||||
|
||||
constexpr Rect(
|
||||
Coord x, Coord y,
|
||||
Dim w, Dim h
|
||||
int x, int y,
|
||||
int w, int h
|
||||
) : pos { x, y },
|
||||
size { w, h }
|
||||
{
|
||||
@@ -228,35 +225,33 @@ struct Rect {
|
||||
{
|
||||
}
|
||||
|
||||
Coord top() const {
|
||||
|
||||
int top() const {
|
||||
return pos.y;
|
||||
}
|
||||
|
||||
Coord bottom() const {
|
||||
int bottom() const {
|
||||
return pos.y + size.h;
|
||||
}
|
||||
|
||||
Coord left() const {
|
||||
int left() const {
|
||||
return pos.x;
|
||||
}
|
||||
|
||||
Coord right() const {
|
||||
int right() const {
|
||||
return pos.x + size.w;
|
||||
}
|
||||
|
||||
Dim width() const {
|
||||
int width() const {
|
||||
return size.w;
|
||||
}
|
||||
|
||||
Dim height() const {
|
||||
int height() const {
|
||||
return size.h;
|
||||
}
|
||||
|
||||
Point center() const {
|
||||
return {
|
||||
static_cast<Coord>(pos.x + size.w / 2),
|
||||
static_cast<Coord>(pos.y + size.h / 2)
|
||||
};
|
||||
return { pos.x + size.w / 2, pos.y + size.h / 2 };
|
||||
}
|
||||
|
||||
bool is_empty() const {
|
||||
|
@@ -36,13 +36,13 @@ Style Style::invert() const {
|
||||
};
|
||||
}
|
||||
|
||||
size_t Painter::draw_char(const Point p, const Style& style, const char c) {
|
||||
int Painter::draw_char(const Point p, const Style& style, const char c) {
|
||||
const auto glyph = style.font.glyph(c);
|
||||
display.draw_glyph(p, glyph, style.foreground, style.background);
|
||||
return glyph.advance().x;
|
||||
}
|
||||
|
||||
size_t Painter::draw_string(Point p, const Style& style, const std::string text) {
|
||||
int Painter::draw_string(Point p, const Style& style, const std::string text) {
|
||||
size_t width = 0;
|
||||
for(const auto c : text) {
|
||||
const auto glyph = style.font.glyph(c);
|
||||
@@ -54,19 +54,19 @@ size_t Painter::draw_string(Point p, const Style& style, const std::string text)
|
||||
return width;
|
||||
}
|
||||
|
||||
void Painter::draw_hline(Point p, size_t width, const Color c) {
|
||||
display.fill_rectangle({ p, { static_cast<Dim>(width), 1 } }, c);
|
||||
void Painter::draw_hline(Point p, int width, const Color c) {
|
||||
display.fill_rectangle({ p, { width, 1 } }, c);
|
||||
}
|
||||
|
||||
void Painter::draw_vline(Point p, size_t height, const Color c) {
|
||||
display.fill_rectangle({ p, { 1, static_cast<Dim>(height) } }, c);
|
||||
void Painter::draw_vline(Point p, int height, const Color c) {
|
||||
display.fill_rectangle({ p, { 1, height } }, c);
|
||||
}
|
||||
|
||||
void Painter::draw_rectangle(const Rect r, const Color c) {
|
||||
draw_hline(r.pos, r.size.w, c);
|
||||
draw_vline({ r.pos.x, static_cast<Coord>(r.pos.y + 1) }, r.size.h - 2, c);
|
||||
draw_vline({ static_cast<Coord>(r.pos.x + r.size.w - 1), static_cast<Coord>(r.pos.y + 1) }, r.size.h - 2, c);
|
||||
draw_hline({ r.pos.x, static_cast<Coord>(r.pos.y + r.size.h - 1) }, r.size.w, c);
|
||||
draw_vline({ r.pos.x, r.pos.y + 1 }, r.size.h - 2, c);
|
||||
draw_vline({ r.pos.x + r.size.w - 1, r.pos.y + 1 }, r.size.h - 2, c);
|
||||
draw_hline({ r.pos.x, r.pos.y + r.size.h - 1 }, r.size.w, c);
|
||||
}
|
||||
|
||||
void Painter::fill_rectangle(const Rect r, const Color c) {
|
||||
|
@@ -46,9 +46,9 @@ public:
|
||||
Painter(const Painter&) = delete;
|
||||
Painter(Painter&&) = delete;
|
||||
|
||||
size_t draw_char(const Point p, const Style& style, const char c);
|
||||
int draw_char(const Point p, const Style& style, const char c);
|
||||
|
||||
size_t draw_string(Point p, const Style& style, const std::string text);
|
||||
int draw_string(Point p, const Style& style, const std::string text);
|
||||
|
||||
void draw_rectangle(const Rect r, const Color c);
|
||||
void fill_rectangle(const Rect r, const Color c);
|
||||
@@ -56,8 +56,8 @@ public:
|
||||
void paint_widget_tree(Widget* const w);
|
||||
|
||||
private:
|
||||
void draw_hline(Point p, size_t width, const Color c);
|
||||
void draw_vline(Point p, size_t height, const Color c);
|
||||
void draw_hline(Point p, int width, const Color c);
|
||||
void draw_vline(Point p, int height, const Color c);
|
||||
|
||||
void paint_widget(Widget* const w);
|
||||
};
|
||||
|
@@ -21,12 +21,13 @@
|
||||
|
||||
#include "ui_widget.hpp"
|
||||
#include "ui_painter.hpp"
|
||||
#include "portapack.hpp"
|
||||
|
||||
#include <cstdint>
|
||||
#include <cstddef>
|
||||
#include <algorithm>
|
||||
|
||||
#include "string_format.hpp"
|
||||
|
||||
namespace ui {
|
||||
|
||||
static bool ui_dirty = true;
|
||||
@@ -43,99 +44,6 @@ bool is_dirty() {
|
||||
return ui_dirty;
|
||||
}
|
||||
|
||||
constexpr size_t to_string_max_length = 16;
|
||||
|
||||
static char* to_string_dec_uint_internal(
|
||||
char* p,
|
||||
uint32_t n
|
||||
) {
|
||||
*p = 0;
|
||||
auto q = p;
|
||||
|
||||
do {
|
||||
const uint32_t d = n % 10;
|
||||
const char c = d + 48;
|
||||
*(--q) = c;
|
||||
n /= 10;
|
||||
} while( n != 0 );
|
||||
|
||||
return q;
|
||||
}
|
||||
|
||||
static char* to_string_dec_uint_pad_internal(
|
||||
char* const term,
|
||||
const uint32_t n,
|
||||
const int32_t l,
|
||||
const char fill
|
||||
) {
|
||||
auto q = to_string_dec_uint_internal(term, n);
|
||||
|
||||
if( fill ) {
|
||||
while( (term - q) < l ) {
|
||||
*(--q) = fill;
|
||||
}
|
||||
}
|
||||
|
||||
return q;
|
||||
}
|
||||
|
||||
std::string to_string_dec_uint(
|
||||
const uint32_t n,
|
||||
const int32_t l,
|
||||
const char fill
|
||||
) {
|
||||
char p[16];
|
||||
auto term = p + sizeof(p) - 1;
|
||||
auto q = to_string_dec_uint_pad_internal(term, n, l, fill);
|
||||
|
||||
// Right justify.
|
||||
while( (term - q) < l ) {
|
||||
*(--q) = ' ';
|
||||
}
|
||||
|
||||
return q;
|
||||
}
|
||||
|
||||
std::string to_string_dec_int(
|
||||
const int32_t n,
|
||||
const int32_t l,
|
||||
const char fill
|
||||
) {
|
||||
const size_t negative = (n < 0) ? 1 : 0;
|
||||
uint32_t n_abs = negative ? -n : n;
|
||||
|
||||
char p[16];
|
||||
auto term = p + sizeof(p) - 1;
|
||||
auto q = to_string_dec_uint_pad_internal(term, n_abs, l - negative, fill);
|
||||
|
||||
// Add sign.
|
||||
if( negative ) {
|
||||
*(--q) = '-';
|
||||
}
|
||||
|
||||
// Right justify.
|
||||
while( (term - q) < l ) {
|
||||
*(--q) = ' ';
|
||||
}
|
||||
|
||||
return q;
|
||||
}
|
||||
|
||||
static void to_string_hex_internal(char* p, const uint32_t n, const int32_t l) {
|
||||
const uint32_t d = n & 0xf;
|
||||
p[l] = (d > 9) ? (d + 87) : (d + 48);
|
||||
if( l > 0 ) {
|
||||
to_string_hex_internal(p, n >> 4, l - 1);
|
||||
}
|
||||
}
|
||||
|
||||
std::string to_string_hex(const uint32_t n, const int32_t l) {
|
||||
char p[16];
|
||||
to_string_hex_internal(p, n, l - 1);
|
||||
p[l] = 0;
|
||||
return p;
|
||||
}
|
||||
|
||||
/* Widget ****************************************************************/
|
||||
|
||||
Point Widget::screen_pos() {
|
||||
@@ -187,7 +95,9 @@ void Widget::hidden(bool hide) {
|
||||
|
||||
// If parent is hidden, either of these is a no-op.
|
||||
if( hide ) {
|
||||
parent()->set_dirty();
|
||||
// TODO: Instead of dirtying parent entirely, dirty only children
|
||||
// that overlap with this widget.
|
||||
parent()->dirty_overlapping_children_in_rect(parent_rect);
|
||||
/* TODO: Notify self and all non-hidden children that they're
|
||||
* now effectively hidden?
|
||||
*/
|
||||
@@ -290,6 +200,14 @@ void Widget::visible(bool v) {
|
||||
}
|
||||
}
|
||||
|
||||
void Widget::dirty_overlapping_children_in_rect(const Rect& child_rect) {
|
||||
for(auto child : children()) {
|
||||
if( !child_rect.intersect(child->parent_rect).is_empty() ) {
|
||||
child->set_dirty();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* View ******************************************************************/
|
||||
|
||||
void View::set_parent_rect(const Rect new_parent_rect) {
|
||||
@@ -339,8 +257,20 @@ Widget* View::initial_focus() {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
std::string View::title() const {
|
||||
return "";
|
||||
};
|
||||
|
||||
/* Rectangle *************************************************************/
|
||||
|
||||
Rectangle::Rectangle(
|
||||
Rect parent_rect,
|
||||
Color c
|
||||
) : Widget { parent_rect },
|
||||
color { c }
|
||||
{
|
||||
}
|
||||
|
||||
void Rectangle::set_color(const Color c) {
|
||||
color = c;
|
||||
set_dirty();
|
||||
@@ -355,6 +285,20 @@ void Rectangle::paint(Painter& painter) {
|
||||
|
||||
/* Text ******************************************************************/
|
||||
|
||||
Text::Text(
|
||||
Rect parent_rect,
|
||||
std::string text
|
||||
) : Widget { parent_rect },
|
||||
text { text }
|
||||
{
|
||||
}
|
||||
|
||||
Text::Text(
|
||||
Rect parent_rect
|
||||
) : Text { parent_rect, { } }
|
||||
{
|
||||
}
|
||||
|
||||
void Text::set(const std::string value) {
|
||||
text = value;
|
||||
set_dirty();
|
||||
@@ -362,165 +306,33 @@ void Text::set(const std::string value) {
|
||||
|
||||
void Text::paint(Painter& painter) {
|
||||
if (style_ == nullptr) style_ = &style();
|
||||
const auto rect = screen_rect();
|
||||
|
||||
painter.fill_rectangle(rect, s.background);
|
||||
|
||||
painter.draw_string(
|
||||
screen_pos(),
|
||||
rect.pos,
|
||||
(*style_),
|
||||
text
|
||||
);
|
||||
}
|
||||
|
||||
void Text::set_style(const Style* new_style) {
|
||||
if( new_style != style_ ) {
|
||||
style_ = new_style;
|
||||
set_dirty();
|
||||
}
|
||||
}
|
||||
|
||||
/* Checkbox **************************************************************/
|
||||
|
||||
void Checkbox::set_text(const std::string value) {
|
||||
text_ = value;
|
||||
set_dirty();
|
||||
}
|
||||
|
||||
std::string Checkbox::text() const {
|
||||
return text_;
|
||||
}
|
||||
|
||||
void Checkbox::set_value(const bool value) {
|
||||
value_ = value;
|
||||
set_dirty();
|
||||
}
|
||||
|
||||
bool Checkbox::value() const {
|
||||
return value_;
|
||||
}
|
||||
|
||||
void Checkbox::paint(Painter& painter) {
|
||||
const auto r = screen_rect();
|
||||
|
||||
const auto paint_style = (has_focus() || flags.highlighted) ? style().invert() : style();
|
||||
|
||||
painter.draw_rectangle({ r.pos.x, r.pos.y, 24, 24 }, style().foreground);
|
||||
|
||||
painter.fill_rectangle(
|
||||
{
|
||||
static_cast<Coord>(r.pos.x + 1), static_cast<Coord>(r.pos.y + 1),
|
||||
static_cast<Dim>(24 - 2), static_cast<Dim>(24 - 2)
|
||||
},
|
||||
style().background
|
||||
);
|
||||
|
||||
painter.draw_rectangle({ r.pos.x+2, r.pos.y+2, 24-4, 24-4 }, paint_style.background);
|
||||
|
||||
if (value_ == true) {
|
||||
// Check
|
||||
portapack::display.draw_line( {r.pos.x+2, r.pos.y+14}, {r.pos.x+6, r.pos.y+18}, ui::Color::green());
|
||||
portapack::display.draw_line( {r.pos.x+6, r.pos.y+18}, {r.pos.x+20, r.pos.y+4}, ui::Color::green());
|
||||
} else {
|
||||
// Cross
|
||||
portapack::display.draw_line( {r.pos.x+1, r.pos.y+1}, {r.pos.x+24-2, r.pos.y+24-2}, ui::Color::red());
|
||||
portapack::display.draw_line( {r.pos.x+24-2, r.pos.y+1}, {r.pos.x+1, r.pos.y+24-2}, ui::Color::red());
|
||||
}
|
||||
|
||||
const auto label_r = paint_style.font.size_of(text_);
|
||||
painter.draw_string(
|
||||
{
|
||||
static_cast<Coord>(r.pos.x + 24 + 4),
|
||||
static_cast<Coord>(r.pos.y + (24 - label_r.h) / 2)
|
||||
},
|
||||
paint_style,
|
||||
text_
|
||||
);
|
||||
}
|
||||
|
||||
bool Checkbox::on_key(const KeyEvent key) {
|
||||
if( key == KeyEvent::Select ) {
|
||||
value_ = not value_;
|
||||
set_dirty();
|
||||
|
||||
if( on_select ) {
|
||||
on_select(*this);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Checkbox::on_touch(const TouchEvent event) {
|
||||
switch(event.type) {
|
||||
case TouchEvent::Type::Start:
|
||||
flags.highlighted = true;
|
||||
set_dirty();
|
||||
return true;
|
||||
|
||||
|
||||
case TouchEvent::Type::End:
|
||||
flags.highlighted = false;
|
||||
value_ = not value_;
|
||||
set_dirty();
|
||||
if( on_select ) {
|
||||
on_select(*this);
|
||||
}
|
||||
return true;
|
||||
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
#if 0
|
||||
switch(event.type) {
|
||||
case TouchEvent::Type::Start:
|
||||
flags.highlighted = true;
|
||||
set_dirty();
|
||||
return true;
|
||||
|
||||
case TouchEvent::Type::Move:
|
||||
{
|
||||
const bool new_highlighted = screen_rect().contains(event.point);
|
||||
if( flags.highlighted != new_highlighted ) {
|
||||
flags.highlighted = new_highlighted;
|
||||
set_dirty();
|
||||
}
|
||||
}
|
||||
return true;
|
||||
|
||||
case TouchEvent::Type::End:
|
||||
if( flags.highlighted ) {
|
||||
flags.highlighted = false;
|
||||
set_dirty();
|
||||
if( on_select ) {
|
||||
on_select(*this);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
/* Button ****************************************************************/
|
||||
|
||||
Button::Button(
|
||||
Rect parent_rect,
|
||||
std::string text
|
||||
) : Widget { parent_rect },
|
||||
text_ { text }
|
||||
{
|
||||
flags.focusable = true;
|
||||
}
|
||||
|
||||
void Button::set_text(const std::string value) {
|
||||
text_ = value;
|
||||
set_dirty();
|
||||
}
|
||||
|
||||
void Button::set_text(const int value) { //std::string
|
||||
text_ = value;
|
||||
set_dirty();
|
||||
}
|
||||
|
||||
void Button::set_style(const Style* new_style) {
|
||||
if( new_style != style_ ) {
|
||||
style_ = new_style;
|
||||
set_dirty();
|
||||
}
|
||||
}
|
||||
|
||||
std::string Button::text() const {
|
||||
return text_;
|
||||
}
|
||||
@@ -535,19 +347,13 @@ void Button::paint(Painter& painter) {
|
||||
painter.draw_rectangle(r, style().foreground);
|
||||
|
||||
painter.fill_rectangle(
|
||||
{
|
||||
static_cast<Coord>(r.pos.x + 1), static_cast<Coord>(r.pos.y + 1),
|
||||
static_cast<Dim>(r.size.w - 2), static_cast<Dim>(r.size.h - 2)
|
||||
},
|
||||
{ r.pos.x + 1, r.pos.y + 1, r.size.w - 2, r.size.h - 2 },
|
||||
paint_style.background
|
||||
);
|
||||
|
||||
const auto label_r = paint_style.font.size_of(text_);
|
||||
painter.draw_string(
|
||||
{
|
||||
static_cast<Coord>(r.pos.x + (r.size.w - label_r.w) / 2),
|
||||
static_cast<Coord>(r.pos.y + (r.size.h - label_r.h) / 2)
|
||||
},
|
||||
{ r.pos.x + (r.size.w - label_r.w) / 2, r.pos.y + (r.size.h - label_r.h) / 2 },
|
||||
paint_style,
|
||||
text_
|
||||
);
|
||||
@@ -623,6 +429,17 @@ bool Button::on_touch(const TouchEvent event) {
|
||||
|
||||
/* OptionsField **********************************************************/
|
||||
|
||||
OptionsField::OptionsField(
|
||||
Point parent_pos,
|
||||
size_t length,
|
||||
options_t options
|
||||
) : Widget { { parent_pos, { static_cast<ui::Dim>(8 * length), 16 } } },
|
||||
length_ { length },
|
||||
options { options }
|
||||
{
|
||||
flags.focusable = true;
|
||||
}
|
||||
|
||||
size_t OptionsField::selected_index() const {
|
||||
return selected_index_;
|
||||
}
|
||||
@@ -642,7 +459,7 @@ void OptionsField::set_selected_index(const size_t new_index) {
|
||||
void OptionsField::set_by_value(value_t v) {
|
||||
size_t new_index { 0 };
|
||||
for(const auto& option : options) {
|
||||
if( option.second >= v ) {
|
||||
if( option.second == v ) {
|
||||
set_selected_index(new_index);
|
||||
break;
|
||||
}
|
||||
@@ -650,12 +467,6 @@ void OptionsField::set_by_value(value_t v) {
|
||||
}
|
||||
}
|
||||
|
||||
void OptionsField::set_options(options_t new_options) {
|
||||
options = new_options;
|
||||
set_by_value(0);
|
||||
set_dirty();
|
||||
}
|
||||
|
||||
void OptionsField::paint(Painter& painter) {
|
||||
const auto paint_style = has_focus() ? style().invert() : style();
|
||||
|
||||
@@ -683,6 +494,21 @@ bool OptionsField::on_touch(const TouchEvent event) {
|
||||
|
||||
/* NumberField ***********************************************************/
|
||||
|
||||
NumberField::NumberField(
|
||||
Point parent_pos,
|
||||
size_t length,
|
||||
range_t range,
|
||||
int32_t step,
|
||||
char fill_char
|
||||
) : Widget { { parent_pos, { static_cast<ui::Dim>(8 * length), 16 } } },
|
||||
range { range },
|
||||
step { step },
|
||||
length_ { length },
|
||||
fill_char { fill_char }
|
||||
{
|
||||
flags.focusable = true;
|
||||
}
|
||||
|
||||
int32_t NumberField::value() const {
|
||||
return value_;
|
||||
}
|
||||
|
@@ -29,8 +29,6 @@
|
||||
|
||||
#include "utility.hpp"
|
||||
|
||||
#include "message.hpp"
|
||||
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
#include <string>
|
||||
@@ -41,25 +39,14 @@ void dirty_set();
|
||||
void dirty_clear();
|
||||
bool is_dirty();
|
||||
|
||||
// TODO: Move these somewhere else!
|
||||
// TODO: Allow l=0 to not fill/justify? Already using this way in ui_spectrum.hpp...
|
||||
std::string to_string_dec_uint(const uint32_t n, const int32_t l = 0, const char fill = 0);
|
||||
std::string to_string_dec_int(const int32_t n, const int32_t l = 0, const char fill = 0);
|
||||
std::string to_string_hex(const uint32_t n, const int32_t l = 0);
|
||||
|
||||
class Context {
|
||||
public:
|
||||
FocusManager& focus_manager() {
|
||||
return focus_manager_;
|
||||
}
|
||||
|
||||
MessageHandlerMap& message_map() {
|
||||
return message_map_;
|
||||
}
|
||||
|
||||
private:
|
||||
FocusManager focus_manager_;
|
||||
MessageHandlerMap message_map_;
|
||||
};
|
||||
|
||||
class Widget {
|
||||
@@ -68,12 +55,6 @@ public:
|
||||
) : parent_rect { }
|
||||
{
|
||||
}
|
||||
|
||||
constexpr Widget(
|
||||
Point parent_point
|
||||
) : parent_rect { parent_point, { 24, 24 } }
|
||||
{
|
||||
}
|
||||
|
||||
constexpr Widget(
|
||||
Rect parent_rect
|
||||
@@ -150,6 +131,8 @@ protected:
|
||||
.highlighted = false,
|
||||
.visible = false,
|
||||
};
|
||||
|
||||
void dirty_overlapping_children_in_rect(const Rect& child_rect);
|
||||
};
|
||||
|
||||
class View : public Widget {
|
||||
@@ -174,6 +157,8 @@ public:
|
||||
|
||||
virtual Widget* initial_focus();
|
||||
|
||||
virtual std::string title() const;
|
||||
|
||||
protected:
|
||||
std::vector<Widget*> children_;
|
||||
Rect dirty_screen_rect;
|
||||
@@ -183,13 +168,7 @@ protected:
|
||||
|
||||
class Rectangle : public Widget {
|
||||
public:
|
||||
constexpr Rectangle(
|
||||
const Rect parent_rect,
|
||||
const Color c
|
||||
) : Widget { parent_rect },
|
||||
color { c }
|
||||
{
|
||||
}
|
||||
Rectangle(Rect parent_rect, Color c);
|
||||
|
||||
void paint(Painter& painter) override;
|
||||
|
||||
@@ -205,19 +184,8 @@ public:
|
||||
) : text { "" } {
|
||||
}
|
||||
|
||||
Text(
|
||||
Rect parent_rect,
|
||||
std::string text
|
||||
) : Widget { parent_rect },
|
||||
text { text }
|
||||
{
|
||||
}
|
||||
|
||||
Text(
|
||||
Rect parent_rect
|
||||
) : Text { parent_rect, { } }
|
||||
{
|
||||
}
|
||||
Text(Rect parent_rect, std::string text);
|
||||
Text(Rect parent_rect);
|
||||
|
||||
void set(const std::string value);
|
||||
void set_style(const Style* new_style);
|
||||
@@ -267,16 +235,8 @@ private:
|
||||
class Button : public Widget {
|
||||
public:
|
||||
std::function<void(Button&)> on_select;
|
||||
std::function<void(Button&,KeyEvent)> on_dir;
|
||||
|
||||
Button(
|
||||
Rect parent_rect,
|
||||
std::string text
|
||||
) : Widget { parent_rect },
|
||||
text_ { text }
|
||||
{
|
||||
flags.focusable = true;
|
||||
}
|
||||
Button(Rect parent_rect, std::string text);
|
||||
|
||||
Button(
|
||||
) : Button { { }, { } }
|
||||
@@ -307,18 +267,7 @@ public:
|
||||
|
||||
std::function<void(size_t, value_t)> on_change;
|
||||
|
||||
OptionsField(
|
||||
Point parent_pos,
|
||||
size_t length,
|
||||
options_t options
|
||||
) : Widget { { parent_pos, { static_cast<ui::Dim>(8 * length), 16 } } },
|
||||
length_ { length },
|
||||
options { options }
|
||||
{
|
||||
flags.focusable = true;
|
||||
}
|
||||
|
||||
void set_options(options_t new_options);
|
||||
OptionsField(Point parent_pos, size_t length, options_t options);
|
||||
|
||||
size_t selected_index() const;
|
||||
void set_selected_index(const size_t new_index);
|
||||
@@ -342,20 +291,7 @@ public:
|
||||
|
||||
using range_t = std::pair<int32_t, int32_t>;
|
||||
|
||||
NumberField(
|
||||
Point parent_pos,
|
||||
size_t length,
|
||||
range_t range,
|
||||
int32_t step,
|
||||
char fill_char
|
||||
) : Widget { { parent_pos, { static_cast<ui::Dim>(8 * length), 16 } } },
|
||||
range { range },
|
||||
step { step },
|
||||
length_ { length },
|
||||
fill_char { fill_char }
|
||||
{
|
||||
flags.focusable = true;
|
||||
}
|
||||
NumberField(Point parent_pos, size_t length, range_t range, int32_t step, char fill_char);
|
||||
|
||||
NumberField(const NumberField&) = delete;
|
||||
NumberField(NumberField&&) = delete;
|
||||
|
81
firmware/common/units.hpp
Normal file
81
firmware/common/units.hpp
Normal file
@@ -0,0 +1,81 @@
|
||||
/*
|
||||
* Copyright (C) 2016 Jared Boone, ShareBrained Technology, Inc.
|
||||
*
|
||||
* 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 __UNITS_H__
|
||||
#define __UNITS_H__
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
namespace units {
|
||||
|
||||
class Pressure {
|
||||
public:
|
||||
constexpr Pressure(
|
||||
) : kpa_ { 0 }
|
||||
{
|
||||
}
|
||||
|
||||
constexpr Pressure(
|
||||
const int kilopascal
|
||||
) : kpa_ { static_cast<int16_t>(kilopascal) }
|
||||
{
|
||||
}
|
||||
|
||||
int kilopascal() const {
|
||||
return kpa_;
|
||||
}
|
||||
|
||||
int psi() const {
|
||||
return kpa_ * 1000 / 6895;
|
||||
}
|
||||
|
||||
private:
|
||||
int16_t kpa_;
|
||||
};
|
||||
|
||||
class Temperature {
|
||||
public:
|
||||
constexpr Temperature(
|
||||
) : c_ { 0 }
|
||||
{
|
||||
}
|
||||
|
||||
constexpr Temperature(
|
||||
const int celsius
|
||||
) : c_ { static_cast<int16_t>(celsius) }
|
||||
{
|
||||
}
|
||||
|
||||
int celsius() const {
|
||||
return c_;
|
||||
}
|
||||
|
||||
int fahrenheit() const {
|
||||
return (c_ * 9 / 5) + 32;
|
||||
}
|
||||
|
||||
private:
|
||||
int16_t c_;
|
||||
};
|
||||
|
||||
} /* namespace units */
|
||||
|
||||
#endif/*__UNITS_H__*/
|
@@ -25,6 +25,7 @@
|
||||
#include <type_traits>
|
||||
#include <cstdint>
|
||||
#include <cstddef>
|
||||
#include <algorithm>
|
||||
#include <complex>
|
||||
#include <memory>
|
||||
|
||||
@@ -70,6 +71,37 @@ inline float magnitude_squared(const std::complex<float> c) {
|
||||
return r2 + i2;
|
||||
}
|
||||
|
||||
template<class T>
|
||||
struct range_t {
|
||||
const T minimum;
|
||||
const T maximum;
|
||||
|
||||
const T& clip(const T& value) const {
|
||||
return std::max(std::min(value, maximum), minimum);
|
||||
}
|
||||
|
||||
void reset_if_outside(T& value, const T& reset_value) const {
|
||||
if( (value < minimum ) ||
|
||||
(value > maximum ) ) {
|
||||
value = reset_value;
|
||||
}
|
||||
}
|
||||
|
||||
bool below_range(const T& value) const {
|
||||
return value < minimum;
|
||||
}
|
||||
|
||||
bool contains(const T& value) const {
|
||||
// TODO: Subtle gotcha here! Range test doesn't include maximum!
|
||||
return (value >= minimum) && (value < maximum);
|
||||
}
|
||||
|
||||
bool out_of_range(const T& value) const {
|
||||
// TODO: Subtle gotcha here! Range test in contains() doesn't include maximum!
|
||||
return !contains(value);
|
||||
}
|
||||
};
|
||||
|
||||
namespace std {
|
||||
|
||||
/*! Stephan T Lavavej (STL!) implementation of make_unique, which has been accepted into the C++14 standard.
|
||||
|
@@ -26,20 +26,14 @@
|
||||
|
||||
#include <hal.h>
|
||||
|
||||
static inline bool m4_flag_saturation() {
|
||||
return __get_APSR() & (1U << 27);
|
||||
}
|
||||
|
||||
static inline void clear_m4_flag_saturation() {
|
||||
uint32_t flags = 1;
|
||||
__asm volatile ("MSR APSR_nzcvqg, %0" : : "r" (flags));
|
||||
}
|
||||
|
||||
static inline complex32_t multiply_conjugate_s16_s32(const complex16_t::rep_type a, const complex16_t::rep_type b) {
|
||||
// conjugate: conj(a + bj) = a - bj
|
||||
// multiply: (a + bj) * (c + dj) = (ac - bd) + (bc + ad)j
|
||||
// conjugate-multiply: (ac + bd) + (bc - ad)j
|
||||
//return { a.real() * b.real() + a.imag() * b.imag(), a.imag() * b.real() - a.real() * b.imag() };
|
||||
// NOTE: Did not use combination of SMUAD and SMUSDX because of non-saturating arithmetic.
|
||||
// const int32_t r = __SMUAD(a, b);
|
||||
// const int32_t i = __SMUSDX(b, a);
|
||||
const int32_t rr = __SMULBB(a, b);
|
||||
const int32_t ii = __SMULTT(a, b);
|
||||
const int32_t r = __QADD(rr, ii);
|
||||
|
@@ -27,15 +27,15 @@
|
||||
class volume_t {
|
||||
public:
|
||||
constexpr volume_t operator-() const {
|
||||
return { .cb = -cb };
|
||||
return { -cb };
|
||||
}
|
||||
|
||||
constexpr volume_t operator+(const volume_t& other) const {
|
||||
return { .cb = cb + other.cb };
|
||||
return { cb + other.cb };
|
||||
}
|
||||
|
||||
constexpr volume_t operator-(const volume_t& other) const {
|
||||
return { .cb = cb - other.cb };
|
||||
return { cb - other.cb };
|
||||
}
|
||||
|
||||
volume_t& operator+=(const volume_t& other) {
|
||||
@@ -52,11 +52,11 @@ public:
|
||||
}
|
||||
|
||||
static constexpr volume_t centibel(const int cb) {
|
||||
return { .cb = cb };
|
||||
return { cb };
|
||||
}
|
||||
|
||||
static constexpr volume_t decibel(const int db) {
|
||||
return { .cb = db * 10 };
|
||||
return { db * 10 };
|
||||
}
|
||||
|
||||
int32_t centibel() const {
|
||||
|
@@ -19,14 +19,82 @@
|
||||
* Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
#include <cstdint>
|
||||
#include <array>
|
||||
|
||||
#include "wm8731.hpp"
|
||||
#include "utility.hpp"
|
||||
|
||||
namespace wolfson {
|
||||
namespace wm8731 {
|
||||
|
||||
void WM8731::init() {
|
||||
reset();
|
||||
|
||||
write(PowerDownControl {
|
||||
.lineinpd = 1,
|
||||
.micpd = 0,
|
||||
.adcpd = 0,
|
||||
.dacpd = 0,
|
||||
.outpd = 0,
|
||||
.oscpd = 1,
|
||||
.clkoutpd = 1,
|
||||
.poweroff = 0,
|
||||
.reserved0 = 0,
|
||||
});
|
||||
|
||||
// write(SamplingControl {
|
||||
// .usb_normal = 0,
|
||||
// .bosr = 0,
|
||||
// .sr = 0,
|
||||
// .clkidiv2 = 0,
|
||||
// .clkodiv2 = 0,
|
||||
// .reserved0 = 0,
|
||||
// });
|
||||
|
||||
write(DigitalAudioInterfaceFormat {
|
||||
.format = 2,
|
||||
.iwl = 0,
|
||||
.lrp = 0,
|
||||
.lrswap = 0,
|
||||
.ms = 0,
|
||||
.bclkinv = 0,
|
||||
.reserved0 = 0,
|
||||
});
|
||||
|
||||
write(DigitalAudioPathControl {
|
||||
.adchpd = 0,
|
||||
.deemp = 0,
|
||||
.dacmu = 0,
|
||||
.hpor = 0,
|
||||
.reserved0 = 0,
|
||||
});
|
||||
|
||||
write(AnalogAudioPathControl {
|
||||
.micboost = 1, // Enable 20dB boost
|
||||
.mutemic = 0, // Disable mute (unmute)
|
||||
.insel = 1, // Microphone input to ADC
|
||||
.bypass = 0,
|
||||
.dacsel = 1,
|
||||
.sidetone = 0,
|
||||
.sideatt = 0,
|
||||
.reserved0 = 0,
|
||||
});
|
||||
|
||||
write(ActiveControl {
|
||||
.active = 1,
|
||||
.reserved0 = 0,
|
||||
});
|
||||
|
||||
set_line_in_volume(0.0_dB);
|
||||
headphone_mute();
|
||||
}
|
||||
|
||||
void WM8731::reset() {
|
||||
write(0x0f, 0);
|
||||
}
|
||||
|
||||
void WM8731::write(const Register reg) {
|
||||
write(toUType(reg), map.w[toUType(reg)]);
|
||||
}
|
||||
|
||||
void WM8731::write(const address_t reg_address, const reg_t value) {
|
||||
const uint16_t word = (reg_address << 9) | value;
|
||||
const std::array<uint8_t, 2> values {
|
||||
@@ -36,5 +104,59 @@ void WM8731::write(const address_t reg_address, const reg_t value) {
|
||||
bus.transmit(bus_address, values.data(), values.size());
|
||||
}
|
||||
|
||||
reg_t WM8731::read(const address_t reg_address) {
|
||||
return map.w[reg_address];
|
||||
}
|
||||
|
||||
void WM8731::write(const LeftLineIn value) {
|
||||
map.r.left_line_in = value;
|
||||
write(Register::LeftLineIn);
|
||||
}
|
||||
|
||||
void WM8731::write(const RightLineIn value) {
|
||||
map.r.right_line_in = value;
|
||||
write(Register::RightLineIn);
|
||||
}
|
||||
|
||||
void WM8731::write(const LeftHeadphoneOut value) {
|
||||
map.r.left_headphone_out = value;
|
||||
write(Register::LeftHeadphoneOut);
|
||||
}
|
||||
|
||||
void WM8731::write(const RightHeadphoneOut value) {
|
||||
map.r.right_headphone_out = value;
|
||||
write(Register::RightHeadphoneOut);
|
||||
}
|
||||
|
||||
void WM8731::write(const AnalogAudioPathControl value) {
|
||||
map.r.analog_audio_path_control = value;
|
||||
write(Register::AnalogAudioPathControl);
|
||||
}
|
||||
|
||||
void WM8731::write(const DigitalAudioPathControl value) {
|
||||
map.r.digital_audio_path_control = value;
|
||||
write(Register::DigitalAudioPathControl);
|
||||
}
|
||||
|
||||
void WM8731::write(const PowerDownControl value) {
|
||||
map.r.power_down_control = value;
|
||||
write(Register::PowerDownControl);
|
||||
}
|
||||
|
||||
void WM8731::write(const DigitalAudioInterfaceFormat value) {
|
||||
map.r.digital_audio_interface_format = value;
|
||||
write(Register::DigitalAudioInterfaceFormat);
|
||||
}
|
||||
|
||||
void WM8731::write(const SamplingControl value) {
|
||||
map.r.sampling_control = value;
|
||||
write(Register::SamplingControl);
|
||||
}
|
||||
|
||||
void WM8731::write(const ActiveControl value) {
|
||||
map.r.active_control = value;
|
||||
write(Register::ActiveControl);
|
||||
}
|
||||
|
||||
} /* namespace wm8731 */
|
||||
} /* namespace wolfson */
|
||||
|
@@ -23,11 +23,10 @@
|
||||
#define __WM8731_H__
|
||||
|
||||
#include <cstdint>
|
||||
#include <array>
|
||||
|
||||
#include "i2c_pp.hpp"
|
||||
#include "wm8731.hpp"
|
||||
|
||||
#include "utility.hpp"
|
||||
#include "volume.hpp"
|
||||
|
||||
namespace wolfson {
|
||||
@@ -289,71 +288,9 @@ public:
|
||||
{
|
||||
}
|
||||
|
||||
void init() {
|
||||
reset();
|
||||
void init();
|
||||
|
||||
write(PowerDownControl {
|
||||
.lineinpd = 1,
|
||||
.micpd = 0,
|
||||
.adcpd = 0,
|
||||
.dacpd = 0,
|
||||
.outpd = 0,
|
||||
.oscpd = 1,
|
||||
.clkoutpd = 1,
|
||||
.poweroff = 0,
|
||||
.reserved0 = 0,
|
||||
});
|
||||
|
||||
// write(SamplingControl {
|
||||
// .usb_normal = 0,
|
||||
// .bosr = 0,
|
||||
// .sr = 0,
|
||||
// .clkidiv2 = 0,
|
||||
// .clkodiv2 = 0,
|
||||
// .reserved0 = 0,
|
||||
// });
|
||||
|
||||
write(DigitalAudioInterfaceFormat {
|
||||
.format = 2,
|
||||
.iwl = 0,
|
||||
.lrp = 0,
|
||||
.lrswap = 0,
|
||||
.ms = 0,
|
||||
.bclkinv = 0,
|
||||
.reserved0 = 0,
|
||||
});
|
||||
|
||||
write(DigitalAudioPathControl {
|
||||
.adchpd = 0,
|
||||
.deemp = 0,
|
||||
.dacmu = 0,
|
||||
.hpor = 0,
|
||||
.reserved0 = 0,
|
||||
});
|
||||
|
||||
write(AnalogAudioPathControl {
|
||||
.micboost = 1, // Enable 20dB boost
|
||||
.mutemic = 0, // Disable mute (unmute)
|
||||
.insel = 1, // Microphone input to ADC
|
||||
.bypass = 0,
|
||||
.dacsel = 1,
|
||||
.sidetone = 0,
|
||||
.sideatt = 0,
|
||||
.reserved0 = 0,
|
||||
});
|
||||
|
||||
write(ActiveControl {
|
||||
.active = 1,
|
||||
.reserved0 = 0,
|
||||
});
|
||||
|
||||
set_line_in_volume(0.0_dB);
|
||||
headphone_mute();
|
||||
}
|
||||
|
||||
void reset() {
|
||||
write(0x0f, 0);
|
||||
}
|
||||
void reset();
|
||||
|
||||
void set_line_in_volume(const volume_t volume) {
|
||||
const auto normalized = line_in_gain_range.normalize(volume);
|
||||
@@ -374,7 +311,7 @@ public:
|
||||
|
||||
write(LeftHeadphoneOut {
|
||||
.lhpvol = static_cast<reg_t>(n),
|
||||
.lzcen = 1,
|
||||
.lzcen = 0,
|
||||
.lrhpboth = 1,
|
||||
.reserved0 = 0,
|
||||
});
|
||||
@@ -394,66 +331,27 @@ public:
|
||||
// write(Register::AnalogAudioPathControl);
|
||||
// }
|
||||
|
||||
reg_t read(const address_t reg_address);
|
||||
|
||||
private:
|
||||
I2C& bus;
|
||||
const I2C::address_t bus_address;
|
||||
RegisterMap map { default_after_reset };
|
||||
|
||||
void write(const Register reg) {
|
||||
write(toUType(reg), map.w[toUType(reg)]);
|
||||
}
|
||||
|
||||
void write(const Register reg);
|
||||
|
||||
void write(const address_t reg_address, const reg_t value);
|
||||
|
||||
void write(const LeftLineIn value) {
|
||||
map.r.left_line_in = value;
|
||||
write(Register::LeftLineIn);
|
||||
}
|
||||
|
||||
void write(const RightLineIn value) {
|
||||
map.r.right_line_in = value;
|
||||
write(Register::RightLineIn);
|
||||
}
|
||||
|
||||
void write(const LeftHeadphoneOut value) {
|
||||
map.r.left_headphone_out = value;
|
||||
write(Register::LeftHeadphoneOut);
|
||||
}
|
||||
|
||||
void write(const RightHeadphoneOut value) {
|
||||
map.r.right_headphone_out = value;
|
||||
write(Register::RightHeadphoneOut);
|
||||
}
|
||||
|
||||
void write(const AnalogAudioPathControl value) {
|
||||
map.r.analog_audio_path_control = value;
|
||||
write(Register::AnalogAudioPathControl);
|
||||
}
|
||||
|
||||
void write(const DigitalAudioPathControl value) {
|
||||
map.r.digital_audio_path_control = value;
|
||||
write(Register::DigitalAudioPathControl);
|
||||
}
|
||||
|
||||
void write(const PowerDownControl value) {
|
||||
map.r.power_down_control = value;
|
||||
write(Register::PowerDownControl);
|
||||
}
|
||||
|
||||
void write(const DigitalAudioInterfaceFormat value) {
|
||||
map.r.digital_audio_interface_format = value;
|
||||
write(Register::DigitalAudioInterfaceFormat);
|
||||
}
|
||||
|
||||
void write(const SamplingControl value) {
|
||||
map.r.sampling_control = value;
|
||||
write(Register::SamplingControl);
|
||||
}
|
||||
|
||||
void write(const ActiveControl value) {
|
||||
map.r.active_control = value;
|
||||
write(Register::ActiveControl);
|
||||
}
|
||||
void write(const LeftLineIn value);
|
||||
void write(const RightLineIn value);
|
||||
void write(const LeftHeadphoneOut value);
|
||||
void write(const RightHeadphoneOut value);
|
||||
void write(const AnalogAudioPathControl value);
|
||||
void write(const DigitalAudioPathControl value);
|
||||
void write(const PowerDownControl value);
|
||||
void write(const DigitalAudioInterfaceFormat value);
|
||||
void write(const SamplingControl value);
|
||||
void write(const ActiveControl value);
|
||||
};
|
||||
|
||||
} /* namespace wm8731 */
|
||||
|
Reference in New Issue
Block a user