Commit 26451868 authored by Guillaume Gonnet's avatar Guillaume Gonnet
Browse files

Complete implementation of benchmark app.

parent 65c85224
......@@ -23,9 +23,9 @@ DEVELHELP ?= 1
QUIET ?= 1
# Reset EEPROM data on start?
RESET_ON_START ?= 1
CFLAGS += -DRESET_ON_START=$(RESET_ON_START)
# Board number (starting at 1).
BOARD_NUMBER ?= 1
CFLAGS += -DBOARD_NUMBER=$(BOARD_NUMBER)
# We use "IMAG" region in LoRaMAC-node.
REGION ?= IMAG
......@@ -35,7 +35,6 @@ CFLAGS += -DREGION_$(REGION)
# All modules defined in this application.
USEMODULE += cubsat-app
USEMODULE += cubsat-app-benchmark
USEMODULE += cubsat-app-cmds
USEMODULE += cubsat-app-providers
USEMODULE += cubsat-app-drivers
USEMODULE += lorariot-ctrl
......@@ -52,9 +51,6 @@ USEMODULE += periph_eeprom
USEMODULE += periph_uart
USEMODULE += periph_i2c
# TMP.
FEATURES_REQUIRED += periph_rtc
# Source directory is "src".
DIRS += src
......
......@@ -10,7 +10,7 @@
| Longitude | | 3 * 8 = 24 |
| Altitude | 0 - 65535 | 2 * 8 = 16 |
This total length of the payload (without LoRaWAN header) is 10 bytes.
This total length of the payload (without LoRaWAN header) is 10 bytes.
All integers are big endian.
......@@ -32,9 +32,9 @@ coding rate of `4/8`.
## Transmission power
This parameter is the index in the array below (values in dBm).
> `2, 5, 8, 11, 14, 15, 16, 17, 18, 19`
> `2, 5, 8, 11, 13, 15, 17, 18, 19`
This array has 10 elements so this parameter is coded on **4 bits**.
This array has 9 elements so this parameter is coded on **4 bits**.
## Temperature
......
......@@ -45,7 +45,7 @@ const DataRate = (byte >> 5) & 0b111;
const CodingRate = (byte >> 4) & 0b1;
let Power = byte & 0b1111;
Power = [2, 5, 8, 11, 14, 15, 16, 17, 18, 19][Power];
Power = [2, 5, 8, 11, 13, 15, 17, 18, 19][Power];
// Extract temperature.
......
......@@ -7,7 +7,6 @@
MODULE := cubsat-app
DIRS += benchmark
DIRS += commands
DIRS += providers
DIRS += drivers
......
......@@ -9,9 +9,19 @@ This project is under the MIT license
#include "benchmark.h"
#include <xtimer.h>
// Stack of the benchmark thread.
#define BM_STACKSIZE (THREAD_STACKSIZE_DEFAULT)
static char bm_stack[BM_STACKSIZE];
// PID of benchmark thread.
kernel_pid_t bm_pid;
// Transmission powers to test.
uint8_t bm_power[] = { 2, 5, 8, 11, 14, 15, 16, 17, 18, 19 };
uint8_t bm_power[] = { 2, 5, 8, 11, 13, 15, 17, 18, 19 };
// Datarates to test.
uint8_t bm_dr[] = { DR_0, DR_1, DR_2, DR_3, DR_4, DR_5 };
......@@ -20,7 +30,16 @@ uint8_t bm_dr[] = { DR_0, DR_1, DR_2, DR_3, DR_4, DR_5 };
uint8_t bm_cr[] = { CR_4_5, CR_4_8 };
// Providers to test.
const provider_t *bm_pvd[] = { &pvd_orange_bm, &pvd_ttn_bm };
const provider_t *bm_pvd[] = { &pvd_orange_bm, &pvd_campus_bm, &pvd_ttn_bm };
// Ports to use for benchmark.
#define MIN_PORT 10
#define MAX_PORT 220
// Buffer and payload for benchmark.
static uint8_t bm_buffer[10];
lorariot_mcps_t bm_payload = LORARIOT_UNCONFIRMED_EX(DR_0, 50, 10, bm_buffer);
// Count the number of elements in an array.
......@@ -31,16 +50,44 @@ const provider_t *bm_pvd[] = { &pvd_orange_bm, &pvd_ttn_bm };
for (uint8_t name ## _i = 0; name ## _i < CNT(bm_ ## name); name ## _i++)
// Run the benchmark.
void bm_run(void)
// Run the benchmark one time.
void bm_run(uint8_t port)
{
bm_payload.port = 50; // TODO: change port.
bm_payload.port = port;
foreach_param(power) // Power: 10 values
foreach_param(power) // Power: 9 values
foreach_param(cr) // Coding rate: 2 values
foreach_param(dr) // Datarate: 6 values
foreach_param(pvd) { // Providers: 2 values
bm_encode_payload(power_i, dr_i, cr_i);
foreach_param(pvd) { // Providers: 3 values
bm_encode_payload(&bm_payload, power_i, dr_i, cr_i);
pvd_send(bm_pvd[pvd_i], &bm_payload);
xtimer_usleep(50 * 1000); // 50 ms
}
}
// Thread that run the benchmark indefinitely.
static void *bm_thread(void *args)
{
(void)args;
xtimer_sleep(5); // Wait 5s and start running benchmark.
printf("Staring benchmark!\n");
uint8_t port = MIN_PORT;
while (true) {
bm_run(port);
xtimer_sleep(5); // 5s
port = MIN_PORT + ((port + 1 - MIN_PORT) % (MAX_PORT - MIN_PORT));
}
return NULL;
}
// Start benchmark thread.
void bm_start_thread(void)
{
bm_pid = thread_create(bm_stack, sizeof(bm_stack),
THREAD_PRIORITY_MAIN - 1, THREAD_CREATE_STACKTEST,
bm_thread, NULL, "bm-thread");
}
......@@ -12,6 +12,8 @@ This project is under the MIT license
#include "providers/providers.h"
#include "lorariot.h"
#include <thread.h>
// Update temperature value every .. ms
#define TEMPERATURE_UPDATE_MS 1500
......@@ -33,15 +35,27 @@ extern uint8_t bm_cr[];
extern const provider_t *bm_pvd[];
// PID of benchmark thread.
extern kernel_pid_t bm_pid;
/**
* @brief Encode message data to the payload.
* @param mcps MCPS message that will be sent.
* @param power_i Transmission power (index in `bm_power`).
* @param dr_i Datarate (index in `bm_dr`).
* @param cr_i Coding rate (index in `bm_cr`).
*/
void bm_encode_payload(uint8_t power_i, uint8_t dr_i, uint8_t cr_i);
void bm_encode_payload(lorariot_mcps_t *mcps, uint8_t power_i, uint8_t dr_i,
uint8_t cr_i);
/**
* @brief Run the benchmark one time.
* @param port LoRaWAN port to use in messages.
*/
void bm_run(uint8_t port);
/**
* @brief Run the benchmark.
* @brief Start benchmark thread.
*/
void bm_run(void);
void bm_start_thread(void);
......@@ -12,39 +12,46 @@ This project is under the MIT license
#include "drivers/ds75lx.h"
#include <xtimer.h>
#include <mutex.h>
// Buffer that contains encoded data.
static uint8_t bm_buffer[10];
// Temperature cache.
static uint32_t temp_last_usec;
static uint16_t temp_cache = 0;
// Static payload for benchmark.
lorariot_mcps_t bm_payload = LORARIOT_UNCONFIRMED_EX(DR_0, 50, 10, bm_buffer);
// Mutex that protect temperature cache.
static mutex_t temp_mutex = MUTEX_INIT;
// Get temperature with right payload format.
static uint16_t bm_get_temperature(void)
{
uint32_t now = xtimer_now_usec();
if (now - temp_last_usec < (TEMPERATURE_UPDATE_MS * 1000))
return temp_cache;
// There are two threads that can use this function: benchmark thread and
// ping thread.
mutex_lock(&temp_mutex);
int16_t temp;
ds75_read_temperature(&temp);
// Limit the number of times we read the temperature.
uint32_t now = xtimer_now_usec();
if (now - temp_last_usec >= (TEMPERATURE_UPDATE_MS * 1000)) {
int16_t temp;
ds75_read_temperature(&temp);
temp_cache = ((uint16_t)(temp + (55 << 8)) >> 6) & 0x1FF;
temp_last_usec = now;
temp_cache = ((uint16_t)(temp + (55 << 8)) >> 6) & 0x1FF;
temp_last_usec = now;
}
// Unlock one other waiting thread.
mutex_unlock(&temp_mutex);
return temp_cache;
}
// Encode message data to the payload.
void bm_encode_payload(uint8_t power_i, uint8_t dr_i, uint8_t cr_i)
void bm_encode_payload(lorariot_mcps_t *mcps, uint8_t power_i, uint8_t dr_i,
uint8_t cr_i)
{
assert(mcps->buffer && mcps->len >= 10);
// Retreive GPS data.
int32_t lat, lon;
int16_t alt;
......@@ -55,32 +62,33 @@ void bm_encode_payload(uint8_t power_i, uint8_t dr_i, uint8_t cr_i)
// Encode LoRa settings (on 8 bits).
bm_buffer[0] =
uint8_t *buffer = (uint8_t *)mcps->buffer;
buffer[0] =
((dr_i & 0b111) << 5) |
((cr_i & 0b1) << 4) |
((power_i & 0b1111) << 0);
// Encode latitude (on 23 bits, we assume it's > 0).
bm_buffer[2] = ((uint32_t)lat >> 16) & 0x7F;
bm_buffer[3] = ((uint32_t)lat >> 8) & 0xFF;
bm_buffer[4] = ((uint32_t)lat >> 0) & 0xFF;
buffer[2] = ((uint32_t)lat >> 16) & 0x7F;
buffer[3] = ((uint32_t)lat >> 8) & 0xFF;
buffer[4] = ((uint32_t)lat >> 0) & 0xFF;
// Encode longitude (on 24 bits).
bm_buffer[5] = ((uint32_t)lon >> 16) & 0xFF;
bm_buffer[6] = ((uint32_t)lon >> 8) & 0xFF;
bm_buffer[7] = ((uint32_t)lon >> 0) & 0xFF;
buffer[5] = ((uint32_t)lon >> 16) & 0xFF;
buffer[6] = ((uint32_t)lon >> 8) & 0xFF;
buffer[7] = ((uint32_t)lon >> 0) & 0xFF;
// Encode temperature (on 9 bits).
bm_buffer[1] = (temp >> 1) & 0xFF;
bm_buffer[2] |= temp & 0b1;
buffer[1] = (temp >> 1) & 0xFF;
buffer[2] |= temp & 0b1;
// Encode altitude (on 16 bits);
bm_buffer[8] = ((uint16_t)alt >> 8) & 0xFF;
bm_buffer[9] = ((uint16_t)alt >> 0) & 0xFF;
buffer[8] = ((uint16_t)alt >> 8) & 0xFF;
buffer[9] = ((uint16_t)alt >> 0) & 0xFF;
// Change MCPS parameters for the next frame.
bm_payload.code_rate = bm_cr[cr_i];
bm_payload.power = bm_power[power_i];
bm_payload.dr = bm_dr[dr_i];
mcps->code_rate = bm_cr[cr_i];
mcps->power = bm_power[power_i];
mcps->dr = bm_dr[dr_i];
}
#
# Cubsat command module.
#
# Copyright (C) 2019, ENSIMAG students
# This project is under the MIT license
MODULE := cubsat-app-cmds
CFLAGS += -Wno-unused-parameter
include $(RIOTBASE)/Makefile.base
/*
Base commands.
Copyright (C) 2019, ENSIMAG students
This project is under the MIT license
*/
#include "commands.h"
// Debug "d" command.
int cmd_debug(int argc, char **argv)
{
// TODO.
return APP_CMD_OK;
}
// Reset "r" command.
int cmd_reset(int argc, char **argv)
{
// TODO.
return APP_CMD_OK;
}
/*
Controll commands (received from LoRaWAN or UART RX).
Copyright (C) 2019, ENSIMAG students
This project is under the MIT license
*/
#include "commands.h"
#include <string.h>
// All command entries.
static const app_cmd_ent_t cmd_entries[] = {
{ "d", cmd_debug },
{ "r", cmd_reset },
};
// Number of entries.
#define CMD_COUNT (sizeof(cmd_entries) / sizeof(*cmd_entries))
// Parse a command.
int cmds_parse(char *line)
{
// Count the number of arguments.
int argc = 1;
for (char *s = line; *s; s++) {
if (*s == ';') argc++;
}
// Split line into arguments.
char *argv[argc];
char *c = line;
for (char i = 0, *s = line; *s; s++) {
if (*s == ';') {
*s++ = '\0';
argv[(int)i++] = c;
c = s;
}
}
argv[argc - 1] = c;
// Get the command to run.
char *cmd = argv[0];
for (size_t i = 0; i < CMD_COUNT; i++) {
if (!strcmp(cmd, cmd_entries[i].name))
return cmd_entries[i].func(argc, argv);
}
// Command is not found.
return APP_CMD_NOT_FOUND;
}
/*
Controll commands (received from LoRaWAN or UART RX).
Copyright (C) 2019, ENSIMAG students
This project is under the MIT license
*/
#pragma once
/*
This module provides some commands for controlling this benchmark application.
There are two ways for sending a command: via a LoRaWAN packet on port 6 and via
Serial RX (message format is "§[msg]\n", without quotes, [msg] contains the
message).
Command messages are as following: "[cmd];[arg1];[arg2];.." (without quotes, no
new line at end), where [cmd] is the name of the command, [arg1] .. [argN] are
the command arguments.
*/
// An application command.
typedef int (*app_cmd_t)(int argc, char **argv);
// A command entry.
typedef struct {
char *name;
app_cmd_t func;
} app_cmd_ent_t;
// Standard command errors.
enum {
APP_CMD_OK = 0,
APP_CMD_NOT_FOUND = -50,
APP_CMD_INV_PARAMS = -51,
};
// Parse a command.
int cmds_parse(char *line);
// Initialize command module.
void cmds_init(void);
// All application commands.
int cmd_debug(int argc, char **argv);
int cmd_reset(int argc, char **argv);
/*
Received from LoRaWAN or UART RX.
Copyright (C) 2019, ENSIMAG students
This project is under the MIT license
*/
#include "commands.h"
#include "lorariot.h"
#include <thread.h>
#include <msg.h>
// Message queue of receive thread.
#define MSG_QUEUE_SIZE (4U)
static msg_t msg_queue[MSG_QUEUE_SIZE];
// LoRaRIOT event loop.
#define RECV_STACKSIZE (THREAD_STACKSIZE_DEFAULT)
static char recv_stack[RECV_STACKSIZE];
// Thread that receive commands.
static void *recv_thread(void *args)
{
(void)args;
msg_init_queue(msg_queue, MSG_QUEUE_SIZE);
while (true) {
msg_t msg;
msg_receive(&msg);
switch (msg.type) {
// TODO.
}
}
return NULL;
}
// Initialize command module.
void cmds_init(void)
{
kernel_pid_t pid = thread_create(recv_stack, sizeof(recv_stack),
THREAD_PRIORITY_MAIN + 3, THREAD_CREATE_STACKTEST,
recv_thread, NULL, "recv-thread");
lorariot_set_recv_thread(pid);
}
......@@ -9,6 +9,8 @@ This file is based on LoRaMAC-node from Semtech, under Revised BSD License.
#include "gps.h"
#include <mutex.h>
#include <string.h>
#include <stdlib.h>
......@@ -27,6 +29,9 @@ static const int32_t MaxWestPosition = 8388608; // -2^23
gps_nmea_t gps_nmea;
gps_data_t gps_data;
// Mutex that protect GPS data.
static mutex_t gps_mutex = MUTEX_INIT;
// Convert a nibble to hex char.
static int8_t nibble_to_hex(uint8_t a)
......@@ -264,6 +269,8 @@ uint8_t gps_parse_data(int8_t *rxBuffer, int32_t rxBufferSize)
// Get the lastest GPS position in binary format.
uint8_t gps_get_binary(int32_t *lat, int32_t *lon, int16_t *alt)
{
mutex_lock(&gps_mutex);
uint8_t status = gps_data.has_fix ? GPS_SUCCESS : GPS_FAIL;
if (!gps_data.has_fix)
gps_reset_data();
......@@ -272,6 +279,7 @@ uint8_t gps_get_binary(int32_t *lat, int32_t *lon, int16_t *alt)
*lon = gps_data.longitude_bin;
*alt = gps_data.altitude;
mutex_unlock(&gps_mutex);
return status;
}
......
......@@ -11,46 +11,25 @@ This project is under the MIT license
#include "drivers/ds75lx.h"
#include "drivers/gps.h"
#include "providers/providers.h"
#include "commands/commands.h"
#include "benchmark/benchmark.h"
#include <periph/rtc.h>
#include <periph/uart.h>
#include <periph/pm.h>
#include <xtimer.h>
extern gps_data_t gps_data;
#include <stdio.h>
// Buffer and payload for ping messages.
static uint8_t ping_buffer[10];
lorariot_mcps_t ping_payload = LORARIOT_UNCONFIRMED_EX(DR_0, 8, 10, ping_buffer);
// TMP: send a ping message.
static void send_message(void)
{
bm_encode_payload(9, 5, 1);
puts("Sending message ...");
pvd_send(&pvd_orange_cmd, &bm_payload);
puts("Message sent!");
xtimer_usleep(50 * 1000); // 50 ms
}
static kernel_pid_t pid;
static void rtc_cb(void *arg)
// Send a ping message.
void send_ping_message(void)
{
(void) arg;
uart_poweron(UART_DEV(0));
thread_wakeup(pid);
}
// Use parameters that make the message go as far as possible.
bm_encode_payload(&ping_payload, 8, 0, 1);
static void setup_rtc(void)
{
struct tm time;
rtc_get_time(&time);
time.tm_min += 3;
mktime(&time);
rtc_set_alarm(&time, rtc_cb, NULL);
// Now send the message the ping provider.
pvd_send(&pvd_ttn_ping, &ping_payload);
}
......@@ -64,35 +43,31 @@ int main(void)
lorariot_init();
ds75_init();
// cmds_init();
#if RESET_ON_START
// If enable, reset all providers when the application starts.
// pvd_reset_all();
#endif
// GPS is initialized by `stdio_init` because it depends on UART.
pid = thread_getpid();