Thursday, July 9, 2026

Open source multibeam sonar processing software

I tried to do another code review for MB-System is morning. It's been many years since I dug deep into the system. Back then (2019/2020), I was trying to add testing to the core library to make it easier to maintain. I made progress, but didn't get traction from the community. The review today reminded me how much friction there is in the old code. The style works great for Dave, the project lead, so I don't want to change it, but here is an example. It's pure C.


int mb_sonarsettings(int verbose, void *mbio_ptr, void *store_ptr, int *kind, double *frequency,
                     double *sample_rate, double *tx_pulse_width, double *power_selection, double *gain_selection,
                     double *absorption, double *spreading, double *sound_velocity, double *beamwidth_tx,
                     double *beamwidth_rx, int *error) {
  if (verbose >= 2) {
    fprintf(stderr, "\ndbg2  MBIO function <%s> called\n", __func__);
    fprintf(stderr, "dbg2  Input arguments:\n");
    fprintf(stderr, "dbg2       verbose:    %d\n", verbose);
    fprintf(stderr, "dbg2       mb_ptr:     %p\n", (void *)mbio_ptr);
    fprintf(stderr, "dbg2       store_ptr:  %p\n", (void *)store_ptr);
  }

  struct mb_io_struct *mb_io_ptr = (struct mb_io_struct *)mbio_ptr;

  /* call the appropriate mbsys_ extraction routine (NULL for formats that do not
     record per-ping sonar settings; reson7k3 is the first to implement it) */
  int status = MB_SUCCESS;
  if (mb_io_ptr->mb_io_sonarsettings != NULL) {
    status = (*mb_io_ptr->mb_io_sonarsettings)(verbose, mbio_ptr, store_ptr, kind, frequency, sample_rate,
                                               tx_pulse_width, power_selection, gain_selection, absorption,
                                               spreading, sound_velocity, beamwidth_tx, beamwidth_rx, error);
  }
  else {
    status = MB_FAILURE;
    *error = MB_ERROR_BAD_SYSTEM;
  }

  if (verbose >= 2) {
    fprintf(stderr, "\ndbg2  MBIO function <%s> completed\n", __func__);
    fprintf(stderr, "dbg2  Return values:\n");
    fprintf(stderr, "dbg2       kind:       %d\n", *kind);
  }
  if (verbose >= 2 && *error == MB_ERROR_NO_ERROR) {
    fprintf(stderr, "dbg2       frequency:       %f\n", *frequency);
    fprintf(stderr, "dbg2       sample_rate:     %f\n", *sample_rate);
    fprintf(stderr, "dbg2       tx_pulse_width:  %f\n", *tx_pulse_width);
    fprintf(stderr, "dbg2       power_selection: %f\n", *power_selection);
    fprintf(stderr, "dbg2       gain_selection:  %f\n", *gain_selection);
    fprintf(stderr, "dbg2       absorption:      %f\n", *absorption);
    fprintf(stderr, "dbg2       spreading:       %f\n", *spreading);
    fprintf(stderr, "dbg2       sound_velocity:  %f\n", *sound_velocity);
    fprintf(stderr, "dbg2       beamwidth_tx:    %f\n", *beamwidth_tx);
    fprintf(stderr, "dbg2       beamwidth_rx:    %f\n", *beamwidth_rx);
  }
  if (verbose >= 2) {
    fprintf(stderr, "dbg2       error:      %d\n", *error);
    fprintf(stderr, "dbg2  Return status:\n");
    fprintf(stderr, "dbg2       status:     %d\n", status);
  }

  return (status);
}
That really boils down to this:
int mb_sonarsettings(int verbose, void *mbio_ptr, void *store_ptr, int *kind, double *frequency,
                     double *sample_rate, double *tx_pulse_width, double *power_selection, double *gain_selection,
                     double *absorption, double *spreading, double *sound_velocity, double *beamwidth_tx,
                     double *beamwidth_rx, int *error) {
  struct mb_io_struct *mb_io_ptr = (struct mb_io_struct *)mbio_ptr;

  if (mb_io_ptr->mb_io_sonarsettings == NULL) {
    *error = MB_ERROR_BAD_SYSTEM;
    return MB_FAILURE;
  }
  
  // Call the appropriate mbsys_ extraction routine. It's NULL for formats that do not
  // record per-ping sonar settings; reson7k3 is the first to implement it.
  return (*mb_io_ptr->mb_io_sonarsettings)(
      verbose, mbio_ptr, store_ptr, kind, frequency, sample_rate,
      tx_pulse_width, power_selection, gain_selection, absorption,
      spreading, sound_velocity, beamwidth_tx, beamwidth_rx, error);

}
Now imagine that spread across a huge amount of drivers. It adds up really quickly. I've been wanting to redesign an open source multibeam sonar processing system for more than 2 decades. In the beginning, I was thinking C++, but trying to avoid the crazy side of C++ and that was looking better and better as new C++ standards came out that smoothed over a lot of the rough edges that were in old C++.

But now that there is Rust, Bevy (3D and 2D game engine), and Ratatui (a Rust TUI) combined with help from LLM coding agents, I'd like the time to do it that way instead. I don't yet have the intuition of how best to build the core IO library. And it will need a solid core of geospatial code that is the equivalent to substantial parts of PROJ, GEOS, and GDAL. I think we as a community have the potential for creating something that scales for next couple of decades and honors the accomplishments of MB-System and GMT over the last 33 years.


No comments:

Post a Comment