919 lines
20 KiB
C++

/* -*- tab-width: 2; mode: c; -*-
*
* C++ class for Arduino to function as a wrapper around opendroneid.
*
* Copyright (c) 2020-2022, Steve Jack.
*
* Jan. '23: Function to set the self ID.
*
* Nov. '22: Moved the processor specific code to a separate file.
* Had another attempt to get beacon to work.
* Tidied up the scheduler.
*
* May '22: opendroneid 2.0.
*
* Nov. '21: Removed some redundant code.
* Added option to use the new odid_wifi_build_message_pack_beacon_frame() function.
*
* Oct. '21: Updated for opendroneid release 1.0.
*
* May '21: Packed WiFi.
*
* April '21: Added support for beacon frames (untested).
* Minor tidying up.
*
* January '21: Modified initialisation of BasicID.
* Authenication codes.
*
*
* MIT licence.
*
* NOTES
*
* When porting to a different processor, check the time() function.
*
*
*/
#define DIAGNOSTICS 0
//
#pragma GCC diagnostic warning "-Wunused-variable"
#include <Arduino.h>
#include <time.h>
#include <sys/time.h>
extern "C" {
int clock_gettime(clockid_t,struct timespec *);
uint64_t alt_unix_secs(int,int,int,int,int,int);
}
#include "id_open.h"
/*
*
*/
ID_OpenDrone::ID_OpenDrone() {
int i;
static const char *dummy = "";
//
UAS_operator = (char *) dummy;
#if ID_OD_WIFI
// scrambled, not poached
// Nodemcu doesn't like certain mac addresses
// setting the first value to 0 seems to solve this
WiFi_mac_addr[i] = 0;
for (int i = 1; i < 6; i++) {
// WiFi_mac_addr[i] = (uint8_t) (rand() % 100 + 100);
WiFi_mac_addr[i] = (uint8_t) (rand() % 256);
}
memset(ssid,0,sizeof(ssid));
strcpy(ssid,"UAS_ID_OPEN");
beacon_interval = 10;
#if ID_OD_WIFI_BEACON
// If ODID_PACK_MAX_MESSAGES == 10, then the potential size of the beacon message is > 255.
#if ODID_PACK_MAX_MESSAGES > 9
#undef ODID_PACK_MAX_MESSAGES
#define ODID_PACK_MAX_MESSAGES 9
#endif
memset(beacon_frame,0,BEACON_FRAME_SIZE);
#if !USE_BEACON_FUNC
beacon_counter =
beacon_length =
beacon_timestamp =
beacon_seq =
beacon_payload = beacon_frame;
#endif
#endif
#endif
memset(msg_counter,0,sizeof(msg_counter));
//
// Below '// 0' indicates where we are setting 0 to 0 for clarity.
//
memset(&UAS_data,0,sizeof(ODID_UAS_Data));
basicID_data = &UAS_data.BasicID[0];
location_data = &UAS_data.Location;
selfID_data = &UAS_data.SelfID;
system_data = &UAS_data.System;
operatorID_data = &UAS_data.OperatorID;
for (i = 0; i < ODID_AUTH_MAX_PAGES; ++i) {
auth_data[i] = &UAS_data.Auth[i];
auth_data[i]->DataPage = i;
auth_data[i]->AuthType = ODID_AUTH_NONE; // 0
}
UAS_data.BasicID[0].IDType = ODID_IDTYPE_NONE; // 0
UAS_data.BasicID[0].UAType = ODID_UATYPE_NONE; // 0
UAS_data.BasicID[1].IDType = ODID_IDTYPE_NONE; // 0
UAS_data.BasicID[1].UAType = ODID_UATYPE_NONE; // 0
odid_initLocationData(location_data);
location_data->Status = ODID_STATUS_UNDECLARED; // 0
location_data->SpeedVertical = INV_SPEED_V;
location_data->HeightType = ODID_HEIGHT_REF_OVER_TAKEOFF;
location_data->HorizAccuracy = ODID_HOR_ACC_10_METER;
location_data->VertAccuracy = ODID_VER_ACC_10_METER;
location_data->BaroAccuracy = ODID_VER_ACC_10_METER;
location_data->SpeedAccuracy = ODID_SPEED_ACC_10_METERS_PER_SECOND;
location_data->TSAccuracy = ODID_TIME_ACC_1_5_SECOND;
selfID_data->DescType = ODID_DESC_TYPE_TEXT;
strcpy(selfID_data->Desc,"Recreational");
odid_initSystemData(system_data);
system_data->OperatorLocationType = ODID_OPERATOR_LOCATION_TYPE_TAKEOFF;
system_data->ClassificationType = ODID_CLASSIFICATION_TYPE_EU;
system_data->AreaCount = 1;
system_data->AreaRadius = 500;
system_data->AreaCeiling =
system_data->AreaFloor = -1000.0;
system_data->CategoryEU = ODID_CATEGORY_EU_SPECIFIC;
system_data->ClassEU = ODID_CLASS_EU_UNDECLARED;
system_data->OperatorAltitudeGeo = -1000.0;
operatorID_data->OperatorIdType = ODID_OPERATOR_ID;
//
construct2();
return;
}
/*
*
*/
void ID_OpenDrone::init(UTM_parameters *parameters) {
int status, i;
char text[128];
status = 0;
text[0] = text[63] = 0;
#if DIAGNOSTICS
Debug_Serial = &Serial;
#endif
// operator
UAS_operator = parameters->UAS_operator;
strncpy(operatorID_data->OperatorId,parameters->UAS_operator,ODID_ID_SIZE);
operatorID_data->OperatorId[sizeof(operatorID_data->OperatorId) - 1] = 0;
// basic
UAS_data.BasicID[0].UAType = (ODID_uatype_t) parameters->UA_type;
UAS_data.BasicID[1].UAType = (ODID_uatype_t) parameters->UA_type;
#if ID_NATIONAL
init_national(parameters);
#else
UAS_data.BasicID[0].IDType = (ODID_idtype_t) parameters->ID_type;
UAS_data.BasicID[1].IDType = (ODID_idtype_t) parameters->ID_type2;
switch(basicID_data->IDType) {
case ODID_IDTYPE_SERIAL_NUMBER:
strncpy(basicID_data->UASID,parameters->UAV_id,ODID_ID_SIZE);
break;
case ODID_IDTYPE_CAA_REGISTRATION_ID:
strncpy(basicID_data->UASID,parameters->UAS_operator,ODID_ID_SIZE);
break;
}
basicID_data->UASID[sizeof(basicID_data->UASID) - 1] = 0;
#endif
// system
if (parameters->region < 2) {
system_data->ClassificationType = (ODID_classification_type_t) parameters->region;
}
if (parameters->EU_category < 4) {
system_data->CategoryEU = (ODID_category_EU_t) parameters->EU_category;
}
if (parameters->EU_class < 8) {
system_data->ClassEU = (ODID_class_EU_t) parameters->EU_class;
}
//
encodeBasicIDMessage(&basicID_enc[0],&UAS_data.BasicID[0]);
encodeBasicIDMessage(&basicID_enc[1],&UAS_data.BasicID[1]);
encodeLocationMessage(&location_enc,location_data);
encodeAuthMessage(&auth_enc,auth_data[0]);
encodeSelfIDMessage(&selfID_enc,selfID_data);
encodeSystemMessage(&system_enc,system_data);
encodeOperatorIDMessage(&operatorID_enc,operatorID_data);
//
if (UAS_operator[0]) {
strncpy(ssid,UAS_operator,i = sizeof(ssid)); ssid[i - 1] = 0;
}
ssid_length = strlen(ssid);
init2(ssid,ssid_length,WiFi_mac_addr,wifi_channel);
#if ID_OD_WIFI
#if ID_OD_WIFI_BEACON && !USE_BEACON_FUNC
init_beacon();
// payload
beacon_payload = &beacon_frame[beacon_offset];
beacon_offset += 7;
*beacon_payload++ = 0xdd;
beacon_length = beacon_payload++;
*beacon_payload++ = 0xfa;
*beacon_payload++ = 0x0b;
*beacon_payload++ = 0xbc;
*beacon_payload++ = 0x0d;
beacon_counter = beacon_payload++;
beacon_max_packed = BEACON_FRAME_SIZE - beacon_offset - 2;
if (beacon_max_packed > (ODID_PACK_MAX_MESSAGES * ODID_MESSAGE_SIZE)) {
beacon_max_packed = (ODID_PACK_MAX_MESSAGES * ODID_MESSAGE_SIZE);
}
#endif
#endif
return;
}
/*
*
*/
void ID_OpenDrone::set_self_id(char *self_id) {
memset(selfID_data->Desc,0,ODID_STR_SIZE + 1);
strncpy(selfID_data->Desc,self_id,ODID_STR_SIZE);
encodeSelfIDMessage(&selfID_enc,selfID_data);
return;
}
/*
* These authentication functions need reviewing to make sure that they
* comply with opendroneid release 2.0.
*/
void ID_OpenDrone::set_auth(char *auth) {
set_auth((uint8_t *) auth,strlen(auth),0x0a);
return;
}
//
void ID_OpenDrone::set_auth(uint8_t *auth,short int len,uint8_t type) {
int i, j;
char text[160];
uint8_t check[32];
auth_page_count = 1;
if (len > MAX_AUTH_LENGTH) {
len = MAX_AUTH_LENGTH;
auth[len] = 0;
}
auth_data[0]->AuthType = (ODID_authtype_t) type;
for (i = 0; (i < 17)&&(auth[i]); ++i) {
check[i] =
auth_data[0]->AuthData[i] = auth[i];
}
check[i] =
auth_data[0]->AuthData[i] = 0;
if (Debug_Serial) {
sprintf(text,"Auth. Code \'%s\' (%d)\r\n",auth,len);
Debug_Serial->print(text);
sprintf(text,"Page 0 \'%s\'\r\n",check);
Debug_Serial->print(text);
}
if (len > 16) {
for (auth_page_count = 1; (auth_page_count < ODID_AUTH_MAX_PAGES)&&(i < len); ++auth_page_count) {
auth_data[auth_page_count]->AuthType = (ODID_authtype_t) type;
for (j = 0; (j < 23)&&(i < len); ++i, ++j) {
check[j] =
auth_data[auth_page_count]->AuthData[j] = auth[i];
}
if (j < 23) {
auth_data[auth_page_count]->AuthData[j] = 0;
}
check[j] = 0;
if (Debug_Serial) {
sprintf(text,"Page %d \'%s\'\r\n",auth_page_count,check);
Debug_Serial->print(text);
}
}
len = i;
}
auth_data[0]->LastPageIndex = (auth_page_count) ? auth_page_count - 1: 0;
auth_data[0]->Length = len;
#if not defined(ARDUINO_ARCH_NRF52)
time_t secs;
time(&secs);
auth_data[0]->Timestamp = (uint32_t) (secs - ID_OD_AUTH_DATUM);
#else
auth_data[0]->Timestamp = 0;
#endif
if (Debug_Serial) {
sprintf(text,"%d pages\r\n",auth_page_count);
Debug_Serial->print(text);
}
return;
}
/*
*
*/
int ID_OpenDrone::transmit(struct UTM_data *utm_data) {
int i, status;
char text[128];
time_t secs = 0;
static int phase = 0;
//
i = 0;
text[0] = 0;
msecs = millis();
// For the ODID 2.0 and auth timestamps.
#if defined(ARDUINO_ARCH_NRF52) || defined(ARDUINO_ARCH_ESP8266)
secs = alt_unix_secs(utm_data->years,utm_data->months,utm_data->days,
utm_data->hours,utm_data->minutes,utm_data->seconds);
// secs = ID_OD_AUTH_DATUM;
#elif 0
struct tm clock_tm;
clock_tm.tm_year = utm_data->years - 1900;
clock_tm.tm_mon = utm_data->months - 1;
clock_tm.tm_mday = utm_data->days;
clock_tm.tm_hour = utm_data->hours;
clock_tm.tm_min = utm_data->minutes;
clock_tm.tm_sec = utm_data->seconds;
secs = mktime(&clock_tm);
#else
time(&secs);
#endif
//
if ((!system_data->OperatorLatitude)&&(utm_data->base_valid)) {
system_data->OperatorLatitude = utm_data->base_latitude;
system_data->OperatorLongitude = utm_data->base_longitude;
system_data->OperatorAltitudeGeo = utm_data->base_alt_m;
system_data->Timestamp = (uint32_t) (secs - ID_OD_AUTH_DATUM);
encodeSystemMessage(&system_enc,system_data);
}
// Periodically encode live data and advertise using Bluetooth.
if ((msecs > last_msecs)&&
((msecs - last_msecs) > 74)) {
last_msecs += 75;
switch (phase) {
case 0: case 8: case 16: case 24: case 32:
case 4: case 12: case 20: case 28: case 36: // Every 300 ms.
if (utm_data->satellites >= SATS_LEVEL_2) {
location_data->Status = ODID_STATUS_UNDECLARED;
location_data->Direction = (float) utm_data->heading;
location_data->SpeedHorizontal = 0.514444 * (float) utm_data->speed_kn;
location_data->SpeedVertical = INV_SPEED_V;
location_data->Latitude = utm_data->latitude_d;
location_data->Longitude = utm_data->longitude_d;
location_data->Height = utm_data->alt_agl_m;
location_data->AltitudeGeo = utm_data->alt_msl_m;
location_data->TimeStamp = (float) ((utm_data->minutes * 60) + utm_data->seconds) +
0.01 * (float) utm_data->csecs;
UAS_data.LocationValid = 1;
} else {
location_data->Status = ODID_STATUS_REMOTE_ID_SYSTEM_FAILURE;
}
if ((status = encodeLocationMessage(&location_enc,location_data)) == ODID_SUCCESS) {
transmit_ble((uint8_t *) &location_enc,sizeof(location_enc));
} else if (Debug_Serial) {
sprintf(text,"ID_OpenDrone::%s, encodeLocationMessage returned %d\r\n",
__func__,status);
Debug_Serial->print(text);
}
break;
case 6: case 14: case 22: case 30: case 38: // Every 600 ms.
if (secs > ID_OD_AUTH_DATUM) {
system_data->Timestamp = (uint32_t) (secs - ID_OD_AUTH_DATUM);
encodeSystemMessage(&system_enc,system_data);
}
transmit_ble((uint8_t *) &system_enc,sizeof(system_enc));
break;
case 2:
if (UAS_data.BasicID[0].IDType) {
transmit_ble((uint8_t *) &basicID_enc[0],sizeof(ODID_BasicID_encoded));
}
break;
case 10:
if (UAS_data.BasicID[1].IDType) {
transmit_ble((uint8_t *) &basicID_enc[1],sizeof(ODID_BasicID_encoded));
}
break;
case 18:
transmit_ble((uint8_t *) &selfID_enc,sizeof(selfID_enc));
break;
case 26:
transmit_ble((uint8_t *) &operatorID_enc,sizeof(operatorID_enc));
break;
case 34:
if (auth_page_count) {
// Refresh the timestamp on page 0?
encodeAuthMessage(&auth_enc,auth_data[auth_page]);
transmit_ble((uint8_t *) &auth_enc,sizeof(auth_enc));
if (++auth_page >= auth_page_count) {
auth_page = 0;
}
}
break;
default:
break;
}
if (++phase > 39) {
phase = 0;
}
}
//
#if ID_OD_WIFI
// Pack and transmit the WiFi data.
if ((msecs - last_wifi) >= beacon_interval) {
last_wifi = msecs;
if (wifi_toggle ^= 1) { // Basic IDs, operator, system and location.
UAS_data.SystemValid = 1;
if (UAS_data.BasicID[0].UASID[0]) {
UAS_data.BasicIDValid[0] = 1;
}
if (UAS_data.BasicID[1].UASID[0]) {
UAS_data.BasicIDValid[1] = 1;
}
if (UAS_data.OperatorID.OperatorId[0]) {
UAS_data.OperatorIDValid = 1;
}
status = transmit_wifi(utm_data,0);
UAS_data.BasicIDValid[0] =
UAS_data.BasicIDValid[1] =
UAS_data.LocationValid =
UAS_data.SystemValid =
UAS_data.OperatorIDValid = 0;
} else {
#if ID_NATIONAL
UAS_data.Auth[0].Timestamp = system_data->Timestamp;
// memset(UAS_data.Auth[0].AuthData,0,12);
encodeAuthMessage(&auth_enc,&UAS_data.Auth[0]);
status = transmit_wifi(utm_data,pack_encrypt_national(beacon_payload));
#else // Self ID, authentication and location.
if (UAS_data.SelfID.Desc[0]) {
UAS_data.SelfIDValid = 1;
}
for (i = 0; (i < auth_page_count)&&(i < ODID_AUTH_MAX_PAGES); ++i) {
UAS_data.AuthValid[i] = 1;
}
status = transmit_wifi(utm_data,0);
UAS_data.LocationValid =
UAS_data.SelfIDValid = 0;
for (i = 0; (i < auth_page_count)&&(i < ODID_AUTH_MAX_PAGES); ++i) {
UAS_data.AuthValid[i] = 0;
}
#endif
}
}
#endif // ID_OD_WIFI
return status;
}
/*
*
*/
int ID_OpenDrone::transmit_wifi(struct UTM_data *utm_data,int prepacked) {
#if ID_OD_WIFI
int length = 0, wifi_status = 0;
uint64_t usecs = 0;
char text[128];
text[0] = 0;
//
if (++sequence > 0xffffff) {
sequence = 1;
}
msecs = millis();
wifi_interval = msecs - last_wifi;
last_wifi = msecs;
#if not (defined(ARDUINO_ARCH_RP2040) || defined(ARDUINO_ARCH_NRF52))
struct timespec ts;
clock_gettime(CLOCK_REALTIME,&ts);
usecs = (uint64_t)((double) ts.tv_sec * 1e6 + (double) ts.tv_nsec * 1e-3);
#else
usecs = micros();
#endif
#if ID_OD_WIFI_NAN
uint8_t buffer[1024];
static uint8_t send_counter = 0;
if ((length = odid_wifi_build_nan_sync_beacon_frame((char *) WiFi_mac_addr,
buffer,sizeof(buffer))) > 0) {
wifi_status = transmit_wifi2(buffer,length);
}
if ((Debug_Serial)&&((length < 0)||(wifi_status != 0))) {
sprintf(text,"odid_wifi_build_nan_sync_beacon_frame() = %d, transmit_wifi2() = %d\r\n",
length,(int) wifi_status);
Debug_Serial->print(text);
}
if ((length = odid_wifi_build_message_pack_nan_action_frame(&UAS_data,(char *) WiFi_mac_addr,
++send_counter,
buffer,sizeof(buffer))) > 0) {
wifi_status = transmit_wifi2(buffer,length);
}
if (Debug_Serial) {
if ((length < 0)||(wifi_status != 0)) {
sprintf(text,"odid_wifi_build_message_pack_nan_action_frame() = %d, transmit_wifi2() = %d\r\n",
length,(int) wifi_status);
Debug_Serial->print(text);
#if DIAGNOSTICS
} else {
sprintf(text,"ID_OpenDrone::%s ... ",__func__);
Debug_Serial->print(text);
for (int i = 0; i < 32; ++i) {
sprintf(text,"%02x ",buffer[16 + i]);
Debug_Serial->print(text);
}
Debug_Serial->print(" ... \r\n");
#endif
}
}
#endif // NAN
#if ID_OD_WIFI_BEACON
#if USE_BEACON_FUNC
if ((length = odid_wifi_build_message_pack_beacon_frame(&UAS_data,(char *) WiFi_mac_addr,
ssid,ssid_length,
beacon_interval,++beacon_counter,
beacon_frame,BEACON_FRAME_SIZE)) > 0) {
wifi_status = transmit_wifi2(beacon_frame,length);
}
#if DIAGNOSTICS && 1
if (Debug_Serial) {
sprintf(text,"ID_OpenDrone::%s * %02x ... ",__func__,beacon_frame[0]);
Debug_Serial->print(text);
for (int i = 0; i < 20; ++i) {
sprintf(text,"%02x ",beacon_frame[22 + i]);
Debug_Serial->print(text);
}
Debug_Serial->print(" ... *\r\n");
}
#endif // DIAG
#else
int i, len2 = 0;
++*beacon_counter;
for (i = 0; i < 8; ++i) {
beacon_timestamp[i] = (usecs >> (i * 8)) & 0xff;
}
#if 1
beacon_seq[0] = (uint8_t) (sequence << 4);
beacon_seq[1] = (uint8_t) (sequence >> 4);
#endif
length = (prepacked > 0) ? prepacked:
odid_message_build_pack(&UAS_data,beacon_payload,beacon_max_packed);
if (length > 0) {
*beacon_length = length + 5;
wifi_status = transmit_wifi2(beacon_frame,len2 = beacon_offset + length);
}
#if DIAGNOSTICS && 1
if (Debug_Serial) {
sprintf(text,"ID_OpenDrone::%s %d %d+%d=%d ",
__func__,beacon_max_packed,beacon_offset,length,len2);
Debug_Serial->print(text);
sprintf(text,"* %02x ... ",beacon_frame[0]);
Debug_Serial->print(text);
for (int i = 0; i < 16; ++i) {
if ((i == 3)||(i == 10)) {
Debug_Serial->print("| ");
}
sprintf(text,"%02x ",beacon_frame[beacon_offset - 10 + i]);
Debug_Serial->print(text);
}
sprintf(text,"... %02x (%2d,%4u,%4u)\r\n",beacon_frame[len2 - 1],
wifi_status,wifi_interval,ble_interval);
Debug_Serial->print(text);
}
#endif // DIAG
#endif // FUNC
#endif // BEACON
#endif // WIFI
return 0;
}
/*
*
*/
int ID_OpenDrone::transmit_ble(uint8_t *odid_msg,int length) {
msecs = millis();
ble_interval = msecs - last_ble;
last_ble = msecs;
#if ID_OD_BT
int i, j, k, len, status;
uint8_t *a;
i = j = k = len = 0;
a = ble_message;
memset(ble_message,0,sizeof(ble_message));
//
ble_message[j++] = 0x1e;
ble_message[j++] = 0x16;
ble_message[j++] = 0xfa; // ASTM
ble_message[j++] = 0xff; //
ble_message[j++] = 0x0d;
#if 0
ble_message[j++] = ++counter;
#else
ble_message[j++] = ++msg_counter[odid_msg[0] >> 4];
#endif
for (i = 0; (i < length)&&(j < sizeof(ble_message)); ++i, ++j) {
ble_message[j] = odid_msg[i];
}
status = transmit_ble2(ble_message,len = j);
#if DIAGNOSTICS && 0
char text[64], text2[34];
static int first = 1;
if (Debug_Serial) {
if (first) {
first = 0;
Debug_Serial->print("0000000 00 ");
for (i = 0; (i < 32); ++i) {
sprintf(text,"%02d ",i);
Debug_Serial->print(text);
}
Debug_Serial->print("\r\n");
}
sprintf(text,"%7lu %02x (%2d,%2d) .. ",
millis(),len - 1,len - 1,length);
Debug_Serial->print(text);
for (i = 0; (i < len)&&(i < 32); ++i) {
sprintf(text,"%02x ",a[i]);
text2[i] = ((a[i] > 31)&&(a[i] < 127)) ? a[i]: '.';
Debug_Serial->print(text);
}
text2[i] = 0;
Debug_Serial->print(text2);
Debug_Serial->print("\r\n");
}
#endif
#endif // BT
return 0;
}
/*
*
*/