This commit is contained in:
furrtek
2016-01-31 09:34:24 +01:00
parent 29ec87a9ad
commit 44638e504b
166 changed files with 8700 additions and 3967 deletions

View File

@@ -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 */

View File

@@ -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 */

View 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 */

View 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__*/

View File

@@ -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__*/

View 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__*/

View 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 */

View 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__*/

View File

@@ -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_;
};

View File

@@ -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__*/

View File

@@ -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;
};
};

View File

@@ -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);
}
}
*/
};

View File

@@ -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) {

View 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"

View 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__*/

View File

@@ -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__*/

View 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 */

View 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
View 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
View 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__*/

View 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__*/

View File

@@ -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__*/

View File

@@ -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();

View File

@@ -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);
}

View File

@@ -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

View File

@@ -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() ) {

View File

@@ -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(

View File

@@ -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();

View 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 };
}

View 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__*/

View File

@@ -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 {

View File

@@ -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;

View 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__*/

View File

@@ -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
View 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__*/

View File

@@ -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;
}

View File

@@ -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 {

View File

@@ -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) {

View File

@@ -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);
};

View File

@@ -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_;
}

View File

@@ -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
View 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__*/

View File

@@ -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.

View File

@@ -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);

View File

@@ -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 {

View File

@@ -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 */

View File

@@ -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 */