vip-aerospace / artemis-cosmos-teensy-fsw

The micro-COSMOS flight software for the Artemis Teensy 4.1.
https://hsfl.github.io/artemis-cosmos-teensy-fsw/
0 stars 0 forks source link

The Life of a Packet #1

Open montoyaoa opened 6 months ago

montoyaoa commented 6 months ago

This issue will help you become acquainted with PacketComm, and the flow of packets through the Teensy FSW. It is helpful to think in terms of packets and their flow, since the is the primary method by which different parts of the satellite talk to each other.

Presume you have a packet incoming in from the ground using the RFM23 radio. Your job is to describe in as much detail as possible, how the packet is handled through the flight software.

Obviously I won't throw you into this blind, and I want you to fully understand the software. So, I'm going to provide some questions, which should help guide your exploration of the code.

  1. Your packet is in the radio, and needs to be sent from the radio to the Teensy.
  2. How does the Teensy get data from the radio? (see rfm23_channel.cpp)
  3. In which channel does this occur? (rfm23_channel.cpp)
  4. What does this channel do once it has the packet? (rfm23_channel.cpp)
  5. Which channel is the packet routed to? How does this occur? (artemis_defs.cpp)
  6. How do we keep track of which packets belong to a certain channel? (artemis_defs.h)
  7. Now that the packet is in some other channel (the answer to question 5), what happens to it in this channel? (main.cpp)
  8. Specifically, which parts of the packet are checked in order to route it? (main.cpp)
  9. Presume this packet is destined for the Raspberry Pi. How is it routed to the appropriate channel? (artemis_defs.cpp)
  10. How does the Raspberry Pi channel send the packet to the Raspberry Pi?

This should provide a helpful start in navigating the codebase in VSCode. Here are some helpful resources:

This is a diagram of the FSW architecture. You can see, generally, how different parts of the satellite are connected via different communications protocols. The Ke Ao FSW Architecture.

VSCode is a very helpful editor. If you want to learn more about something, just hover over it and you can see more about it. This is, of course, if someone documented it before. image

Additionally, you can right-click on things to bring up a menu: image There are a few helpful choices here:

Hopefully this helps you become more familiar with the codebase! Navigating unfamiliar code with your IDE really is an extremely valuable skill as a software developer.

Diamondragon808 commented 6 months ago
  1. "Your packet is in the radio, and needs to be sent from the radio to the Teensy."
  2. In the rfm23_channel.cpp, we have the function '_receive_fromradio'. This looks like our main method for getting packets of data from our radio. At the end of this function, our packet also gets routed to the main channel.
  3. At the beginning of this process, this is all occurring in the channel of our radio (rfm23).
  4. The packet in this channel gets routed to the main channel thanks to the functions in rfm23_channel.cpp. More specifically it looks like this is thanks to '_route_packet_tomain' which was defined in artemis_defs.cpp.
  5. Based on previous questions and paths, I think that the packet will be routed to the main channel. Although, various functions in artemis_defs.cpp can also lead to packets being routed to other places such as the raspberry pi and pdu.
montoyaoa commented 6 months ago

This is a link to the Doxygen documentation: https://hsfl.github.io/artemis-cosmos-teensy-fsw/index.html

Diamondragon808 commented 6 months ago
  1. We can keep track of which packets go to which channels by looking at some of the queues of the respective channels. We have push and pull queue functions for the sake of these packets.
  2. In this part of the code (main.cpp), we have two routing functions, '_routepackets' and '_route_packet_toground'. The first function, '_routepackets', pulls the packet from the main channel queue and routes it to one of a few channels, either the rpi, pdu, or ground channel.
Diamondragon808 commented 6 months ago
  1. One thing I noted was an if statement: 'if (packet.data[1] == 0)'. This seemed to dictate where the packet could be routed to, though I need to do more investigating on the specifics of the packets data.
  2. When it is being routed to the rpi channel (in this presumption), our '_function route_packet_torpi' checks both queues and mutex in order to send the packet to the appropriate channel. Previously in person, we used the analogy of a "talking stick" in order to describe what a mutex (mutual exclusion) is.
montoyaoa commented 6 months ago
  1. The helper function receive_from_radio() receives incoming data from the radio. This helper function determines the timeout to wait for data from the radio, based on the number of packets in the RFM23 queue. It calls the RFM23::recv() function with a packet and that timeout. This function is part of the RFM23 driver software. If data has been received, recv() returns the number of bytes received. Otherwise, -1 is received. If data has been received, the raw data is written to the serial debug connection. The RFM23 channel is paused for 2 seconds, allowing other threads to execute.
  2. This occurs in the RFM23 channel, found in rfm23_channel.cpp. This channel has two main functions: setup(), and loop(). The setup() function runs first, then loop() runs in a loop forever. receive_from_radio() is called from loop().
  3. After the data has been written to a packet, the packet is routed to the main channel using the route_packet_to_main() function at the end of the receive_from_radio().
  4. The packet is routed to the main channel using the route_packet_to_main() function. This is a wrapper for the PushQueue() function. Using the PushQueue() function, the newly received packet is pushed to the end of the main_queue data structure. If the queue is full, then the frontmost packet is removed and the new packet is pushed to the end.
  5. We keep track of packets using queues, a data structure. There is one queue per channel, and that queue keeps track of packets that the channel has to process. Like a real life queue, new packets are added to the end, and packets are removed from the front. Thus, it is a First In, First Out (FIFO) data structure.
  6. The main channel, like every other channel, has a setup() and loop() function. In the loop() function, the helper function route_packets() routes packets throughout the Teensy. route_packets() pulls a packet from the main_queue, and inspects its header to determine where it should be routed.
  7. First the nodedest field of the header of the packet is inspected. This describes where the packet is headed. It can be either to the ground, the Raspberry Pi, or Teensy. If it is to be routed to the ground, it is routed to the ground by passing it to the appropriate radio's channel (the RFM23). If it is to be routed to the Raspberry Pi, make sure it is powered using the ensure_rpi_is_powered() function, then route the packet to the Raspberry Pi by pushing it to the rpi_queue using the route_packet_to_rpi() helper function. Finally, if it is to be routed to the Teensy, the routing depends on the type of packet. If the packet is a ping packet, send a pong packet in reply using the send_pong_reply() helper function. If the packet is an Electrical Power System (EPS) communications packet, route that packet to the PDU. If the packet is an EPS switch set command, check the first byte of the payload. If it is meant to command the Raspberry Pi, further inspect the payload bytes. If the second byte is 0, route the packet to the Raspberry Pi. If the third byte is 1, enable the Raspberry Pi and wait 5 seconds. Otherwise, ensure the Raspberry Pi is turned on. If the packet is meant to control another switch on the PDU, pass it along to the PDU. If the packet is a PDU switch status request, check the first byte of the payload to determine which switch is being checked. If it is the Raspberry Pi, use a custom helper function report_rpi_enabled() to send a reply packet. Otherwise, route the packet to the PDU using the route_packet_to_pdu() helper function. Finally, if the packet is a beacon command packet, beacon all the attached Artemis devices (IMU, GPS, etc.) and PDU switch status.
  8. If the packet is intended for the Raspberry P, the packet.header.nodedest field of the header would be equal to 0x03, the RPI_NODE_ID. This would trigger that section of the route_packets() helper function. The ensure_rpi_is_powered() helper function would be called next. This reads a pin (UART6_RX or pin 7 on the Teensy). If it is low, then the Raspberry Pi is disabled and next the voltage of the batteries is checked. If it is above 7V, then the Raspberry Pi is enabled using another helper function enable_rpi(). This helper function sets the RPI_ENABLE (pin 36 on Teensy) pin high, and starts an instance of the Raspberry Pi channel. The channel then waits 5 seconds to let the Raspberry Pi boot up. Otherwise, the batteries are depleted and we shouldn't start the Raspberry Pi. Instead, we just update the status of the switches on the PDU. Finally, we route the packet to the Raspberry Pi channel.
  9. The Raspberry Pi channel, like every other channel, has a setup() and loop() function. In the loop function, the helper function handle_queue() checks the rpi_queue for packets destined for the Raspberry Pi. The header of the packet is inspected to determine its type. if the packet is a EPS switch set command packet, and the command is to shut the Raspberry Pi down, turn off the Raspberry Pi using the shut_down_pi() helper function. Otherwise, route the packet to the Raspberry Pi using the send_to_pi() helper function. The send_to_pi)( helper function first SLIP pacetizes the packet. That is, it takes the packet header and payload, combines them into one block of data called packetized, and runs a SLIP encoding algorithm on it. This just uses special bytes to mark the start and end of transmission, and the exact details are not yet needed. After packetizing the packet, the packetized version (one big block of data) is sent, byte by byte, over the Serial2 connection on the Teensy. This is the serial connection between the Teensy and Raspberry Pi, and the packet has been written to the Raspberry Pi.