ROV CTD TimescaleDB Client
The ROV CTD data is streamed into a TimescaleDB database onboard the Rachel Carson so you can query the data from there using any standard SQL client or using code.
Message Structure
If you use the Python code in the example below you should see a set of record that look something like this:
Latest 5 rows from navproc_seabird_ctd_msg:
--------------------------------------------------------------------------------
{'id': 1640205, 'lcm_channel': 'SEABIRD_CTD_MSG', 'message_type': 'navproc/msg_t', 'pub_id': 'navproc-rcsn:seabird_ctd(3115)', 'pub_sequence': 819580, 'pub_timestamp': datetime.datetime(2025, 10, 8, 21, 47, 1, 705280, tzinfo=datetime.timezone.utc), 'in_water': False, 'temperature': 15.3889, 'conductivity': 0.09939, 'pressure': -0.582, 'analog1': 2.4587, 'analog2': 1.9701, 'analog3': 3.4485, 'analog4': 0.0006, 'sound_velocity': 1468.023}
{'id': 1640204, 'lcm_channel': 'SEABIRD_CTD_MSG', 'message_type': 'navproc/msg_t', 'pub_id': 'navproc-rcsn:seabird_ctd(3115)', 'pub_sequence': 819579, 'pub_timestamp': datetime.datetime(2025, 10, 8, 21, 47, 1, 462006, tzinfo=datetime.timezone.utc), 'in_water': False, 'temperature': 15.3882, 'conductivity': 0.10158, 'pressure': -0.58, 'analog1': 2.4584, 'analog2': 1.9642, 'analog3': 3.4486, 'analog4': 0.0002, 'sound_velocity': 1468.037}
{'id': 1640203, 'lcm_channel': 'SEABIRD_CTD_MSG', 'message_type': 'navproc/msg_t', 'pub_id': 'navproc-rcsn:seabird_ctd(3115)', 'pub_sequence': 819578, 'pub_timestamp': datetime.datetime(2025, 10, 8, 21, 47, 1, 218136, tzinfo=datetime.timezone.utc), 'in_water': False, 'temperature': 15.3886, 'conductivity': 0.1029, 'pressure': -0.57, 'analog1': 2.458, 'analog2': 1.9682, 'analog3': 3.4479, 'analog4': 0.0007, 'sound_velocity': 1468.049}
{'id': 1640202, 'lcm_channel': 'SEABIRD_CTD_MSG', 'message_type': 'navproc/msg_t', 'pub_id': 'navproc-rcsn:seabird_ctd(3115)', 'pub_sequence': 819577, 'pub_timestamp': datetime.datetime(2025, 10, 8, 21, 47, 0, 954557, tzinfo=datetime.timezone.utc), 'in_water': False, 'temperature': 15.3886, 'conductivity': 0.10368, 'pressure': -0.609, 'analog1': 2.4578, 'analog2': 1.9661, 'analog3': 3.4439, 'analog4': 0.0004, 'sound_velocity': 1468.054}
{'id': 1640201, 'lcm_channel': 'SEABIRD_CTD_MSG', 'message_type': 'navproc/msg_t', 'pub_id': 'navproc-rcsn:seabird_ctd(3115)', 'pub_sequence': 819576, 'pub_timestamp': datetime.datetime(2025, 10, 8, 21, 47, 0, 710438, tzinfo=datetime.timezone.utc), 'in_water': False, 'temperature': 15.3891, 'conductivity': 0.10526, 'pressure': -0.609, 'analog1': 2.4571, 'analog2': 1.973, 'analog3': 3.4441, 'analog4': 0.0001, 'sound_velocity': 1468.068}
Python
import psycopg2
from psycopg2 import sql
def query_timescaledb(host, database, table, user, password, port=5432):
"""
Query the latest 20 rows from TimescaleDB
"""
try:
# Connect to the database
conn = psycopg2.connect(
host=host,
database=database,
user=user,
password=password,
port=port
)
print(f"Connected to {host}:{port}/{database}")
# Create a cursor
cur = conn.cursor()
# Query for the latest 20 rows ordered by pub_timestamp
query = sql.SQL("""
SELECT * FROM {table}
ORDER BY pub_timestamp DESC
LIMIT 5
""").format(table=sql.Identifier(table))
cur.execute(query)
# Fetch the results
rows = cur.fetchall()
# Get column names
colnames = [desc[0] for desc in cur.description]
# Print results
print(f"\nLatest 5 rows from {table}:")
print("-" * 80)
for row in rows:
print(dict(zip(colnames, row)))
print() # Blank line between rows
# Close cursor and connection
cur.close()
conn.close()
return rows, colnames
except Exception as e:
print(f"Error: {e}")
return None, None
def main():
# Database connection parameters
HOST = "coredata-rcsn.rc.mbari.org"
DATABASE = "navproc"
TABLE = "navproc_seabird_ctd_msg"
USER = ""
PASSWORD = ""
PORT = 5432
query_timescaledb(HOST, DATABASE, TABLE, USER, PASSWORD, PORT)
if __name__ == "__main__":
main()