Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions blast_config/command_list.c
Original file line number Diff line number Diff line change
Expand Up @@ -254,6 +254,9 @@ struct scom scommands[xyzzy + 1] = {


/* MISC */
// acomp
{COMMAND(acomp_start_saving), "Telling ACOMP to start saving data", GR_MISC},
{COMMAND(acomp_stop_saving), "Telling ACOMP to stop saving data", GR_MISC},
// HW watchdog and in charge
{COMMAND(disallow_hw_wd), "Setting the HW watchdog to be ignored", GR_MISC | CONFIRM},
{COMMAND(allow_hw_wd), "Setting the HW watchdog to be listened to", GR_MISC | CONFIRM},
Expand Down Expand Up @@ -1550,6 +1553,12 @@ struct mcom mcommands[plugh + 2] = {
},

/* MISC */
// ACOMP
{COMMAND(acomp_save_n_seconds), "Tell ACOMP to save N seconds of data", GR_MISC, 1,
{
{"Length of timestream", 1, 1200, 'i', "NONE"}
}
},
// XY stage
{COMMAND(xy_goto), "move the X-Y translation stage to absolute position", GR_MISC, 4,
{
Expand Down
5 changes: 5 additions & 0 deletions blast_config/include/command_list.h
Original file line number Diff line number Diff line change
Expand Up @@ -219,6 +219,9 @@ enum singleCommand {
sc2_trigger_off,

/* MISC */
// acomp
acomp_start_saving,
acomp_stop_saving,
// HW watchdog
disallow_hw_wd,
allow_hw_wd,
Expand Down Expand Up @@ -465,6 +468,8 @@ enum multiCommand {
xsc_filter_matching,

/* MISC */
// acomp
acomp_save_n_seconds,
// XY stage
xy_goto,
xy_jump,
Expand Down
27 changes: 26 additions & 1 deletion mcp/commanding/commands.c
Original file line number Diff line number Diff line change
Expand Up @@ -868,7 +868,20 @@ void SingleCommand(enum singleCommand command, int scheduled)
break;

/* MISC */
// HW WD and incharge
// acomp
case acomp_start_saving:
CommandData.acomp_commands.send_trigger = 1;
CommandData.acomp_commands.trigger_value = 1;
CommandData.acomp_commands.counting = 0;
CommandData.acomp_commands.countdown = 0;
break;
case acomp_stop_saving:
CommandData.acomp_commands.send_trigger = 1;
CommandData.acomp_commands.trigger_value = 0;
CommandData.acomp_commands.counting = 0;
CommandData.acomp_commands.countdown = 0;
break;
// HW watchdog and incharge
case disallow_hw_wd:
CommandData.bypass_HW_WD = 1;
break;
Expand Down Expand Up @@ -2518,6 +2531,13 @@ void MultiCommand(enum multiCommand command, double *rvalues,
}

/* MISC */
// acomp
case acomp_save_n_seconds:
CommandData.acomp_commands.send_trigger = 1;
CommandData.acomp_commands.trigger_value = 1;
CommandData.acomp_commands.counting = 1;
CommandData.acomp_commands.countdown = ivalues[0];
break;
// XY stage
case xy_goto:
CommandData.xystage.x1 = ivalues[0];
Expand Down Expand Up @@ -2697,6 +2717,11 @@ void InitCommandData()
// HW WD BYPASS MUST BE 0 ON STARTUP
CommandData.bypass_HW_WD = 0;

CommandData.acomp_commands.countdown = 0;
CommandData.acomp_commands.send_trigger = 0;
CommandData.acomp_commands.trigger_value = 0;
CommandData.acomp_commands.counting = 0;

CommandData.sc_bools.sc1_command_bool = 1;
CommandData.sc_bools.sc2_command_bool = 1;
CommandData.sc_bools.sc1_image_bool = 1;
Expand Down
2 changes: 2 additions & 0 deletions mcp/communications/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ add_library (communications STATIC
${CMAKE_CURRENT_SOURCE_DIR}/pilot.c
${CMAKE_CURRENT_SOURCE_DIR}/highrate.c
${CMAKE_CURRENT_SOURCE_DIR}/evtm.c
${CMAKE_CURRENT_SOURCE_DIR}/analysis_comp_sharing.c
${CMAKE_CURRENT_SOURCE_DIR}/analysis_comp_trigger.c
)
define_file_basename_for_sources(communications)

Expand Down
157 changes: 157 additions & 0 deletions mcp/communications/analysis_comp_sharing.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,157 @@
/* analysis_comp_sharing.c: TIM threads and functions for sharing pointing with acomp1
*
* This software is copyright (C) 2026 University of Arizona
*
* This file is part of the BLAST flight code licensed under the GNU
* General Public License.
*
* You should have received a copy of the GNU General Public License
* along with this software; if not, write to the Free Software Foundation,
* Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
*/

#include <stdio.h>
#include <string.h>
#include <math.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netdb.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <unistd.h>
#include <pthread.h>
#include <signal.h>
#include <errno.h>

#include "socket_utils.h"
#include "analysis_comp_sharing.h"
#include "command_struct.h"
#include "tx.h"
#include "channels_tng.h"

int proceed_with_loop_trigger = 0;

/**
* @brief takes a pointer to a pointing data packet, sets up channel pointers,
* and fills the packet with the most recent data from each channel
*
* @param packet the pointer to the packet that we will fill from channels
*/
void fill_packet_from_channels(pointing_data* packet) {
static channel_t *az_Addr, *el_Addr, *lat_Addr, *lon_Addr;
static channel_t *height_Addr, *time_Addr, *time_usec_Addr;
static int first_time = 1;
if (first_time) {
first_time = 0;
az_Addr = channels_find_by_name("az");
el_Addr = channels_find_by_name("el");
lat_Addr = channels_find_by_name("lat");
lon_Addr = channels_find_by_name("lon");
height_Addr = channels_find_by_name("alt");
time_Addr = channels_find_by_name("time");
time_usec_Addr = channels_find_by_name("time_usec");
}
GET_SCALED_VALUE(az_Addr, packet->az);
GET_SCALED_VALUE(el_Addr, packet->el);
GET_SCALED_VALUE(lat_Addr, packet->latitude);
GET_SCALED_VALUE(lon_Addr, packet->longitude);
GET_VALUE(height_Addr, packet->height);
GET_VALUE(time_Addr, packet->time);
GET_VALUE(time_usec_Addr, packet->time_usec);
}

/**
* @brief clears the data stored in a pointing packet to ensure old data never leaks in
*
* @param packet the pointer to the packet that we will clear
*/
void clear_packet_data(pointing_data* packet) {
memset(packet, 0, sizeof(pointing_data));
}

/**
* @brief triggers the acomp send loop to prepare and
* send a packet of pointing data to the acomp
* has in charge protection to ensure that only the in charge
* computer is sending the data to the acomp
*
*/
void get_acomp_shared_data(void) {
if (InCharge) {
proceed_with_loop_trigger = 1;
}
}

// here goes the actual thread that we call
void * send_pointing_data_acomp(void* args) {
pointing_data acomp_packet;
struct socketData * socket_target = args;
int first_time = 1;
int sockfd;
struct addrinfo hints;
struct addrinfo *servinfo;
struct addrinfo *servinfoCheck;
int returnval;
int bytes_sent;
int length;
int *retval;
char message_str[40];
char ipAddr[INET_ADDRSTRLEN];
while (1) {
if (first_time == 1) {
first_time = 0;
memset(&hints, 0, sizeof(hints));
hints.ai_family = AF_INET; // set to AF_INET to use IPv4
hints.ai_socktype = SOCK_DGRAM;
// fill out address info and return if it fails.
if ((returnval = getaddrinfo(socket_target->ipAddr, socket_target->port, &hints, &servinfo)) != 0) {
blast_err("getaddrinfo: %s\n", gai_strerror(returnval));
return NULL;
}
// now we make a socket with this info
for (servinfoCheck = servinfo; servinfoCheck != NULL; servinfoCheck = servinfoCheck->ai_next) {
if ((sockfd = socket(servinfoCheck->ai_family,
servinfoCheck->ai_socktype, servinfoCheck->ai_protocol)) == -1) {
perror("talker: socket");
continue;
}
break;
}
// check to see if we made a socket
if (servinfoCheck == NULL) {
// set status to 0 (dead) if this fails
blast_err("talker: failed to create socket\n");
return NULL;
}
// if we pass all of these checks then
// we set up the print statement vars
// need to cast the socket address to an INET still address
struct sockaddr_in *ipv = (struct sockaddr_in *)servinfo->ai_addr;
// then pass the pointer to translation and put it in a string
inet_ntop(AF_INET, &(ipv->sin_addr), ipAddr, INET_ADDRSTRLEN);
blast_info("IP target is: %s\n", ipAddr);
// now the "str" is packed with the IP address string
// first time setup of the socket is done
}
while (!proceed_with_loop_trigger) {
usleep(500);
}
fill_packet_from_channels(&acomp_packet);
if (!strcmp(socket_target->ipAddr, ipAddr)) {
length = sizeof(acomp_packet);
if ((bytes_sent = sendto(sockfd, &acomp_packet, length, 0,
servinfo->ai_addr, servinfo->ai_addrlen)) == -1) {
perror("talker: sendto");
}
} else {
blast_err("Target destination %s differs from thread target %s.\n", socket_target->ipAddr, ipAddr);
}
proceed_with_loop_trigger = 0;
}
freeaddrinfo(servinfo);
close(sockfd);
return NULL;
};

122 changes: 122 additions & 0 deletions mcp/communications/analysis_comp_trigger.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
/* analysis_comp_trigger.c: TIM threads and functions for triggering data save on acomp1
*
* This software is copyright (C) 2026 University of Arizona
*
* This file is part of the BLAST flight code licensed under the GNU
* General Public License.
*
* You should have received a copy of the GNU General Public License
* along with this software; if not, write to the Free Software Foundation,
* Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
*/

#include <stdio.h>
#include <string.h>
#include <math.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netdb.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <unistd.h>
#include <pthread.h>
#include <signal.h>
#include <errno.h>

#include "socket_utils.h"
#include "analysis_comp_trigger.h"
#include "command_struct.h"
#include "tx.h"
#include "channels_tng.h"

/**
* @brief function that goes in the 1hz loop to perform a delayed stop taking data countdown.
*
*/
void acomp_countdown_1hz(void) {
if (CommandData.acomp_commands.counting == 1) {
if (CommandData.acomp_commands.countdown == 0) {
Comment thread
ianlowe13 marked this conversation as resolved.
CommandData.acomp_commands.counting = 0;
CommandData.acomp_commands.send_trigger = 1;
CommandData.acomp_commands.trigger_value = 0;
return;
}
CommandData.acomp_commands.countdown--;
}
}



// here goes the actual thread that we call
void * send_data_trigger_acomp(void* args) {
struct trigger acomp_trigger;
struct socketData * socket_target = args;
int first_time = 1;
int sockfd;
struct addrinfo hints;
struct addrinfo *servinfo;
struct addrinfo *servinfoCheck;
int returnval;
int bytes_sent;
int length;
int *retval;
char message_str[40];
char ipAddr[INET_ADDRSTRLEN];
while (1) {
if (first_time == 1) {
first_time = 0;
memset(&hints, 0, sizeof(hints));
hints.ai_family = AF_INET; // set to AF_INET to use IPv4
hints.ai_socktype = SOCK_DGRAM;
// fill out address info and return if it fails.
if ((returnval = getaddrinfo(socket_target->ipAddr, socket_target->port, &hints, &servinfo)) != 0) {
blast_err("getaddrinfo: %s\n", gai_strerror(returnval));
return NULL;
}
// now we make a socket with this info
for (servinfoCheck = servinfo; servinfoCheck != NULL; servinfoCheck = servinfoCheck->ai_next) {
if ((sockfd = socket(servinfoCheck->ai_family,
servinfoCheck->ai_socktype, servinfoCheck->ai_protocol)) == -1) {
perror("talker: socket");
continue;
}
break;
}
// check to see if we made a socket
if (servinfoCheck == NULL) {
// set status to 0 (dead) if this fails
blast_err("talker: failed to create socket\n");
return NULL;
}
// if we pass all of these checks then
// we set up the print statement vars
// need to cast the socket address to an INET still address
struct sockaddr_in *ipv = (struct sockaddr_in *)servinfo->ai_addr;
// then pass the pointer to translation and put it in a string
inet_ntop(AF_INET, &(ipv->sin_addr), ipAddr, INET_ADDRSTRLEN);
blast_info("IP target is: %s\n", ipAddr);
// now the "str" is packed with the IP address string
// first time setup of the socket is done
}
if ((CommandData.acomp_commands.send_trigger == 1) && InCharge) {
acomp_trigger.take_data = CommandData.acomp_commands.trigger_value; // pull the value into the packet
CommandData.acomp_commands.send_trigger = 0; // don't try to send again
if (!strcmp(socket_target->ipAddr, ipAddr)) {
length = sizeof(acomp_trigger);
if ((bytes_sent = sendto(sockfd, &acomp_trigger, length, 0,
servinfo->ai_addr, servinfo->ai_addrlen)) == -1) {
perror("talker: sendto");
}
} else {
blast_err("Target destination %s differs from thread target %s.\n", socket_target->ipAddr, ipAddr);
}
} else {
usleep(100000);
}
}
freeaddrinfo(servinfo);
close(sockfd);
return NULL;
};
Loading
Loading