maritime-labs/calypso-anemometer

Support compass readings

amotl opened this issue · 20 comments

amotl commented

Is this needed?

// enable the IMU to receive roll, pitch and eCompass
const characteristic = await device.writeCharacteristicWithResponseForService(
  service, '0000a003-0000-1000-8000-00805f9b34fb', Buffer.from([0x01]).toString('base64')
);

-- UltraSonicComponent.js#L131-L134

Would be nice to get compass readings from calypso. Did you make some progress here?

amotl commented

Dear Simon,

thank you for writing in. Others also asked about compass support recently. We will have to dive into a little recap to check the current state of the corresponding implementation.

Based on my findings shared below, I think compass data should be decoded well already, and may already make it into the corresponding SignalK telemetry adapter correctly. Can you verify that? On the other hand, what others have asked about, I think compass data is not translated to NMEA0183 yet.

With kind regards,
Andreas.

Details

It looks like 8843346 added decoding the compass data already:

class CalypsoDeviceStatusCharacteristic(Enum):
mode = BleCharSpec(uuid="0000a001-0000-1000-8000-00805f9b34fb", name="mode", decoder=CalypsoDeviceMode)
rate = BleCharSpec(uuid="0000a002-0000-1000-8000-00805f9b34fb", name="rate", decoder=CalypsoDeviceDataRate)
compass = BleCharSpec(
uuid="0000a003-0000-1000-8000-00805f9b34fb", name="compass", decoder=CalypsoDeviceCompassStatus
)

CalypsoDeviceStatusCharacteristic.compass,

However, I discovered that within the backlog:

- [o] Unlock support for device's "compass" feature
- [o] Make up ``NMEA-0183`` messages for other parameters ``battery_level``,
``temperature``, ``roll``, ``pitch``, and ``compass``

This is the current code responsible for translating compass data to SignalK:

SignalKDeltaItem(path="navigation.attitude.yaw", value=reading.compass),
SignalKDeltaItem(path="navigation.headingMagnetic", value=reading.compass),

This is the current code responsible for translating compass data to NMEA0183:

def set_reading(self, reading: CalypsoReading):
"""
Derive NMEA-0183 IIVWR message from measurement reading.
"""
reading = reading.adjusted()
iivwr = Nmea0183MessageIIVWR(
direction_degrees=reading.wind_direction,
speed_meters_per_second=reading.wind_speed,
)
self.items = [iivwr.to_message()]

amotl commented

In order to add more information here...

Calypso UP10 FAQ

Manufacturer information about the compass can be found at https://calypsoinstruments.com/web/content/884.

Compass to NMEA0183

On the other hand, what others have asked about, I think compass data is not translated to NMEA0183 yet.

@UserMacUseface told us that the NMEA sentence for this is quite simple 1:

HDT - Heading - True

        1   2 3 
        |   | | 
 $--HDT,x.x,T*hh<CR><LF>

 Field Number: 
  1) Heading Degrees, true 
  2) T = True 
  3) Checksum

Thank you already! We will consider implementing corresponding support on the next development iteration.

Footnotes

  1. http://aprs.gids.nl/nmea/#hdt

amotl commented

@UserMacUseface may have discovered a problem with decoded compass/heading data. Thank you so much for the report!

The value for heading is a bit funny.
image

While tracking the processing of this value through the code the other day, I already noticed the following snippet:

SignalKDeltaItem(path="navigation.attitude.yaw", value=reading.compass),
SignalKDeltaItem(path="navigation.headingMagnetic", value=reading.compass)

reading.compass is defined in the model as compass=360 - compass. However, while the documentation of Calypso claims degrees 1, the actual value seems to be radians though.

Is there a log where I can see the raw data being read from the device?

Footnotes

  1. image

amotl commented

However, while the documentation of Calypso claims degrees, the actual value seems to be radians though.

Ah, wow!

But the device claims to be the same as before, and there is no updated documentation about it? Maybe there has been something within the release notes of the firmware update you installed recently? (#13 (comment))

Is there a log where I can see the raw data being read from the device?

The decoder implementation has been based on information from the Calypso Ultrasonic decoding cheat sheet by Fabian Tollenaar. Logging raw data would not make much sense because it is in binary format.

@dataclasses.dataclass
class CalypsoReading:
wind_speed: float
wind_direction: int
battery_level: int
temperature: int
roll: int
pitch: int
compass: int
@classmethod
def from_buffer(cls, buffer: bytearray):
"""
Decoding cheat sheet by Fabian Tollenaar.
https://github.com/decipherindustries/signalk-calypso-ultrasonic/blob/master/DECODE
Length: 10
Bytes: 0-1 2-3 4 5 6 7 8-9
Ex. data: 0000 3F01 04 7C 00 00 0000
AABB CCDD EE FF GG HH IIJJ
Decode:
0-1. Wind speed: hex2dec(BB AA) / 100
2-3. Wind direction: hex2dec(DD CC)
4. Battery level: hex2dec(EE) * 10
5. Temp level: hex2dec(FF) - 100
6. Roll: hex2dec(GG) - 90
7. Pitch: hex2dec(HH) - 90
8-9. Compass: 360 - hex2dec(JJ II)
Ex.
0-1. hex2dec(00 00) => 0 / 100 => 0
2-3. hex2dec(01 3F) => 319 (degrees)
4. hex2dec(04) => 4 * 10 => 40%
5. hex2dec(7C) => 124 - 100 => 24 (degrees C)
6. hex2dec(00) => 0 - 90 => -90 (degrees)
7. hex2dec(00) => 0 - 90 => -90 (degrees)
8-9. 360 - hex2dec(00 00) => 360 - 0 => 360
"""
# Decode from binary.
data = struct.unpack("<HHBBBBH", buffer)
# Decompose.
(wind_speed, wind_direction, battery_level, temperature, roll, pitch, compass) = data
# Apply adjustments.
reading = cls(
wind_speed=wind_speed / 100.0,
wind_direction=wind_direction,
battery_level=battery_level * 10,
temperature=temperature - 100,
roll=roll - 90,
pitch=pitch - 90,
compass=360 - compass,
)
return reading

/cc @decipherindustries

But the device claims to be same as before, and there is no updated documentation about it? Maybe there has been something within the release notes of the firmware update you installed recently? (#13 (comment))

I cannot seem to find any release notes from calypso instruments :/

amotl commented

On a side note: roll and pitch also come from the gyro, don't they? Should we perhaps rename the reading.compass field to reading.heading for the sake of better "naming things"?

amotl commented

Regarding the "funny" readings, we discovered that all angle values should be submitted to SignalK using Radian (rad) unit, see also #14 (comment).

amotl commented

Regarding "reading the compass at all", we may have missed the corresponding method to enable it. Currently, I can only find methods to set "mode" and "datarate".

async def set_mode(self, mode: CalypsoDeviceMode):
logger.info(f"Setting device mode to {mode}")
await self.client.write_gatt_char(
CalypsoDeviceStatusCharacteristic.mode.value.uuid, data=bytes([mode.value]), response=True
)
async def set_datarate(self, rate: CalypsoDeviceDataRate):
logger.info(f"Setting data rate to {rate}")
await self.client.write_gatt_char(
CalypsoDeviceStatusCharacteristic.rate.value.uuid, data=bytes([rate.value]), response=True
)

amotl commented

Regarding "reading the compass at all", we may have missed the corresponding method to enable it.

The improvement GH-23 finally adds that, with a corresponding --compass=on command line option.

amotl commented

Regarding the missing "Compass to NMEA0183" telemetry propagation (#12 (comment)), the values for pitch and roll shall apparently be propagated into XDR - Transducer Values messages 1, as suggested by @UserMacUseface.

Within that message, I also spotted a slot to transmit air temperature. Does it make sense to propagate the temperature measured by the Calypso into this slot, or would it be wrong, because, mounted on the top of the mast, it will most probably not what meteorologists understand as "air temperature"?

            1 2   3 4       n
|   |   |   |       |
*  $--XDR,a,x.x,a,c--c, ..... *hh<CR><LF>
Measured Value Transducer Type Measured Data Unit of measure Transducer Name
air temperature "C" temperature 2 decimals "C" celsius "TempAir" or "ENV_OUTAIR_T"
pitch "A" angle -180..0 nose down 0..180 nose up "D" degrees "PTCH" or "PITCH"
rolling "A" angle -180..0 L 0..180 R "D" degrees "ROLL"

Footnotes

  1. NMEA 0183 Sentences » XDR

Within that message, I also spotted a slot to transmit air temperature. Does it make sense to propagate the temperature measured by the Calypso into this slot, or would it be wrong, because, mounted on the top of the mast, it will most probably not what meteorologists understand as "air temperature"?

Its surely a good idea to transmit all the values that the calypso generates. that way any future implementation can use it if needed.
The temperature difference on top of the mast is very probably not too much different then on deck level.
Nice gimmick to have but probably not critical to any kind of sailing activities :)

amotl commented

Thanks for clarifying. I've created GH-21 to discuss the topic of NMEA0183 sentences for non-compass related values seperately.

amotl commented

Regarding the NMEA0183 talker identifier, thank you for sharing #21 (comment). On this page, I discovered another example for encoding a compass/heading value.

$HCHDM,238,M<CR><LF>

... where "HC" specifies the talker as being a magnetic compass, the "HDM" specifies the magnetic heading message follows.

In our case, it would be HCHDT instead of HCHDM, because we emit the "true heading" - is that correct? Or, following your suggestion at #21 (comment), MLHDT would also be a feasable name, right?

amotl commented

Proposal

Would those example sentences be feasible?

# Heading
$MLHDT,274.07,T

# Pitch
$MLXDR,A,-42.42,D,PITCH#CALYPSO

# Roll
$MLXDR,A,30.07,D,ROLL#CALYPSO

Optionally, "heading" could additionally be emitted as XDR sentence as well?

# Heading
$MLXDR,A,274.07,D,HEADING#CALYPSO

P.S.: As we can see, there is apparently no way to tell XDR sentences apart, other than looking at the name field, here PITCH vs. ROLL vs. HEADING. Does this make sense?

amotl commented

XDR sentences can also carry multiple values -- thanks, @UserMacUseface. When combining pitch and roll, and also omitting the CALYPSO suffix, a corresponding sentence could look like that:

$MLXDR,A,-42.42,D,PTCH,A,30.07,D,ROLL

As seen at 12, the order of Pitch/Roll may also be important.

image

Footnotes

  1. Airmar GH2183 GPS/Gyro Compass User Manual » Table 3: NMEA 0183 Transmitted Sentences, p. 87

amotl commented

GH-24 implements the most recent suggestions. Thank you so much for the support!

amotl commented

Dear @honigwald and @UserMacUseface,

we just released calypso-anemometer 0.6.0, including the corresponding improvements, and will be happy to hear back from you about its outcome on this matter.

You may be able to get a reading with compass turned on, using

# Get device reading(s), with compass data (roll, pitch, heading).
calypso-anemometer read --compass=on
calypso-anemometer read --compass=on --subscribe

Emitting this data to both telemetry variants NMEA-0183 and SignalK, may also work well now.

With kind regards,
Andreas.

amotl commented

Unfortunately, with the recent 0.6.0 release, eCompass readings do not work yet, see GH-27.