Skip to content

Commit

Permalink
Added NMEA-0183 network listener.
Browse files Browse the repository at this point in the history
Socket receiving NMEA messages.

First basic working version of NMEA listener.

Removed latitude and longitude parsing from header.

Status bar turns red when no nav received.

Replaced GUI name NMEA with NMEA-0183 to be more precise.

Source of position information (file, listener or manual) is now clearer.

Added callback methods for nmea listener.
  • Loading branch information
eriffon committed Sep 26, 2023
1 parent 4782803 commit df45912
Show file tree
Hide file tree
Showing 16 changed files with 473 additions and 58 deletions.
7 changes: 7 additions & 0 deletions hyo2/soundspeed/base/callbacks/abstract_callbacks.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@

if TYPE_CHECKING:
from hyo2.soundspeed.listener.sis.sis import Sis
from hyo2.soundspeed.listener.nmea.nmea import Nmea


logger = logging.getLogger(__name__)
Expand All @@ -15,6 +16,7 @@ class GeneralAbstractCallbacks(metaclass=ABCMeta):

def __init__(self) -> None:
self.sis_listener = None # type: Optional[Sis]
self.nmea_listener = None # type: Optional[Nmea]

@abstractmethod
def ask_number(self, title: Optional[str] = "", msg: Optional[str] = "Enter number", default: Optional[float] = 0.0,
Expand Down Expand Up @@ -73,6 +75,11 @@ def ask_location_from_sis(self) -> bool:
"""Ask user for location"""
pass

@abstractmethod
def ask_location_from_nmea(self) -> bool:
"""Ask user for location"""
pass

@abstractmethod
def ask_tss(self) -> Optional[float]:
"""Ask user for transducer sound speed"""
Expand Down
11 changes: 11 additions & 0 deletions hyo2/soundspeed/base/callbacks/cli_callbacks.py
Original file line number Diff line number Diff line change
Expand Up @@ -170,6 +170,17 @@ def ask_location_from_sis(self) -> bool:
return True
return False

def ask_location_from_nmea(self) -> bool:
"""Ask user whether retrieving location from NMEA-0183"""
bool_msg = "Geographic location required for pressure/depth conversion and atlas lookup.\n" \
"Use geographic position from NMEA-0183?\'y' for yes, other inputs to enter position manually."

raw = input(bool_msg)
# print(raw)
if (raw == "Y") or (raw == "y"):
return True
return False

def ask_tss(self) -> Optional[float]:
"""Ask user for transducer sound speed"""
tss = 1500.0
Expand Down
3 changes: 3 additions & 0 deletions hyo2/soundspeed/base/callbacks/fake_callbacks.py
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,9 @@ def ask_directory(self, key_name: Optional[str] = None, default_path: Optional[s
def ask_location_from_sis(self) -> bool:
return True

def ask_location_from_nmea(self) -> bool:
return True

def ask_tss(self) -> Optional[float]:
return 1500.0

Expand Down
18 changes: 18 additions & 0 deletions hyo2/soundspeed/base/setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@ def __init__(self, release_folder, use_setup_name=None):
self.use_sis4 = None
self.use_sis5 = None
self.use_sippican = None
self.use_nmea = None
self.use_mvp = None

# output
Expand All @@ -78,6 +79,9 @@ def __init__(self, release_folder, use_setup_name=None):
# listeners - sippican
self.sippican_listen_port = None
self.sippican_listen_timeout = None
# listeners - nmea
self.nmea_listen_port = None
self.nmea_listen_timeout = None
# listeners - mvp
self.mvp_ip_address = None
self.mvp_listen_port = None
Expand Down Expand Up @@ -158,6 +162,7 @@ def load_from_db(self, db_path: Optional[str] = None):
self.use_sis4 = db.use_sis4
self.use_sis5 = db.use_sis5
self.use_sippican = db.use_sippican
self.use_nmea = db.use_nmea
self.use_mvp = db.use_mvp

# output
Expand All @@ -178,6 +183,10 @@ def load_from_db(self, db_path: Optional[str] = None):
self.sippican_listen_port = db.sippican_listen_port
self.sippican_listen_timeout = db.sippican_listen_timeout

# listeners - nmea
self.nmea_listen_port = db.nmea_listen_port
self.nmea_listen_timeout = db.nmea_listen_timeout

# listeners - mvp
self.mvp_ip_address = db.mvp_ip_address
self.mvp_listen_port = db.mvp_listen_port
Expand Down Expand Up @@ -243,6 +252,7 @@ def save_to_db(self):
db.use_sis4 = self.use_sis4
db.use_sis5 = self.use_sis5
db.use_sippican = self.use_sippican
db.use_nmea = self.use_nmea
db.use_mvp = self.use_mvp

# client list
Expand All @@ -264,6 +274,10 @@ def save_to_db(self):
db.sippican_listen_port = self.sippican_listen_port
db.sippican_listen_timeout = self.sippican_listen_timeout

# listeners - nmea
db.nmea_listen_port = self.nmea_listen_port
db.nmea_listen_timeout = self.nmea_listen_timeout

# listeners - mvp
db.mvp_ip_address = self.mvp_ip_address
db.mvp_listen_port = self.mvp_listen_port
Expand Down Expand Up @@ -322,6 +336,7 @@ def __repr__(self):
msg += " <use_sis4: %s>\n" % self.use_sis4
msg += " <use_sis5: %s>\n" % self.use_sis5
msg += " <use_sippican: %s>\n" % self.use_sippican
msg += " <use_nmea: %s>\n" % self.use_nmea
msg += " <use_mvp: %s>\n" % self.use_mvp
msg += " <output>\n"
msg += " <clients>\n"
Expand All @@ -334,6 +349,9 @@ def __repr__(self):
msg += " <listeners - sippican>\n"
msg += " <sippican_listen_port: %s>\n" % self.sippican_listen_port
msg += " <sippican_listen_timeout: %s>\n" % self.sippican_listen_timeout
msg += " <listeners - nmea>\n"
msg += " <nmea_listen_port: %s>\n" % self.nmea_listen_port
msg += " <nmea_listen_timeout: %s>\n" % self.nmea_listen_timeout
msg += " <listeners - mvp>\n"
msg += " <mvp_ip_address: %s>\n" % self.mvp_ip_address
msg += " <mvp_listen_port: %s>\n" % self.mvp_listen_port
Expand Down
27 changes: 27 additions & 0 deletions hyo2/soundspeed/base/setup_db.py
Original file line number Diff line number Diff line change
Expand Up @@ -464,6 +464,15 @@ def use_sippican(self):
def use_sippican(self, value):
self._setter_bool("use_sippican", value)

# --- use_nmea
@property
def use_nmea(self):
return self._getter_bool("use_nmea")

@use_nmea.setter
def use_nmea(self, value):
self._setter_bool("use_nmea", value)

# --- use_mvp
@property
def use_mvp(self):
Expand Down Expand Up @@ -527,6 +536,24 @@ def sippican_listen_timeout(self):
def sippican_listen_timeout(self, value):
self._setter_int("sippican_listen_timeout", value)

# --- nmea_listen_port
@property
def nmea_listen_port(self):
return self._getter_int("nmea_listen_port")

@nmea_listen_port.setter
def nmea_listen_port(self, value):
self._setter_int("nmea_listen_port", value)

# --- nmea_listen_timeout
@property
def nmea_listen_timeout(self):
return self._getter_int("nmea_listen_timeout")

@nmea_listen_timeout.setter
def nmea_listen_timeout(self, value):
self._setter_int("nmea_listen_timeout", value)

# --- mvp_ip_address
@property
def mvp_ip_address(self):
Expand Down
24 changes: 17 additions & 7 deletions hyo2/soundspeed/base/setup_sql.py
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,7 @@
use_sis integer NOT NULL DEFAULT 1,
use_sis5 integer NOT NULL DEFAULT 0,
use_sippican integer NOT NULL DEFAULT 0,
use_nmea integer NOT NULL DEFAULT 0,
use_mvp integer NOT NULL DEFAULT 0,
/* listeners - sis4 */
sis_listen_port integer NOT NULL DEFAULT 16103,
Expand All @@ -90,6 +91,9 @@
/* listeners - sippican */
sippican_listen_port integer NOT NULL DEFAULT 2002,
sippican_listen_timeout integer NOT NULL DEFAULT 10,
/* listeners - nmea */
nmea_listen_port integer NOT NULL DEFAULT 2006,
nmea_listen_timeout integer NOT NULL DEFAULT 10,
/* listeners - mvp */
mvp_ip_address text NOT NULL DEFAULT "127.0.0.1",
mvp_listen_port integer NOT NULL DEFAULT 2006,
Expand Down Expand Up @@ -173,13 +177,13 @@
INSERT INTO general
(id, setup_name, setup_status, use_woa09, use_woa13, use_woa18, use_rtofs,
ssp_extension_source, ssp_salinity_source, ssp_temp_sal_source, ssp_up_or_down, rx_max_wait_time,
use_sis, use_sippican, use_mvp, sis_listen_port, sis_listen_timeout,
sis_auto_apply_manual_casts, sippican_listen_port, sippican_listen_timeout, mvp_ip_address,
mvp_listen_port, mvp_listen_timeout, mvp_transmission_protocol, mvp_format, mvp_winch_port,
mvp_fish_port, mvp_nav_port, mvp_system_port, mvp_sw_version, mvp_instrument_id, mvp_instrument,
server_source, server_apply_surface_sound_speed, current_project, custom_projects_folder,
custom_outputs_folder, custom_woa09_folder, custom_woa13_folder, noaa_tools, default_institution,
default_survey, default_vessel, auto_apply_default_metadata)
use_sis, use_sippican, use_nmea, use_mvp, sis_listen_port, sis_listen_timeout,
sis_auto_apply_manual_casts, sippican_listen_port, sippican_listen_timeout, nmea_listen_port,
nmea_listen_timeout, mvp_ip_address, mvp_listen_port, mvp_listen_timeout, mvp_transmission_protocol,
mvp_format, mvp_winch_port, mvp_fish_port, mvp_nav_port, mvp_system_port, mvp_sw_version,
mvp_instrument_id, mvp_instrument, server_source, server_apply_surface_sound_speed, current_project,
custom_projects_folder, custom_outputs_folder, custom_woa09_folder, custom_woa13_folder, noaa_tools,
default_institution, default_survey, default_vessel, auto_apply_default_metadata)
SELECT
id, setup_name, setup_status,
CASE WHEN typeof(use_woa09) == 'text' THEN
Expand Down Expand Up @@ -213,6 +217,11 @@
ELSE
use_sippican
END,
CASE WHEN typeof(use_nmea) == 'text' THEN
use_nmea == 'True'
ELSE
use_nmea
END,
CASE WHEN typeof(use_mvp) == 'text' THEN
use_mvp == 'True'
ELSE
Expand All @@ -225,6 +234,7 @@
sis_auto_apply_manual_casts
END,
sippican_listen_port, sippican_listen_timeout,
nmea_listen_port, nmea_listen_timeout,
mvp_ip_address, mvp_listen_port, mvp_listen_timeout, mvp_transmission_protocol, mvp_format, mvp_winch_port,
mvp_fish_port, mvp_nav_port, mvp_system_port, mvp_sw_version, mvp_instrument_id, mvp_instrument,
server_source,
Expand Down
80 changes: 80 additions & 0 deletions hyo2/soundspeed/formats/nmea0183.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
import logging

logger = logging.getLogger(__name__)


class Nmea0183Nav:

def __init__(self, data):

self.data = data
#print("data: {}".format(self.data))
self.msg = None

self.latitude = None
self.longitude = None

self.parse()

def parse(self) -> None:
self.msg = self.data.split(',')



class Nmea0183GGA(Nmea0183Nav):
def __init__(self, data):
super(Nmea0183GGA, self).__init__(data)

self.timestamp = self.msg[1]
self.lat = self.msg[2]
self.lat_dir = self.msg[3]
self.lon = self.msg[4]
self.lon_dir = self.msg[5]
self.gps_qual = self.msg[6]
self.num_sats = self.msg[7]
self.horizontal_dil = self.msg[8]
self.altitude = self.msg[9]
self.altitude_units = self.msg[10]
self.geo_sep = self.msg[11]
self.geo_sep_units = self.msg[12]
self.age_gps_data = self.msg[13]
self.ref_station_id = self.msg[14]

self.latitude = int(self.lat[:2]) + float(self.lat[2:])/60.
if self.lat_dir == 'S':
self.latitude = -1.0 * self.latitude
#logger.debug("Latitude is: {}".format(self.latitude))

# TODO: self.longitude = some calculation based on self.lon and self.lon_dir
self.longitude = int(self.lon[:3]) + float(self.lon[3:])/60.
if self.lon_dir == 'W':
self.longitude = -1.0 * self.longitude
#logger.debug("Longitude is: {}".format(self.longitude))

def __str__(self):
# TODO: for the string representation
pass



class Nmea0183GGL(Nmea0183Nav):
def __init__(self, data):
super(Nmea0183GGL, self).__init__(data)

self.msg = self.data.split(',')

self.lat = self.msg[1]
self.lat_dir = self.msg[2]
self.lon = self.msg[3]
self.lon_dir = self.msg[4]
self.timestamp = self.msg[5]
self.status = self.msg[6]

# TODO: self.latitude = some calculation based on self.lat and self.lat_dir

# TODO: self.longitude = some calculation based on self.lon and self.lon_dir


def __str__(self):
# TODO: for the string representation
pass
35 changes: 1 addition & 34 deletions hyo2/soundspeed/formats/readers/sea_and_sun.py
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ def read(self, data_path, settings, callbacks=CliCallbacks(), progress=None):
return True

def _parse_header(self):
"""Parsing header: field header, filename, time, latitude, longitude, probe type, serial number"""
"""Parsing header: field header, filename, time, probe type, serial number"""
logger.debug('parsing header')

# control flags
Expand Down Expand Up @@ -94,39 +94,6 @@ def _parse_header(self):
logger.info("Fail to interpret date from German: %s" % e)
date_string = None

elif line_idx == 9: # latitude and longitude
try:
lat_str = line[21:34]
lat_tokens = lat_str.split()
# logger.debug("lat: %s" % (lat_tokens,))
lat_deg = int(lat_tokens[0].split('\xb0')[0])
lat_min = float(lat_tokens[1].split('\'')[0])
if lat_tokens[2] == "S":
lat_sign = -1.0
else:
lat_sign = 1.0

self.ssp.cur.meta.latitude = lat_sign * (lat_deg + lat_min / 60.0)

except Exception as e:
logger.warning("unable to retrieve latitude from %s, %s" % (line, e))

try:
lon_str = line[47:60]
lon_tokens = lon_str.split()
# logger.debug("lon: %s" % (lon_tokens,))
lon_deg = int(lon_tokens[0].split('\xb0')[0])
lon_min = float(lon_tokens[1].split('\'')[0])
if lon_tokens[2] == "W":
lon_sign = -1.0
else:
lon_sign = 1.0

self.ssp.cur.meta.longitude = lon_sign * (lon_deg + lon_min / 60.0)

except Exception as e:
logger.warning("unable to retrieve longitude from %s, %s" % (line, e))

elif line_idx == 17: # probe type and serial number
if line[:len(self.tk_serial)] == self.tk_serial:
try:
Expand Down
Loading

0 comments on commit df45912

Please sign in to comment.