GPS implementation (#216)
Implemented basic GPS support: - GPS HAL - GPS Service - GPS Settings app
This commit is contained in:
committed by
GitHub
parent
2345ba6d13
commit
14e459e50f
@@ -1,5 +1,6 @@
|
||||
#pragma once
|
||||
|
||||
#include "Tactility/hal/gps/GpsDevice.h"
|
||||
#include "Tactility/hal/sdcard/SdCardDevice.h"
|
||||
#include "Tactility/hal/spi/Spi.h"
|
||||
#include "Tactility/hal/uart/Uart.h"
|
||||
@@ -44,14 +45,17 @@ struct Configuration {
|
||||
/** An optional power interface for battery or other power delivery. */
|
||||
const CreatePower _Nullable power = nullptr;
|
||||
|
||||
/** A list of I2C interfaces */
|
||||
/** A list of I2C interface configurations */
|
||||
const std::vector<i2c::Configuration> i2c = {};
|
||||
|
||||
/** A list of SPI interfaces */
|
||||
/** A list of SPI interface configurations */
|
||||
const std::vector<spi::Configuration> spi = {};
|
||||
|
||||
/** A list of UART interfaces */
|
||||
/** A list of UART interface configurations */
|
||||
const std::vector<uart::Configuration> uart = {};
|
||||
|
||||
/** A list of GPS device configurations */
|
||||
const std::vector<gps::GpsDevice::Configuration> gps = {};
|
||||
};
|
||||
|
||||
} // namespace
|
||||
|
||||
@@ -22,7 +22,8 @@ public:
|
||||
Touch,
|
||||
SdCard,
|
||||
Keyboard,
|
||||
Power
|
||||
Power,
|
||||
Gps
|
||||
};
|
||||
|
||||
typedef uint32_t Id;
|
||||
@@ -36,7 +37,7 @@ public:
|
||||
Device();
|
||||
virtual ~Device() = default;
|
||||
|
||||
Id getId() const { return id; }
|
||||
inline Id getId() const { return id; }
|
||||
|
||||
/** The type of device. */
|
||||
virtual Type getType() const = 0;
|
||||
|
||||
@@ -0,0 +1,99 @@
|
||||
#pragma once
|
||||
|
||||
#include "../Device.h"
|
||||
#include "../uart/Uart.h"
|
||||
#include "GpsDeviceInitDefault.h"
|
||||
#include "Satellites.h"
|
||||
|
||||
#include <Tactility/Mutex.h>
|
||||
#include <Tactility/Thread.h>
|
||||
|
||||
#include <minmea.h>
|
||||
#include <utility>
|
||||
|
||||
namespace tt::hal::gps {
|
||||
|
||||
class GpsDevice : public Device {
|
||||
|
||||
public:
|
||||
|
||||
typedef int SatelliteSubscriptionId;
|
||||
typedef int LocationSubscriptionId;
|
||||
|
||||
struct Configuration {
|
||||
std::string name;
|
||||
uart_port_t uartPort;
|
||||
uint32_t baudRate;
|
||||
std::function<bool(uart_port_t)> initFunction = initGpsDefault;
|
||||
};
|
||||
|
||||
private:
|
||||
|
||||
struct SatelliteSubscription {
|
||||
SatelliteSubscriptionId id;
|
||||
std::shared_ptr<std::function<void(Device::Id id, const minmea_sat_info&)>> onData;
|
||||
};
|
||||
|
||||
struct LocationSubscription {
|
||||
LocationSubscriptionId id;
|
||||
std::shared_ptr<std::function<void(Device::Id id, const minmea_sentence_rmc&)>> onData;
|
||||
};
|
||||
|
||||
const Configuration configuration;
|
||||
Mutex mutex;
|
||||
std::unique_ptr<Thread> thread;
|
||||
bool threadInterrupted = false;
|
||||
std::vector<SatelliteSubscription> satelliteSubscriptions;
|
||||
std::vector<LocationSubscription> locationSubscriptions;
|
||||
SatelliteSubscriptionId lastSatelliteSubscriptionId = 0;
|
||||
LocationSubscriptionId lastLocationSubscriptionId = 0;
|
||||
|
||||
static int32_t threadMainStatic(void* parameter);
|
||||
int32_t threadMain();
|
||||
|
||||
bool isThreadInterrupted() const;
|
||||
|
||||
public:
|
||||
|
||||
explicit GpsDevice(Configuration configuration) : configuration(std::move(configuration)) {
|
||||
assert(this->configuration.initFunction != nullptr);
|
||||
}
|
||||
|
||||
~GpsDevice() override = default;
|
||||
|
||||
Type getType() const override { return Type::Gps; }
|
||||
|
||||
std::string getName() const override { return configuration.name; }
|
||||
std::string getDescription() const override { return ""; }
|
||||
|
||||
bool start();
|
||||
bool stop();
|
||||
|
||||
bool isStarted() const;
|
||||
|
||||
SatelliteSubscriptionId subscribeSatellites(const std::function<void(Device::Id deviceId, const minmea_sat_info&)>& onData) {
|
||||
satelliteSubscriptions.push_back({
|
||||
.id = ++lastSatelliteSubscriptionId,
|
||||
.onData = std::make_shared<std::function<void(Device::Id, const minmea_sat_info&)>>(onData)
|
||||
});
|
||||
return lastSatelliteSubscriptionId;
|
||||
}
|
||||
|
||||
void unsubscribeSatellites(SatelliteSubscriptionId subscriptionId) {
|
||||
std::erase_if(satelliteSubscriptions, [subscriptionId](auto& subscription) { return subscription.id == subscriptionId; });
|
||||
}
|
||||
|
||||
LocationSubscriptionId subscribeLocations(const std::function<void(Device::Id deviceId, const minmea_sentence_rmc&)>& onData) {
|
||||
locationSubscriptions.push_back({
|
||||
.id = ++lastLocationSubscriptionId,
|
||||
.onData = std::make_shared<std::function<void(Device::Id, const minmea_sentence_rmc&)>>(onData)
|
||||
});
|
||||
return lastLocationSubscriptionId;
|
||||
}
|
||||
|
||||
void unsubscribeLocations(SatelliteSubscriptionId subscriptionId) {
|
||||
std::erase_if(locationSubscriptions, [subscriptionId](auto& subscription) { return subscription.id == subscriptionId; });
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
#pragma once
|
||||
|
||||
#include <Tactility/hal/uart/Uart.h>
|
||||
|
||||
namespace tt::hal::gps {
|
||||
|
||||
bool initGpsDefault(uart_port_t port);
|
||||
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
#pragma once
|
||||
|
||||
#include <Tactility/RtosCompat.h>
|
||||
#include <Tactility/Mutex.h>
|
||||
|
||||
#include <minmea.h>
|
||||
|
||||
#include <ranges>
|
||||
#include <memory>
|
||||
|
||||
namespace tt::hal::gps {
|
||||
|
||||
/** Thread-safe storage of recent satellites */
|
||||
class SatelliteStorage {
|
||||
|
||||
public:
|
||||
|
||||
static constexpr size_t recordCount = 32;
|
||||
|
||||
private:
|
||||
|
||||
struct SatelliteRecord {
|
||||
minmea_sat_info data {
|
||||
.nr = 0,
|
||||
.elevation = 0,
|
||||
.azimuth = 0,
|
||||
.snr = 0
|
||||
};
|
||||
TickType_t lastUpdated = 0;
|
||||
bool inUse = false;
|
||||
};
|
||||
|
||||
Mutex mutex = Mutex(Mutex::Type::Recursive);
|
||||
std::array<SatelliteRecord, recordCount> records;
|
||||
uint16_t recycleTimeSeconds;
|
||||
uint16_t recentTimeSeconds;
|
||||
|
||||
SatelliteRecord* findRecord(int number);
|
||||
|
||||
SatelliteRecord* findUnusedRecord();
|
||||
|
||||
SatelliteRecord* findRecordToRecycle();
|
||||
|
||||
/** Tries to find an existing record, otherwise return a free one, otherwise return the oldest active one */
|
||||
SatelliteRecord* findWithFallback(int number);
|
||||
|
||||
public:
|
||||
|
||||
explicit SatelliteStorage(
|
||||
uint16_t recycleTimeSeconds = 120,
|
||||
uint16_t recentTimeSeconds = 60
|
||||
) : recycleTimeSeconds(recycleTimeSeconds), recentTimeSeconds(recentTimeSeconds) {}
|
||||
|
||||
void notify(const minmea_sat_info& info);
|
||||
|
||||
void getRecords(const std::function<void(const minmea_sat_info&)>& onRecord) const;
|
||||
};
|
||||
|
||||
} // namespace tt::hal::gps
|
||||
@@ -56,6 +56,19 @@ bool lock(uart_port_t port, TickType_t timeout = 10 / portTICK_PERIOD_MS);
|
||||
bool unlock(uart_port_t port);
|
||||
|
||||
size_t read(uart_port_t port, uint8_t* buffer, size_t bufferSize, TickType_t timeout = 10 / portTICK_PERIOD_MS);
|
||||
bool readByte(uart_port_t port, uint8_t* output, TickType_t timeout = 10 / portTICK_PERIOD_MS);
|
||||
size_t write(uart_port_t port, const uint8_t* buffer, size_t bufferSize, TickType_t timeout = 10 / portTICK_PERIOD_MS);
|
||||
bool writeString(uart_port_t port, const char* buffer, TickType_t timeout = 10 / portTICK_PERIOD_MS);
|
||||
|
||||
size_t available(uart_port_t port, TickType_t timeout = 10 / portTICK_PERIOD_MS);
|
||||
|
||||
bool setBaudRate(uart_port_t port, uint32_t baudRate, TickType_t timeout = 10 / portTICK_PERIOD_MS);
|
||||
uint32_t getBaudRate(uart_port_t port);
|
||||
|
||||
void flush(uart_port_t port, TickType_t timeout = 10 / portTICK_PERIOD_MS);
|
||||
void flushInput(uart_port_t port, TickType_t timeout = 10 / portTICK_PERIOD_MS);
|
||||
|
||||
std::string readStringUntil(uart_port_t port, char untilChar, TickType_t timeout = 10 / portTICK_PERIOD_MS);
|
||||
bool readUntil(uart_port_t port, uint8_t* buffer, size_t bufferSize, uint8_t untilByte, TickType_t timeout = 10 / portTICK_PERIOD_MS);
|
||||
|
||||
} // namespace tt::hal::uart
|
||||
|
||||
@@ -0,0 +1,22 @@
|
||||
#pragma once
|
||||
|
||||
#include "Tactility/hal/gps/GpsDevice.h"
|
||||
|
||||
namespace tt::service::gps {
|
||||
|
||||
/** Register a hardware device to the GPS service. */
|
||||
void addGpsDevice(const std::shared_ptr<hal::gps::GpsDevice>& device);
|
||||
|
||||
/** Deregister a hardware device to the GPS service. */
|
||||
void removeGpsDevice(const std::shared_ptr<hal::gps::GpsDevice>& device);
|
||||
|
||||
/** @return true when GPS is set to receive updates from at least 1 device */
|
||||
bool startReceiving();
|
||||
|
||||
/** Turn GPS receiving off */
|
||||
void stopReceiving();
|
||||
|
||||
/** @return true when GPS receiver is on and 1 or more devices are active */
|
||||
bool isReceiving();
|
||||
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
#pragma once
|
||||
|
||||
#include <minmea.h>
|
||||
|
||||
namespace tt::hal::gps {
|
||||
|
||||
/** @return true when the input float is valid (contains non-zero values) */
|
||||
inline bool isValid(const minmea_float& inFloat) { return inFloat.value != 0 && inFloat.scale != 0; }
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user