Showing posts with label programming. Show all posts
Showing posts with label programming. Show all posts

Monday, July 20, 2026

VSCode: Workaround to sign in to Windsurf Plugin (formerly Codeium) version 1.48.2


 

If you installed the Windsurf Plugin in Visual Studio Code recently and tried to login to the AI code completion service, it would redirect to a web site with no apparent means to authenticate the plugin. I found a workaround to generate the authentication token for VSCode from the Windsurf Neocodeium Github at https://github.com/monkoose/neocodeium.

 The following steps are what I did to authenticate the plugin:

  1.  In VSCode, install the Windsurf Plugin and tap the Login to Windsurf button.

    The prompt may appear


     
  2. Tap Open and the browser will redirect to the Windsurf web site.

  3. If necessary, log into your account.

  4. In the browser, enter the following URL https://windsurf.com/enterprise/account/login?redirect_uri=vim-show-auth-token

    The authentication token is generated.


  5. Copy the token into the clipboard.

  6. In VSCode, press CTRL-SHIFT-p to open the command prompt.



  7. In the prompt, type in Windsurf provide authentication token and press ENTER.

    The command prompt appears


  8. Paste in the generated token into the prompt. Press ENTER.

    If all goes well, the message Successfully logged in appears.


  9. You should now be able to use the AI code completion in VSCode.

 

Friday, March 13, 2026

Quakemon - Monitor Earthquakes Android App

 


Monitor earthquakes, volcano eruptions across countries with Quakemon.


Stay informed with up-to-date incidents and live camera views, all presented through an intuitive map and list interface designed for quick situational awareness.

Key Features:

1. Multi-Countries Earthquakes Monitoring
View earthquake incidents across supported countries using an interactive map or a clear, scrollable list.

2. Detailed Incident Information
Tap any incident in the list or on the map to view detailed information, including location and incident magnitude.

3. Live Volcano Cameras
Tap a camera marker to instantly view a live, zoomable volcano image for real-time conditions.

4. Powerful Filtering
Filter incidents by tags to hide unwanted information and focus only on what matters to you.

5. Camera Favorites
Save frequently used cameras to a favourites list and reorder them for quick and easy access.

Quakemon makes it easy to monitor earthquake events efficiently and stay informed.

Data Sources

Earthquakes and environmental data used in this app may be provided by:

1. Earth Sciences New Zealand (https://earthscience.nz)
2. Geoscience Australia (https://earthquakes.ga.gov.au)
3. GEOFON program of the GFZ Helmholtz Centre for Geosciences Germany(https://geofon.gfz.de)
4. Institute of Geodynamics - National Observatory of Athens (NOA-IG) Greece (https://bbnet.gein.noa.gr)
5. Istituto Nazionale di Geofisica e Vulcanologia Italy (https://terremoti.ingv.it)
6. Kandilli Observatory and Earthquake Research Institute (KOERI) and Regional Earthquake-Tsunami Monitoring Center (RETMC) Turkey (http://www.koeri.boun.edu.tr)
7. BMKG (Badan Meteorologi, Klimatologi, dan Geofisika) Indonesia (https://data.bmkg.go.id)
8. Natural Resources Canada (https://www.earthquakescanada.nrcan.gc.ca)
9. British Geological Survey (UK) (https://earthquakes.bgs.ac.uk)
10. European-Mediterranean Seismological Centre (EMSC) (https://www.emsc-csem.org)
11. USGS (https://www.usgs.gov)

 Download and try out the Quakemon app from the Google Play Store:

Get it on Google Play 

Wednesday, March 19, 2025

Take Geospatial files on the go with the Shapefiler App

 Easily load and visualize multiple GeoJSON and Shapefiles with this powerful mapping tool. The app automatically assigns overlay colors, but you have full control over the styling—customize icons, colors, and opacity through the layer properties menu. Tap on polygons, lines, and markers to view detailed feature attributes. Quickly find specific locations with the built-in free text search, making navigation effortless. Whether you're a GIS professional or a mapping enthusiast, this app provides an intuitive way to explore spatial data on your device. 


Key features

Load multiple Geospatial files: Overlay GeoJSON and Shapefiles over a choice of base map tiles.

Integrated tabular and map views: Tap on a data table record to locate the corresponding feature on the map

Multiple coordinate reference systems: Source Geospatial GeoJSON and Shapefiles in various coordinate reference systems can be reprojected to the standard World Mercator projection for display.

Free text search: Easily search features by text and locate on the map. 

For more detailed information, visit https://dominoc925.blogspot.com/p/shapefiler-help.html

Download and try out the Shapefiler app from the following stores:

Get it on Google Play

Monday, September 30, 2024

Batch Convert Coordinates using the Koordinat2 app

 

A batch coordinates conversion tool has been added to the Koordinat2 web app (https://dominoc925-pages.appspot.com/webapp/koordinat2/default.html) to perform bulk coordinates transformation between coordinate systems. 

This post shows how to use the Batch Coordinates Converter.

Launch the Batch Coordinates Converter

  1. Use a browser to open the Koordinat2 web app url (https://dominoc925-pages.appspot.com/webapp/koordinat2/default.html).




  2. Tap the hamburger icon on the top left.

    The Navigation drawer appears.



  3. Tap the Batch Coordinates Converter list item.

    The Batch Coordinates Converter screen appears.


Define the Primary Coordinate System

  1. In the Primary Coordinates card, click the gear icon.

    The Primary coordinates selection appears.

  2. Select a coordinate system from the list or search for it by clicking the Search icon and typing in the name of the coordinate system e.g. MGRS.

    MGRS is selected as the primary coordinate system.

Define the Secondary Coordinate System

  1. In the Secondary Coordinates card, click the gear icon.

    The Secondary Coordinates screen appears.


  2. Select a coordinate system from the list or search for it by clicking the Search icon and typing in the name of the coordinate system e.g. WGS 84.

    WGS 84 geographic is selected as the secondary coordinate system.

Enter the coordinates and perform the conversion

  1. In the Primary Coordinates text field, type or paste in the coordinates to convert.



  2. Click the right arrow to convert from primary to secondary coordinate system.

    The coordinates are converted.

Monday, September 23, 2024

Convert East North Up (ENU) to/from longitude,latitude and other various coordinate systems

 

The Koordinat2 web app (https://dominoc925-pages.appspot.com/webapp/koordinat2/default.html) has been upgraded to perform coordinate transformation between local coordinate system East North Up (ENU) and many other coordinate systems including MGRS, North East Down, ECEF, geographic longitude/latitude, etc. 

This post shows the typical steps to perform the local ENU conversion to other coordinate systems.

Define the ENU local origin 

The local origin should be as near as possible to the area of interest. If it is too far away, the calculation rounding errors would result in erroneous converted coordinates.

  1. Use a browser to open the Koordinat2 web app url (https://dominoc925-pages.appspot.com/webapp/koordinat2/default.html).

    The Koordinat2 web app home page appears.


  2. Click the hamburger menu icon on the top left.

    The Navigation drawer opens.



  3. Click the Settings item.

    The Settings dialog appears.




  4. Choose the item North East Down/East North Up reference origin.

    The NED/ENU Reference Origin appears.



  5. In the dialog, enter the reference origin Longitude, Latitude, and Altitude. Click Save.

    The Reference Origin is saved.

Define ENU as the Primary coordinates (input)

  1. In the Primary coordinates card, click the gear icon.

    The Primary coordinates selection appears.




  2. Select East North Up from the list or search for it by clicking the Search icon and typing in ENU.



Define the Secondary coordinates (output)

  1. In the Secondary coordinates card, click the gear icon.

    The Secondary coordinates selection appears.



  2. Select a desired coordinate system e.g. MGRS from the list or search for it by clicking the Search icon and typing in  MGRS.



Enter ENU coordinates

  1. In the Primary coordinates card, type in the East, North and Up values.

    The ENU values are converted to the secondary coordinates. And a marker appears on the map.






Tuesday, August 13, 2024

Trainsity Metro App


 

Navigate public train networks effortlessly with the Trainsity Metro app's high-resolution vector maps. Choose and download the maps you need, enjoying detailed zoom levels without sacrificing storage space. Access all features offline, without needing an internet connection.

Key Features:

High-Resolution Vector Maps: Crisp, clear maps with multiple zoom levels for detailed viewing.
 

Offline Functionality: Download maps and use them anytime, anywhere, without an internet connection.
 

Interactive Stations: Tap on a train station to open Google Maps, view the surrounding area, and get precise routing directions.


Optimized Travel Planning: Use the offline function to calculate the best routes between stations, complete with step-by-step directions and estimated timings. 

Train timetable: View static train line schedules, frequencies, and the first/last departures at individual stations. 

Note: Actual travel times may vary.

Discover a seamless way to explore train networks with our intuitive and efficient app. 

Available maps:

  1. Singapore
  2. Kuala Lumpur, Malaysia
  3. Sapporo, Japan
  4. Recife, Brazil
  5. Brasilia, Brazil
  6. Vancouver, Canada
  7. Hong Kong, China
  8. Bangkok, Thailand
  9. Manila, Philippines
  10. Osaka, Japan
  11. Amsterdam, Netherlands
  12. Hanoi, Vietnam  
  13. Utrecht, Netherlands
  14. Jakarta, Indonesia 
  15. Rio de Janeiro, Brazil
  16. Palembang, Indonesia
  17. Edmonton, Canada
  18. Calgary, Canada
  19. Sydney, Australia
  20. Fukuoka, Japan
  21. Paris, France
  22. Fortaleza, Brazil
  23. Montreal, Canada
  24. Rome, Italy
  25. Toronto, Canada
  26. Lyon, France
  27. Algiers, Algeria  
  28. São Paulo, Brazil
  29. Perth, Australia
  30. Ho Chi Minh City, Vietnam
  31. Milan, Italy
  32. Nuremberg, Germany 
  33. Copenhagen, Denmark
  34. Sobral, Brazil
  35. Natal, Brazil  
  36. Doha, Qatar
  37. Kaohsiung, Taiwan
  38. Daegu, South Korea 
  39. Busan, South Korea 
  40. Cariri, Brazil 
  41. Taipei, Taiwan 

 Download and try out the Trainsity Metro app from the following stores:

 

Get it on Google Play 

Monday, March 25, 2024

Convert Earth Centered Earth Fixed to/from other coordinate systems with this web app Koordinat2

According to Wikipedia https://en.wikipedia.org/wiki/Earth-centered%2C_Earth-fixed_coordinate_system: in today's modern age of navigation and spatial referencing, there's a key player that often goes unnoticed: the Earth-centered, Earth-fixed coordinate system, more commonly known as ECEF. This sophisticated system, akin to a cosmic GPS, allows us to pinpoint locations with incredible precision. Picture this: a cartesian grid that not only encompasses the Earth's surface but also delves deep into its core, reaches high into the atmosphere, and extends into the surrounding outer space. Through X, Y, and Z measurements from the Earth's center of mass, ECEF opens up a world of possibilities for mapping and exploration.

The WebApp Koordinat2 here on https://dominoc925-pages.appspot.com/webapp/koordinat2/default.html has been enhanced to include conversions between ECEF and various other geo-coordinate systems.  

The following shows how to use convert ECEF to/fro longitude, latitude, altitude WGS84.

Define ECEF as the Primary coordinates (input)

  1. Open a browser to the Koordinat2 web app url https://dominoc925-pages.appspot.com/webapp/koordinat2/default.html.

    The Koordinat2 web app home page is displayed.


  2. In the Primary coordinates input card, click the gear icon.

    The Primary coordinates selection dialog box appears with a list of favorite coordinate systems.



  3. Click the ECEF item from the favorite list, or search for it by clicking the Search icon and typing in ECEF.





Define WGS 84 as the Secondary coordinates (output)

  1. In the Secondary coordinates card, click the gear icon.

    The Secondary coordinates favorites list is displayed.


  2. Click on the WGS 84 item from the favorites list, or click the Search icon and typing in WGS 84 to search for it.



Enter ECEF coordinates

  1. In the Primary coordinates card, type in the X, Y, and Z ECEF values.

    The ECEF values are converted to the secondary WGS 84 coordinates. A marker appears on the map.



Monday, March 18, 2024

Convert North East Down (NED) to/from longitude,latitude and other various coordinate systems

The Koordinat2 web app (https://dominoc925-pages.appspot.com/webapp/koordinat2/default.html) has been upgraded to perform coordinate transformation between local coordinate system North East Down (NED) and many other coordinate systems including MGRS, geographic longitude/latitude, etc. 

This post shows the typical steps to perform the local NED conversion to other coordinate systems.

Define the NED local origin 

The local origin should be as near as possible to the area of interest. If it is too far away, the calculation rounding errors would result in erroneous converted coordinates.

  1. Use a browser to open the Koordinat2 web app url (https://dominoc925-pages.appspot.com/webapp/koordinat2/default.html).

    The Koordinat2 web app home page appears.


  2. Click the hamburger menu icon on the top left.

    The Navigation drawer opens.



  3. Click the Settings item.

    The Settings dialog appears.


  4. Choose the item North East Down reference origin.

    The NED Reference Origin appears.


  5. In the dialog, enter the reference origin Longitude, Latitude, and Altitude. Click Save.

    The Reference Origin is saved.

Define NED as the Primary coordinates (input)

  1. In the Primary coordinates card, click the gear icon.

    The Primary coordinates selection appears.




  2. Select North East Down from the list or search for it by clicking the Search icon and typing in NED.


Define the Secondary coordinates (output)

  1. In the Secondary coordinates card, click the gear icon.

    The Secondary coordinates selection appears.



  2. Select a desired coordinate system e.g. WGS 84 from the list or search for it by clicking the Search icon and typing in  WGS 84.


Enter NED coordinates

  1. In the Primary coordinates card, type in the North, East, and Down values.

    The NED values are converted to the secondary coordinates. And a marker appears on the map.




Monday, February 12, 2024

Deserialize a list of JSON objects using json_annotation and json_serializable in Flutter

I could not find an example of how to read in a list of JSON objects from a file using the Flutter packages: json_annotation and json_serializable. After a while I figured the way to do it. This post shows an example of automating JSON de-serialization of a JSON file that looks like the following:

[
    {
        "code": "NED",
        "fullname": "North East Down (local)",
    },
    {
        "code": "ECEF",
        "fullname": "Earth Centered Earth Fixed (ECEF)",
    },    
    {
        "code": "LLA",
        "fullname": "WGS 84",
    }
]

Install the Flutter packages Json_serializable and Json_annotation

In a Terminal, type in the following:

$ cd /path/to/the/root/of/flutter/project/

$ flutter pub add json_annotation

$ flutter pub add json_serializable

Code the model class dart file

  1. In a text editor, create the model class dart file, e.g. cs_model.dart.

  2. Type in the following:

import 'package:json_annotation/json_annotation.dart';

// Part file to be auto generated
part 'cs_model.g.dart';

@JsonSerializable()
class APICoordSysQuery {

  // Factory method to load in the list of JSON objects
  factory APICoordSysQuery.fromJson(List<dynamic> parsedJson){
    List<APICoordSys> csList = List<APICoordSys>.empty();
    csList = parsedJson.map ( (e) => APICoordSys.fromJson(e)).toList();
    return APICoordSysQuery(
      csList: csList,
    );
  }
  
  // To be auto generated by the Json_serializable package
  Map<String, dynamic> toJson() => _$APICoordSysQueryToJson(this);

  @JsonKey(name: 'csList')
  List<APICoordSys> csList;

  APICoordSysQuery({
    required this.csList
  });
}

@JsonSerializable()
class APICoordSys {
  
  // Factory method to deserialize a JSON object to be auto generated
  factory APICoordSys.fromJson(Map<String, dynamic> json) =>
    _$APICoordSysFromJson(json);
  
  // Factory method to serialize a JSON Object to be auto generated
  Map<String, dynamic> toJson() => _$APICoordSysToJson(this);

  @JsonKey(name: 'code')
  String code;
  @JsonKey(name: 'fullname')
  String fullName;

  // Constructor
  APICoordSys({
    required this.code,
    required this.fullName
  });
}

Generate the .part file

  1. In a Terminal, run the following:

    $ cd /path/to/flutter/root/project/
    $ dart run build_runner build


    Processing messages appear and the cs_model.part.g.dart file is generated.

Now you should be able to load in a list of JSON objects in Flutter.

 

Monday, November 27, 2023

How I fixed the Android Studio current target and jvm target compatibility error

I encountered the following compilation error in Android Studio of one of my Android project about current target JVM compatibility (or incompatibility), as shown in the screenshot message listing below:

 

Execution failed for task ':app:compileDebugKotlin'.
> 'compileDebugJavaWithJavac' task (current target is 1.8) and 'compileDebugKotlin' task (current target is 17) jvm target compatibility should be set to the same Java version.
  Consider using JVM toolchain: https://kotl.in/gradle/jvm/toolchain

* Try:
> Run with --stacktrace option to get the stack trace.
> Run with --info or --debug option to get more log output.
> Run with --scan to get full insights.

I managed to fix the problem by ensuring the source and target are compatible, i.e. having the same Java version.

To fix the issue:

  1. In Android Studio, select File | Project Structure.

    The Project Structure dialog box appears.


  2. Click Gradle Settings.

    The Gradle dialog box appears.


  3. In the Use Gradle from combo box, choose 'gradle-wrapper.properties' file.

  4. In the Gradle JDK combo box, choose jbr-17 JetBrains Runtime version 17.

  5. Click OK to close all dialog boxes.

  6. In Android Studio, open the app's build.gradle file in the editor.


  7. Within the android construct, add in the compileOptions as shown in the listing below.

android {
    compileSdk 33

// ...etc...

    compileOptions {
        sourceCompatibility JavaVersion.VERSION_17
        targetCompatibility JavaVersion.VERSION_17
    }
}

From this point on, compiling the app should not throw upany  JVM target compatibility errors.

Monday, October 23, 2023

React JS Material UI icons for Mastodon and Blogger

Other than the Linkedin icon, I couldn't find the Mastodon and Blogger React Material UI (MUI) icons in the React @mui/icons-material library so I had to make my own with Inkscape and a good old text editor. The results are shown in the screenshot below.

 Here is the MastodonIcon.js code for the Mastodon icon:

import * as React from 'react';
import SvgIcon from '@mui/material/SvgIcon';

export default function MastodonIcon() {
  return (
    <SvgIcon>
      <svg className="mastodon" width="16" height="16" fill="currentColor" version="1.1" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg">
        <path d="m10.751 11.617c1.7387-0.20699 3.2514-1.2721 3.4412-2.2449 0.30012-1.5334 0.27598-3.7421 0.27598-3.7421 0-2.9926-1.9715-3.8706-1.9715-3.8706-0.99355-0.45451-2.7002-0.6451-4.4734-0.65976h-0.043122c-1.774 0.014661-3.48 0.20526-4.4734 0.65976 0 0-1.9706 0.87709-1.9706 3.8706l-0.00173 0.57093c-0.00345 0.55195-0.00603 1.1643 0.0095 1.8033 0.071582 2.9271 0.53988 5.8128 3.26 6.5286 1.2539 0.33031 2.3311 0.39931 3.1988 0.35188 1.5722-0.08625 2.4554-0.55799 2.4554-0.55799l-0.05176-1.1358s-1.1237 0.3536-2.3864 0.31047c-1.2505-0.04312-2.57-0.13454-2.7728-1.6628a3.1169 3.1169 0 0 1-0.028456-0.42777s1.2281 0.2984 2.784 0.36912c0.95126 0.04312 1.843-0.0552 2.7495-0.163zm1.3911-2.1302h-1.4429v-3.5188c0-0.74083-0.31393-1.1168-0.94091-1.1168-0.69339 0-1.041 0.44587-1.041 1.329v1.9258h-1.4351v-1.9266c0-0.88313-0.34756-1.329-1.041-1.329-0.62699 0-0.94091 0.37602-0.94091 1.1177v3.5179h-1.4429v-3.6239c0-0.74083 0.18973-1.329 0.56921-1.7646 0.39327-0.43553 0.90728-0.6589 1.5463-0.6589 0.73824 0 1.2971 0.28288 1.6671 0.84777l0.35964 0.5994 0.35964-0.5994c0.36998-0.5649 0.92884-0.84777 1.668-0.84777 0.6382 0 1.1522 0.22337 1.5446 0.6589 0.38119 0.43553 0.57007 1.0237 0.57007 1.7646z" strokeWidth=".86244" />
      </svg>
    </SvgIcon>
  );
}

And here is the BloggerIcon.js code for the Blogger icon:

import * as React from 'react';
import SvgIcon from '@mui/material/SvgIcon';

export default function SvgIconChildren() {
  return (
    <SvgIcon>
      <svg width="50.664mm" height="50.575mm" version="1.1" viewBox="0 0 179.52 179.2" xmlns="http://www.w3.org/2001/svg">
        <defs>
          <clipPath id="clipPath8">
            <path d="m111.47 137.84c6.8936-0.96075 12.296-3.7837 17.364-9.0736 3.6662-3.8268 5.9611-7.9689 7.4609-13.466 0.62305-2.2837 0.67529-3.3956 0.78946-16.804 0.0863-10.12 0.0143-14.86-0.24367-16.056-0.37384-1.7344-1.4336-3.345-2.6427-4.0165-0.37209-0.20664-2.7561-0.47002-5.2978-0.58526-4.2591-0.19314-4.7356-0.278-6.08-1.0829-2.1324-1.2768-2.7197-2.6555-2.7255-6.3987-0.0111-7.152-2.9243-13.792-8.6802-19.785-4.1006-4.2694-8.6751-7.1592-13.896-8.7785-1.2498-0.38765-4.0484-0.51957-13.422-0.63274-14.708-0.17757-17.973 0.13047-22.98 2.1682-9.2314 3.7568-15.864 11.674-18.284 21.824-0.45444 1.9064-0.54266 4.9618-0.65 22.513-0.13448 21.988 0.01386 25.217 1.3586 29.575 1.111 3.6002 2.232 5.8063 4.5414 8.9376 4.3994 5.9652 10.993 10.273 17.585 11.49 3.137 0.57911 41.84 0.72399 45.804 0.17161z" display="none" fill="#fff" strokeWidth=".86401" />
            <path className="clip" d="m8.0345 6.3499h163.45v166.5h-163.45zm103.44 131.49c6.8936-0.96075 12.296-3.7837 17.364-9.0736 3.6662-3.8268 5.9611-7.9689 7.4609-13.466 0.62305-2.2837 0.67529-3.3956 0.78946-16.804 0.0863-10.12 0.0143-14.86-0.24367-16.056-0.37384-1.7344-1.4336-3.345-2.6427-4.0165-0.37209-0.20664-2.7561-0.47002-5.2978-0.58526-4.2591-0.19314-4.7356-0.278-6.08-1.0829-2.1324-1.2768-2.7197-2.6555-2.7255-6.3987-0.0111-7.152-2.9243-13.792-8.6802-19.785-4.1006-4.2694-8.6751-7.1592-13.896-8.7785-1.2498-0.38765-4.0484-0.51957-13.422-0.63274-14.708-0.17757-17.973 0.13047-22.98 2.1682-9.2314 3.7568-15.864 11.674-18.284 21.824-0.45444 1.9064-0.54266 4.9618-0.65 22.513-0.13448 21.988 0.01386 25.217 1.3586 29.575 1.111 3.6002 2.232 5.8063 4.5414 8.9376 4.3994 5.9652 10.993 10.273 17.585 11.49 3.137 0.57911 41.84 0.72399 45.804 0.17161z" strokeWidth=".86401" />
          </clipPath>
        </defs>
        <path d="m-82.995 87.838v-171.9h1020v343.8h-1020v-171.9z" fill="none" />
        <g transform="matrix(.92141 0 0 .92141 7.0545 7.0421)" fill="#fff" strokeWidth=".86401">
          <path d="m30.568 167.24c-2.8716-0.77176-5.3495-1.9071-7.6348-3.4982-1.9292-1.3431-4.7488-4.1198-5.8212-5.7323-1.3096-1.9694-2.815-5.342-3.4285-7.6809-0.62576-2.3858-0.6359-3.336-0.64713-60.605-0.01116-56.98 0.0015-58.233 0.61488-60.681 2.169-8.6596 8.8857-15.248 17.548-17.214 2.4907-0.56514 113.75-0.66398 116.44-0.10343 7.2734 1.5175 12.991 5.979 16.299 12.719 2.6303 5.3583 2.3951-0.53816 2.5156 63.081 0.0767 40.479 6e-3 57.473-0.24689 59.59-1.1845 9.9072-7.8732 17.592-17.498 20.103-2.462 0.64225-3.3184 0.65142-59.215 0.63395-54.046-0.0166-56.821-0.0454-58.928-0.61194z" clipPath="url(#clipPath8)" />
          <path d="m70.797 77.476c-3.5242-0.9925-4.841-6.1585-2.2508-8.8303 1.6555-1.7077 2.1132-1.7727 12.476-1.7727 9.3029 0 9.6156 0.02079 10.982 0.72599 1.9748 1.0193 2.8328 2.4564 2.8328 4.7444 0 2.0665-0.80586 3.5146-2.6034 4.6784-0.96518 0.62486-1.542 0.66375-10.657 0.71844-5.6283 0.0338-10.112-0.07615-10.78-0.26416zm-0.44157 34.766c-1.5128-0.67346-2.9215-2.5442-3.165-4.203-0.23192-1.5801 0.54532-3.7524 1.7367-4.854 1.5018-1.3886 2.161-1.435 20.63-1.4498 18.999-0.0152 18.9-0.0234 20.701 1.6952 2.5446 2.4274 2.0078 6.749-1.0585 8.5234l-3.151 0.52354-16.423 0.19641c-14.431 0.17258-18.519-0.0973-19.271-0.43172z" />
        </g>
      </svg>
    </SvgIcon>
  );
}

Hope these help somebody.

Monday, December 12, 2022

How to read an array from a ROS1 launch file into a C++ vector variable

I was having problems reading an array of doubles in a ROS launch file into a vector variable in a C++ ROS1 program node. After some digging around, I found I was doing it the wrong way; instead of using the <param> tag in the ROS launch file, I should be using the <rosparam> tag. 

The example ROS launch file listing shows the correct way to enter an array, e.g. [0.01, 0.1, 0.2] with the <rosparam> tag:

?xml version="1.0" encoding="UTF-8"?>
<launch>
  <node pkg="learning_tutorial" type="my_node" name="my_node">
        <param name="my_string_param" value="hello" />
        <rosparam param="my_array_param">[0.01, 0.1, 0.2]</rosparam>
  </node>
</launch>

Then in the ROS C++ code, I could do the following to read the array:

// ...etc...

using namespace ros;
using namespace std;

vector<double> myArrayParam;
NodeHandle nh;

// Read the my_array_param from the launch file into the myArrayParam variable
nh.param<vector<double>> ( "my_array_param", myArrayParam, { 1, 2, 3});

// Just print out the array parameter
ROS_INFO ( "My array: %f, %f, %f", myArrayParam[0], myArrayParam[1], myArrayParam[2]);
 
// ...etc...

Hope this helps somebody.


Monday, November 7, 2022

Shell script to batch bulk convert *.flac files to *.mp3

I have many music files in flac format and I wanted to convert them to a more compressed mp3 format with ffmpeg on Ubuntu so I can upload them to a storage limited portable music player. To ease the conversion task, I decided to write this simple shell script to do the job. In brief, the script will do the following:

  • find all the files with the extension .flac in the current directory
  • replace the file name extension .flac with the .mp3 extension
  • create a temporary script that calls the ffmpeg command to convert
  • run the temporary script

The listing of the shell script is shown below.  

# Define the internal field separator as a newline
IFS=$'\n'

# Find all the *.flac files in the current directory and perform the conversion
for f in `find . -name "*.flac" `;
do
	# Use the input flac file name prefix and replace the .flac extension with a .mp3 extension
	f=$(echo $f | cut -c 3-)
	outfile=$(basename $f .flac)
	outfile=$outfile.mp3
	
	echo "Convert $f->$outfile..."
	
	# Form the ffmpeg command to convert the input flac file to mp3
	cmd="ffmpeg -hide_banner -i \"$f\" -ab 320k -map_metadata 0 -id3v2_version 3 \"$outfile\" "
	
	# Create a temporary shell script for running the conversion
	echo $cmd > /tmp/tmp.sh
	
	# Run the conversion to mp3
	bash /tmp/tmp.sh
	
	# Clean up
	rm /tmp/tmp.sh
done

To use this shell script, you can do the following:

  1. Save the code listing above to a file e.g. run.sh in a directory, e.g. /path/to
    /directory/


  2. Open up a Linux Terminal.

  3. In the Terminal, type in the command to change directory to the location of the flac files, e.g. /path/to/music/

    $ cd /path/to/music



  4. At the prompt, type in the command to run the shell script.

    $ bash /path/to/run.sh

    The flac files are converted to mp3 files.
 

Monday, October 31, 2022

Simple C++ example to send serial AT commands to and receive data from a modem

I tried using many C/C++ libraries trying to coax a 5G modem to respond to my input AT commands for a long time but I was not successful. The command I was trying to send was the Quectel modem command to query for PDN channels:

AT+CGDCONT?

After a while, I figured out I had to simulate a keyboard Enter press in code to actually tell the modem the command is complete. So all I had to do was append the carriage return (\r) and new line (\n) characters to the AT command string, e.g:

string cmd = "AT+CGDCONT?\r\n";

A working C++ code example is shown below:

#include <string>
#include <iostream>
#include <cstdio>
#include <unistd.h>

// Using header only library from 
// https://github.com/karthickai/serial
#include "Serial.h"


using namespace std;


int main ( int argc, char** argv) {

        string commPort = "/dev/ttyUSB2";
        unsigned int baud = 115200;
        serial::Serial serial;

        serial.open ( commPort, baud);

        if ( !serial.isOpen()) {
                cout << "comm port is not open" << endl;
                return 1;
        }

        // A modem AT query command
        string cmd = "AT+CGDCONT?\r\n";
        vector<uint8_t> cmdVec (cmd.begin(), cmd.end());

        // send command to the modem
        size_t bytesSent = serial.transmitAsync(cmdVec);
        cout << "Bytes sent " << bytesSent << endl;

        int received_bytes = -1;

        while (received_bytes != 0 ) {
                // read one byte from the modem and timeout if the
                // there is no response in more than 1 sec.
                future<vector<uint8_t>> future = serial.receiveAsync(1, 1000);
                vector<uint8_t> const received_data = future.get();
                received_bytes = received_data.size();

                string str(received_data.begin(), received_data.end());
                cout << "[" << received_data[0] << "] " << endl;
        }
        // Close the serial port
        serial.close();

        cout << "End of process" << endl;

        return 0;

}

The example prints out the data sent by the modem to the calling program, as shown below:

Note: this example is using the modern serial header only C++ library from this site: https://github.com/karthickai/serial