CAM/DENM C Sample Code Using OSS ASN.1 Tools


This sample is provided as part of the OSS ASN.1 Tools trial and commercial shipments. The source code and related files are listed below for reference.

The sample demonstrates how to use the OSS ASN.1 Tools to work with messages for the CAM and DENM standards (ETSI EN 302 637-2 and ETSI EN 302 637-3).

It runs on Linux on x86-64 as an example and illustrates how to create, encode, decode, and print CAM and DENM messages using the OSS ASN.1 Tools API.

A makefile is included for building and running the test program using the OSS ASN.1/C runtime.

To explore more samples (LTE RRC, 5G RRC, S1AP, X2AP), visit the main Sample Code page.

Overview

This sample shows how to:

  • Initialize the OSS ASN.1/C runtime for the CAM and DENM schemas.
  • Create CAM and DENM messages.
  • Encode CAM and DENM messages.
  • Decode encoded messages.
  • Print decoded message contents.
  • Build and run the sample test program with a makefile on Linux.

Files in This Sample

The files listed below are included in the OSS ASN.1 Tools trial and commercial shipments and are used by this sample program:

File Description
README.TXT Instructions and sample overview.
CAM.asn ASN.1 specification from ETSI EN 302 637-2 V1.4.1 (2019-04).
DENM.asn ASN.1 specification from ETSI EN 302 637-3 V1.3.1 (2019-04).
ITS-Container.asn ASN.1 specification from ETSI TS 102 894-2 V1.3.1 (2018-08).
camdenm-directives.asn Additional ASN.1 compiler directives.
camdenm_h.c Simple C program that demonstrates creating, encoding, decoding, and printing CAM and DENM messages.
makefile Makefile that builds and runs the sample test.
makefile.rtoed Makefile that builds and runs the sample test using RTOED.
gui/ ASN.1 Studio project for viewing/compiling schema and generating sample data.

Build and Run Instructions

(Installation instructions for the OSS ASN.1 Tools are included in all trial and commercial shipments.)

To build and run this sample, install a trial or licensed version of the OSS ASN.1 Tools for C.

1. Generate C source and header files (runtime-only shipments)

If your shipment is runtime-only, generate the .c and .h files by running:

make cross

This creates a samples/cross directory with:

  • the required .asn files
  • scripts to run the OSS ASN.1/C compiler (asn1cpl.sh or asn1cpl.bat)
2. Build and run the sample
make

This command compiles the sample C source, links it with the OSS ASN.1 static library, and executes the test. To use another type of library in the shipment (such as a shared library), set the A makefile variable, for example:

make A=so
3. Clean up generated files
make clean
4. Other Platforms

Note: The C source code in this sample is platform-independent. Linux commands are shown as an example, but equivalent samples and build instructions are available for Windows, macOS, and other platforms. For help with platform-specific instructions, please contact OSS Support.

Code Listing: camdenm_h.c

The following listing shows the main C source file for this sample test program, camdenm_h.c. It demonstrates how to create, encode, decode, and print CAM and DENM messages using the OSS ASN.1 Tools API.

Show camdenm_h.c source code
/*****************************************************************************/
/* Copyright (C) ###RELEASE_YEAR### OSS Nokalva, Inc.  All rights reserved.                */
/*****************************************************************************/
/* THIS FILE IS PROPRIETARY MATERIAL OF OSS NOKALVA, INC.                    */
/* AND MAY BE USED ONLY BY DIRECT LICENSEES OF OSS NOKALVA, INC.             */
/* THIS FILE MAY NOT BE DISTRIBUTED.                                         */
/* THIS COPYRIGHT STATEMENT MAY NOT BE REMOVED.                              */
/* THIS SAMPLE PROGRAM IS PROVIDED AS IS. THE SAMPLE PROGRAM AND ANY RESULTS */
/* OBTAINED FROM IT ARE PROVIDED WITHOUT ANY WARRANTIES OR REPRESENTATIONS,  */
/* EXPRESS, IMPLIED OR STATUTORY.                                            */
/*****************************************************************************/


/*
 * Demonstrates CAM and DENM PDUs creating, encoding, decoding
 * and further processing.
 */

#include <string.h>
#include "camdenm.h"        /* Compiler-generated header file */

#ifdef DLL_LINKAGE
#define OSSTERM ossWterm
#else
#define OSSTERM ossterm
#endif

#define currentVersion		1

#define CTLTBL         camdenm            /* control table name located at the
                                           * bottom of the generated header file */
#define ENCODING_RULES OSS_PER_UNALIGNED  /* encoding rules */

/* Declaration of value creating functions for PDU types */
static CAM *create_CAM_value(OssGlobal *world);
static DENM *create_DENM_value(OssGlobal *world);

/* Declaration of functions that print some CAM/DENM fields */
static void print_some_CAM_fields(OssGlobal *world, CAM *value);
static void print_some_DENM_fields(OssGlobal *world, DENM *value);

/* Helper macro to allocate memory and set "length" and "value" fields of
 * an UNBOUNDED string to initialize it by a string literal. It is assumed
 * that "world" OssGlobal variable is available.
 */
#define SET_UNBOUNDED_BY_LITERAL(s, literal) {(s).length = sizeof(literal)-1;\
	(s).value = memcpy(ossGetMemory(world, sizeof(literal)-1), literal, \
	sizeof(literal)-1);}


int main(void)
{

    struct ossGlobal w, *world = &w;    /* OSS global structure */
    OssBuf        msgbuf = { 0, NULL }; /* encoded PDU */
    CAM          *CAM_msg = NULL,	/* unencoded CAM_PDU */
		 *CAM_dec = NULL;    	/* decoded CAM_PDU */
    DENM         *DENM_msg = NULL,	/* unencoded DENM_PDU */
		 *DENM_dec = NULL;  	/* decoded DENM_PDU */
    int           pdunum;               /* PDU number */
    int           retcode;		/* return code */

    /*
     * Initialize the OSS runtime first.
     */
    if ((retcode = ossinit(world, CTLTBL))) {
        ossPrint(NULL, "Ossinit() returned %d.\n", retcode);
	return retcode;
    }
#ifdef RELAXED_MODE
    /*
     * Set relaxed mode.
     */
    ossSetFlags(world, ossGetFlags(world) | NOCONSTRAIN | OSS_RELAXED);
#endif
    /*
     * Set encoding rules. Not required unless more than one encoding rule
     * was specified by ASN.1 compiler command line options
     */
/*    ossSetEncodingRules(world, ENCODING_RULES); */

    /* ==== Part 1. Create, encode and decode CAM message ==== */

    /* This is a fake loop statement to simplify the error handling. */
    do {
	pdunum = CAM_PDU;

	ossPrint(world,
		"\nCreating a CAM PDU value... ");

	CAM_msg = create_CAM_value(world);

	ossPrint(world, "Done\n");

	ossPrint(world,
		"\nPrinting the created value with ossPrintPDU() API function...\n");

	ossPrintPDU(world, pdunum, CAM_msg);
	/*
	 * Set the output buffer. Let the encoder allocate memory.
	 */
	msgbuf.value = NULL;
	msgbuf.length = 0;

	ossPrint(world,
		"\nEncoding the created CAM value... ");

	if ((retcode = ossEncode(world, /* pointer to the OSS global structure */
		  pdunum,           /* PDU identifier */
		  CAM_msg,          /* Unencoded PDU to encode */
		  &msgbuf           /* encoded PDU */
		))) {
	    break;
	}
	ossPrint(world, "Done\n");

	ossPrint(world, "\nEncoded CAM message:\n");
	ossPrintHex(world, (char *) msgbuf.value, msgbuf.length);

	/*
	 * Let the decoder allocate memory. It is also possible to provide
	 * a buffer to place the decoded value.
	 */
	CAM_dec = NULL;


	ossPrint(world,
		"\nDecoding the encoded CAM message... ");

	if ((retcode = ossDecode(world,  /* pointer to the OSS global structure */
		  &pdunum,               /* PDU identifier */
		  &msgbuf,               /* encoded PDU to decode */
		  (void **)&CAM_dec      /* decoded PDU */
		))) {
	    break;
	}

        ossPrint(world, "Done\n");

	ossPrint(world,
	    "\nComparing the decoded message with the original PDU value... ");

	ossPrint(world, ossCmpValue(world, pdunum, CAM_msg, CAM_dec)
		    ? "Failed\n" : "Succeeded\n");

	ossPrint(world, "\nPartial custom printing of the decoded CAM PDU:\n\n");

	print_some_CAM_fields(world, CAM_dec);

    } while (0);

    if(retcode)
	ossPrint(world, "Failed with return code %d\n%s\n", retcode, ossGetErrMsg(world));

    /*
     * Free the memory allocated by decoder and encoder.
     */
    if (CAM_dec)
	ossFreePDU(world, pdunum, CAM_dec);
    if (msgbuf.value)
	ossFreeBuf(world, msgbuf.value);
    if (CAM_msg)
	ossFreePDU(world, CAM_PDU, CAM_msg);


    /* ==== Part 2. Create, encode and decode DENM message ==== */

    /* This is a fake loop statement to simplify the error handling. */
    do {
	pdunum = DENM_PDU;

        ossPrint(world,
                "\nCreating a DENM PDU value... ");

	DENM_msg = create_DENM_value(world);

	ossPrint(world, "Done\n");

	ossPrint(world,
		"\nPrinting the created value with ossPrintPDU() API function...\n");

	ossPrintPDU(world, pdunum, DENM_msg);
	/*
	 * Set the output buffer. Let the encoder allocate memory.
	 */
	msgbuf.value = NULL;
	msgbuf.length = 0;
	/*
	 * Encode the PDU.
	 */
	ossPrint(world,
		"\nEncoding the created DENM value... ");

	if ((retcode = ossEncode(world, /* pointer to the OSS global structure */
		  pdunum,           	/* PDU identifier */
		  DENM_msg,          	/* Unencoded PDU to encode */
		  &msgbuf               /* encoded PDU */
		))) {
	    break;
	}

	ossPrint(world, "Done\n");

	ossPrint(world, "\nEncoded DENM message:\n");
	ossPrintHex(world, (char *) msgbuf.value, msgbuf.length);

	/*
	 * Let the decoder allocate memory. It is also possible to provide
	 * a buffer to place the decoded value.
	 */
	DENM_dec = NULL;

        ossPrint(world,
		"\nDecoding the encoded DENM message... ");

	if ((retcode = ossDecode(world, /* pointer to the OSS global structure */
		  &pdunum,           	/* PDU identifier */
		  &msgbuf,           	/* encoded PDU to decode */
		  (void **)&DENM_dec   /* decoded PDU */
		))) {
	    break;
	}

        ossPrint(world, "Done\n");

	ossPrint(world,
	    "\nComparing the decoded message with the original PDU value... ");

	ossPrint(world, ossCmpValue(world, pdunum, DENM_msg, DENM_dec)
		    ? "Failed\n" : "Succeeded\n");

	ossPrint(world,
		"\nPartial custom printing of the decoded DENM PDU:\n\n");

	print_some_DENM_fields(world, DENM_dec);

    } while (0);

    if(retcode)
	ossPrint(world, "Failed with return code %d\n%s\n", retcode, ossGetErrMsg(world));

    /*
     * Free the memory allocated by decoder and encoder.
     */
    if (DENM_dec)
	ossFreePDU(world, pdunum, DENM_dec);
    if (msgbuf.value)
	ossFreeBuf(world, msgbuf.value);
    if (DENM_msg)
	ossFreePDU(world, DENM_PDU, DENM_msg);
    /*
     * Do final cleanup.
     */
    OSSTERM(world);
    return retcode;
}


/* Creation of sample value for the type 'CAM' */
/* ==========================================  */

static const unsigned char _static_value6[] = {
    0x00, 0x11, 0x22, 0x33, 0x44, 0x55
};

static CAM * create_CAM_value(OssGlobal *world)
{
    CAM *_CAM_msg = (CAM *)ossGetMemory(world, sizeof(struct CAM));

    _CAM_msg->header.protocolVersion = currentVersion;
    _CAM_msg->header.messageID = messageID_cam;
    _CAM_msg->header.stationID = 1234567;

    _CAM_msg->cam.generationDeltaTime = 11409;
    _CAM_msg->cam.camParameters.bit_mask = 0;
    {
	BasicContainer *b_container = &_CAM_msg->cam.camParameters.basicContainer;

	b_container->stationType = passengerCar;
	b_container->referencePosition.latitude = 40487111;
	b_container->referencePosition.longitude = 79494789;
	b_container->referencePosition.positionConfidenceEllipse.semiMajorConfidence = 500;
	b_container->referencePosition.positionConfidenceEllipse.semiMinorConfidence = 400;
	b_container->referencePosition.positionConfidenceEllipse.semiMajorOrientation = wgs84North;
	b_container->referencePosition.altitude.altitudeValue = 2000;
	b_container->referencePosition.altitude.altitudeConfidence = alt_000_02;
    }
    _CAM_msg->cam.camParameters.highFrequencyContainer.choice = basicVehicleContainerHighFrequency_chosen;
    {
	BasicVehicleContainerHighFrequency *b_frequency = &_CAM_msg->cam.camParameters.highFrequencyContainer.u.basicVehicleContainerHighFrequency;

	b_frequency->bit_mask = 0;
	b_frequency->heading.headingValue = wgs84North;
	b_frequency->heading.headingConfidence = equalOrWithinZeroPointOneDegree;

	b_frequency->speed.speedValue = 2000;
	b_frequency->speed.speedConfidence = equalOrWithinOneCentimeterPerSec;

	b_frequency->driveDirection = forward;
	b_frequency->vehicleLength.vehicleLengthValue = 35;
	b_frequency->vehicleLength.vehicleLengthConfidenceIndication = noTrailerPresent;

	b_frequency->vehicleWidth = 20;
	b_frequency->longitudinalAcceleration.longitudinalAccelerationValue = 10;
	b_frequency->longitudinalAcceleration.longitudinalAccelerationConfidence = pointOneMeterPerSecSquared;

	b_frequency->curvature.curvatureValue = CurvatureValue_straight;
	b_frequency->curvature.curvatureConfidence = onePerMeter_0_00002;

	b_frequency->curvatureCalculationMode = yawRateUsed;
	b_frequency->yawRate.yawRateValue = YawRateValue_straight;
	b_frequency->yawRate.yawRateConfidence = degSec_000_01;

	b_frequency->bit_mask |= accelerationControl_present;
	b_frequency->accelerationControl.length = 7;
	b_frequency->accelerationControl.value = (unsigned char *)ossGetMemory(world, 1);

	b_frequency->accelerationControl.value[gasPedalEngaged_byte] = gasPedalEngaged;

	b_frequency->bit_mask |= steeringWheelAngle_present;
	b_frequency->steeringWheelAngle.steeringWheelAngleValue = 30;
	b_frequency->steeringWheelAngle.steeringWheelAngleConfidence = equalOrWithinOnePointFiveDegree;

	b_frequency->bit_mask |= lateralAcceleration_present;
	b_frequency->lateralAcceleration.lateralAccelerationValue = -2;
	b_frequency->lateralAcceleration.lateralAccelerationConfidence = pointOneMeterPerSecSquared;

	b_frequency->bit_mask |= verticalAcceleration_present;
	b_frequency->verticalAcceleration.verticalAccelerationValue = pointOneMeterPerSecSquaredUp;
	b_frequency->verticalAcceleration.verticalAccelerationConfidence = pointOneMeterPerSecSquared;
    }

    _CAM_msg->cam.camParameters.bit_mask |= lowFrequencyContainer_present;
    {
	LowFrequencyContainer *l_container = &_CAM_msg->cam.camParameters.lowFrequencyContainer;

	l_container->choice = basicVehicleContainerLowFrequency_chosen;
	l_container->u.basicVehicleContainerLowFrequency.vehicleRole = VehicleRole_default;
	l_container->u.basicVehicleContainerLowFrequency.exteriorLights.length = 8;
	l_container->u.basicVehicleContainerLowFrequency.exteriorLights.value = (unsigned char *)ossGetMemory(world, 1);

	l_container->u.basicVehicleContainerLowFrequency.exteriorLights.value[lowBeamHeadlightsOn_byte] =
					    lowBeamHeadlightsOn | leftTurnSignalOn | daytimeRunningLightsOn;

	/* Create a PathHistory list of one element */
	{
	    PathHistory _pathHistory;

	    l_container->u.basicVehicleContainerLowFrequency.pathHistory =
		_pathHistory = (PathHistory)ossGetMemory(world, sizeof(struct PathHistory));
	    _pathHistory->value.bit_mask = 0;
	    _pathHistory->value.pathPosition.deltaLatitude = 10000;
	    _pathHistory->value.pathPosition.deltaLongitude = 5000;
	    _pathHistory->value.pathPosition.deltaAltitude = DeltaAltitude_unavailable;
	    _pathHistory->value.bit_mask |= pathDeltaTime_present;
	    _pathHistory->value.pathDeltaTime = 1000;
	    _pathHistory->next = NULL;
	}
    }
    _CAM_msg->cam.camParameters.bit_mask |= specialVehicleContainer_present;
    _CAM_msg->cam.camParameters.specialVehicleContainer.choice = publicTransportContainer_chosen;
    _CAM_msg->cam.camParameters.bit_mask |= specialVehicleContainer_present;
    {
	PublicTransportContainer *p_container = &_CAM_msg->cam.camParameters.specialVehicleContainer.u.publicTransportContainer;

	p_container->bit_mask = 0;
	p_container->embarkationStatus = FALSE;
	p_container->bit_mask |= ptActivation_present;
	p_container->ptActivation.ptActivationType = undefinedCodingType;
	p_container->ptActivation.ptActivationData.length = 6;
#ifdef LED
        p_container->ptActivation.ptActivationData.value =
         (unsigned char*) ossGetInitializedMemory(world, 6);
#endif
	memcpy(p_container->ptActivation.ptActivationData.value, _static_value6, 6);
    }
    return _CAM_msg;
}

/* Creation of sample value for the type 'DENM' */
/* ===========================================  */

static DENM *create_DENM_value(OssGlobal *world)
{
    DENM *_DENM_msg = (DENM *)ossGetMemory(world, sizeof(struct DENM));

    _DENM_msg->header.protocolVersion = currentVersion;
    _DENM_msg->header.messageID = messageID_denm;
    _DENM_msg->header.stationID = 1234567;

    _DENM_msg->denm.bit_mask = 0;
    {
	ManagementContainer *_management = &_DENM_msg->denm.management;
	ReferencePosition *_eventPosition = &_management->eventPosition;

	_management->bit_mask = 0;
	_management->actionID.originatingStationID = 20;
	_management->actionID.sequenceNumber = 30;

	_management->detectionTime = 45000000000ULL;
	_management->referenceTime = oneMillisecAfterUTCStartOf2004;

	_eventPosition->latitude = 40487111;
	_eventPosition->longitude = -79494789;
	_eventPosition->positionConfidenceEllipse.semiMajorConfidence = 500;
	_eventPosition->positionConfidenceEllipse.semiMinorConfidence = 400;
	_eventPosition->positionConfidenceEllipse.semiMajorOrientation = 10;

	_eventPosition->altitude.altitudeValue = 2000;
	_eventPosition->altitude.altitudeConfidence = alt_000_02;

	_management->relevanceDistance = lessThan100m;
	_management->relevanceTrafficDirection = upstreamTraffic;

	_management->bit_mask |= validityDuration_present;
	_management->validityDuration = 600;

	_management->bit_mask |= transmissionInterval_present;
	_management->transmissionInterval = oneMilliSecond;
	_management->stationType = unknown;
    }
    {
	SituationContainer *_situation = &_DENM_msg->denm.situation;

	_DENM_msg->denm.bit_mask |= situation_present;
	_situation->bit_mask = 0;
	_situation->informationQuality = lowest;
	_situation->eventType.causeCode = roadworks;
	_situation->eventType.subCauseCode = 0;
    }
    {
	LocationContainer *_location = &_DENM_msg->denm.location;

	_DENM_msg->denm.bit_mask |= location_present;
	_location->bit_mask = 0;
	_location->bit_mask |= eventSpeed_present;
	_location->eventSpeed.speedValue = standstill;
	_location->eventSpeed.speedConfidence = equalOrWithinOneCentimeterPerSec;

	_location->bit_mask |= eventPositionHeading_present;
	_location->eventPositionHeading.headingValue = wgs84North;
	_location->eventPositionHeading.headingConfidence = equalOrWithinOneDegree;

	/* Create a Traces list of one element */
	{
	    Traces _traces;

	    _location->traces =
		_traces = (Traces)ossGetMemory(world, sizeof(struct Traces));
	    /* Create a PathHistory list of two elements */
	    {
		PathHistory _pathHistory;

		/* Fill the element #1 */
		_traces->value =
		    _pathHistory = (PathHistory)ossGetMemory(world, sizeof(struct PathHistory));
		_pathHistory->value.bit_mask = 0;
		_pathHistory->value.pathPosition.deltaLatitude = 20;
		_pathHistory->value.pathPosition.deltaLongitude = 20;
		_pathHistory->value.pathPosition.deltaAltitude = DeltaAltitude_unavailable;
		_pathHistory->value.bit_mask |= pathDeltaTime_present;
		_pathHistory->value.pathDeltaTime = tenMilliSecondsInPast;

		/* Fill the element #2 */
		_pathHistory->next = (PathHistory)ossGetMemory(world, sizeof(struct PathHistory));
		_pathHistory = _pathHistory->next;
		_pathHistory->value.bit_mask = 0;
		_pathHistory->value.pathPosition.deltaLatitude = 22;
		_pathHistory->value.pathPosition.deltaLongitude = 22;
		_pathHistory->value.pathPosition.deltaAltitude = DeltaAltitude_unavailable;
		_pathHistory->next = NULL;
	    }
	    _traces->next = NULL;
	}
	_location->bit_mask |= roadType_present;
	_location->roadType = urban_NoStructuralSeparationToOppositeLanes;
    }
    {
	AlacarteContainer *_alacarte = &_DENM_msg->denm.alacarte;
	ImpactReductionContainer *_impactReduction = &_alacarte->impactReduction;
	RoadWorksContainerExtended *_roadWorks = &_alacarte->roadWorks;
	StationaryVehicleContainer *_stationaryVehicle = &_alacarte->stationaryVehicle;

	_DENM_msg->denm.bit_mask |= alacarte_present;
	_alacarte->bit_mask = 0;
	_alacarte->bit_mask |= impactReduction_present;
	_impactReduction->heightLonCarrLeft = HeightLonCarr_oneCentimeter;
	_impactReduction->heightLonCarrRight = HeightLonCarr_oneCentimeter;
	_impactReduction->posLonCarrLeft = PosLonCarr_oneCentimeter;
	_impactReduction->posLonCarrRight = PosLonCarr_oneCentimeter;

	/* Create a PositionOfPillars list of one element */
	{
	    PositionOfPillars _posOfPillars;

	    _impactReduction->positionOfPillars =
		_posOfPillars = (PositionOfPillars)ossGetMemory(world, sizeof(struct PositionOfPillars));
	    _posOfPillars->value = PosPillar_tenCentimeters;
	    _posOfPillars->next = NULL;
	}

	_impactReduction->posCentMass = PosCentMass_unavailable;
	_impactReduction->wheelBaseVehicle = WheelBaseVehicle_tenCentimeters;
	_impactReduction->turningRadius = point4Meters;
	_impactReduction->posFrontAx = PosFrontAx_tenCentimeters;
	_impactReduction->positionOfOccupants.length = 20;
	_impactReduction->positionOfOccupants.value = (unsigned char *)ossGetMemory(world, 3);
	_impactReduction->positionOfOccupants.value[row1LeftOccupied_byte] =
		row1LeftOccupied | row1RightOccupied | row1MidOccupied | row1NotDetectable
		| row1NotPresent | row2LeftOccupied | row2RightOccupied;
	_impactReduction->positionOfOccupants.value[row3NotPresent_byte] = row3NotPresent;
	_impactReduction->positionOfOccupants.value[row4NotPresent_byte] = row4NotPresent;

	_impactReduction->vehicleMass = 20;
	_impactReduction->requestResponseIndication = response;

	_alacarte->bit_mask |= externalTemperature_present;
	_alacarte->externalTemperature = oneDegreeCelsius;

	_alacarte->bit_mask |= roadWorks_present;
	_roadWorks->bit_mask = 0;
	_roadWorks->bit_mask |= lightBarSirenInUse_present;
	_roadWorks->lightBarSirenInUse.length = 2;
	_roadWorks->lightBarSirenInUse.value = (unsigned char *)ossGetMemory(world, 1);
	_roadWorks->lightBarSirenInUse.value[lightBarActivated_byte] = lightBarActivated | sirenActivated;

	_roadWorks->bit_mask |= RoadWorksContainerExtended_closedLanes_present;
	_roadWorks->closedLanes.bit_mask = 0;
	_roadWorks->closedLanes.bit_mask |= outerhardShoulderStatus_present;
	_roadWorks->closedLanes.outerhardShoulderStatus = availableForStopping;

	_roadWorks->closedLanes.bit_mask = drivingLaneStatus_present;
	_roadWorks->closedLanes.drivingLaneStatus.length = 3;
	_roadWorks->closedLanes.drivingLaneStatus.value = (unsigned char *)ossGetMemory(world, 1);
	_roadWorks->closedLanes.drivingLaneStatus.value[0] = 0x20 | 0x40;

	_roadWorks->bit_mask |= restriction_present;

	/* Create a RestrictedTypes list of one element */
	{
	    RestrictedTypes _restrictedTypes;

	    _roadWorks->restriction =
		_restrictedTypes = (RestrictedTypes)ossGetMemory(world, sizeof(struct RestrictedTypes));
	    _restrictedTypes->value = unknown;
	    _restrictedTypes->next = NULL;
	}

	_roadWorks->bit_mask |= RoadWorksContainerExtended_speedLimit_present;
	_roadWorks->speedLimit = 20;

	_roadWorks->bit_mask |= RoadWorksContainerExtended_incidentIndication_present;
	_roadWorks->incidentIndication.causeCode = reserved;
	_roadWorks->incidentIndication.subCauseCode = 0;

	_roadWorks->bit_mask |= recommendedPath_present;

	/* Create a ItineraryPath list of one element */
	{
    	    ItineraryPath _itineraryPath;

	    _roadWorks->recommendedPath =
		_itineraryPath = (ItineraryPath)ossGetMemory(world, sizeof(struct ItineraryPath));
	    _itineraryPath->value.latitude = 20;
	    _itineraryPath->value.longitude = 20;
	    _itineraryPath->value.positionConfidenceEllipse.semiMajorConfidence = SemiAxisLength_oneCentimeter;
	    _itineraryPath->value.positionConfidenceEllipse.semiMinorConfidence = SemiAxisLength_oneCentimeter;
	    _itineraryPath->value.positionConfidenceEllipse.semiMajorOrientation = wgs84North;
	    _itineraryPath->value.altitude.altitudeValue = 200;
	    _itineraryPath->value.altitude.altitudeConfidence = alt_000_02;
	    _itineraryPath->next = NULL;
	}

	_alacarte->bit_mask |= positioningSolution_present;
	_alacarte->positioningSolution = noPositioningSolution;

	_alacarte->bit_mask |= stationaryVehicle_present;
	_stationaryVehicle->bit_mask = 0;
	_stationaryVehicle->bit_mask |= stationarySince_present;
	_stationaryVehicle->stationarySince = equalOrGreater15Minutes;

	_stationaryVehicle->bit_mask |= stationaryCause_present;
	_stationaryVehicle->stationaryCause.causeCode = roadworks;
	_stationaryVehicle->stationaryCause.subCauseCode = 0;

	_stationaryVehicle->bit_mask |= numberOfOccupants_present;
	_stationaryVehicle->numberOfOccupants = 30;

	_stationaryVehicle->bit_mask |= vehicleIdentification_present;

	_stationaryVehicle->vehicleIdentification.bit_mask |=
						wMInumber_present | vDS_present;
#ifdef LED
	SET_UNBOUNDED_BY_LITERAL(_stationaryVehicle->vehicleIdentification.wMInumber,"WVW");

	SET_UNBOUNDED_BY_LITERAL(_stationaryVehicle->vehicleIdentification.vDS, "ZZZ1JZ");
#else
	strcpy(_stationaryVehicle->vehicleIdentification.wMInumber, "WVW");

	strcpy(_stationaryVehicle->vehicleIdentification.vDS, "ZZZ1JZ");
#endif

	_stationaryVehicle->bit_mask |= energyStorageType_present;
	_stationaryVehicle->energyStorageType.length = 6;
	_stationaryVehicle->energyStorageType.value = (unsigned char *)ossGetMemory(world, 1);
	_stationaryVehicle->energyStorageType.value[gasoline_byte] = gasoline;
    }

    return _DENM_msg;
}

static char *get_messageID(unsigned short id)
{
    static char value[16];

    switch (id) {
	case messageID_denm: return "denm";
	case messageID_cam:  return "cam";
	case poi:            return "poi";
	case spatem:         return "spatem";
	case mapem:          return "mapem";
	case ivim:           return "ivim";
	case ev_rsr:         return "ev-rsr";
	case tistpgtransaction: return "tistpgtransaction";
	case srem:   	     return "srem";
	case ssem:           return "ssem";
	case evcsn:          return "evcsn";
	case saem:           return "saem";
	case rtcmem:         return "rtcmem";
	default:
	    sprintf(value, "%d???", id);
    }
    return value;
}

static char *get_stationType(StationType stype)
{
    static char value[16];

    switch (stype) {
        case unknown:         return "unknown";
        case pedestrian:      return "pedestrian";
        case cyclist:         return "cyclist";
        case moped:           return "moped";
        case motorcycle:      return "motorcycle";
        case passengerCar:    return "passengerCar";
        case bus:             return "bus";
        case lightTruck:      return "lightTruck";
        case heavyTruck:      return "heavyTruck";
        case trailer:         return "trailer";
        case specialVehicles: return "specialVehicles";
        case tram:            return "tram";
	case roadSideUnit:    return "roadSideUnit";
	default:
	    sprintf(value, "%d", stype);
    }
    return value;
}

static char *get_SpeedValue(SpeedValue val)
{
    static char value[16];

    switch (val) {
	case standstill:		return "standstill";
	case oneCentimeterPerSec:	return "oneCentimeterPerSec";
	case SpeedValue_unavailable:	return "unavailable";
	default:
	    sprintf(value, "%d", val);
    }
    return value;
}

static void print_Position(OssGlobal *world,
			    Latitude latitude, Longitude longitude,
			    AltitudeValue altitudeValue)
{
    char *_valueName = NULL;

    ossPrint(world, "(");
    switch (latitude) {
    case Latitude_oneMicrodegreeNorth:
	_valueName = "oneMicrodegreeNorth";
	break;
    case Latitude_oneMicrodegreeSouth:
	_valueName = "oneMicrodegreeSouth";
	break;
    case Latitude_unavailable:
	_valueName = "unavailable";
	break;
    default:
	ossPrint(world, "%d.%06d", latitude / 1000000, abs(latitude % 1000000));
    }
    if (_valueName)
	ossPrint(world, "%s", _valueName);

    _valueName = NULL;
    ossPrint(world, ", ");
    switch (longitude) {
    case Longitude_oneMicrodegreeEast:
	_valueName = "oneMicrodegreeEast";
	break;
    case Longitude_oneMicrodegreeWest:
	_valueName = "oneMicrodegreeWest";
	break;
    case Longitude_unavailable:
	_valueName = "unavailable";
	break;
    default:
	ossPrint(world, "%d.%06d", longitude / 1000000, abs(longitude % 1000000));
    }
    if (_valueName)
	ossPrint(world, "%s", _valueName);

    _valueName = NULL;
    ossPrint(world, "), alt ");
    switch (altitudeValue) {
    case referenceEllipsoidSurface:
	_valueName = "referenceEllipsoidSurface";
	break;
    case AltitudeValue_oneCentimeter:
	_valueName = "oneCentimeter";
	break;
    case AltitudeValue_unavailable:
	_valueName = "unavailable";
	break;
    default:
	ossPrint(world, "%d.%02d m", altitudeValue / 100, abs(altitudeValue % 100));
    }
    if (_valueName)
	ossPrint(world, "%s", _valueName);
    ossPrint(world, "\n");
}

static char *get_CauseCode(CauseCodeType _causeCode)
{
    static	char	str[16];

    switch (_causeCode) {
	case reserved: return "reserved";
	case trafficCondition: return "trafficCondition";
	case accident: return "accident";
	case roadworks: return "roadworks";
	case adverseWeatherCondition_Adhesion: return "adverseWeatherCondition-Adhesion";
	case hazardousLocation_SurfaceCondition: return "hazardousLocation-SurfaceCondition";
	case hazardousLocation_ObstacleOnTheRoad: return "hazardousLocation-ObstacleOnTheRoad";
	case hazardousLocation_AnimalOnTheRoad: return "hazardousLocation-AnimalOnTheRoad";
	case humanPresenceOnTheRoad: return "humanPresenceOnTheRoad";
	case wrongWayDriving: return "wrongWayDriving";
	case rescueAndRecoveryWorkInProgress: return "rescueAndRecoveryWorkInProgress";
	case adverseWeatherCondition_ExtremeWeatherCondition: return "adverseWeatherCondition-ExtremeWeatherCondition";
	case adverseWeatherCondition_Visibility: return "adverseWeatherCondition-Visibility";
	case adverseWeatherCondition_Precipitation: return "adverseWeatherCondition-Precipitation";
	case slowVehicle: return "slowVehicle";
	case dangerousEndOfQueue: return "dangerousEndOfQueue";
	case vehicleBreakdown: return "vehicleBreakdown";
	case postCrash: return "postCrash";
	case humanProblem: return "humanProblem";
	case CauseCodeType_stationaryVehicle: return "stationaryVehicle";
	case emergencyVehicleApproaching: return "emergencyVehicleApproaching";
	case hazardousLocation_DangerousCurve: return "hazardousLocation-DangerousCurve";
	case collisionRisk: return "collisionRisk";
	case signalViolation: return "signalViolation";
	case dangerousSituation: return "dangerousSituation";
	default:
    	    sprintf(str, "%u", _causeCode);
    }
    return str;
}

static void print_some_CAM_fields(OssGlobal *world, CAM *value)
{
    BasicContainer         *b_container;
    HighFrequencyContainer *h_container;

    ossPrint(world, "messageID: %s\n", get_messageID(value->header.messageID));
    ossPrint(world, "stationID: %u\n", value->header.stationID);

    ossPrint(world, "generationDeltaTime: ");
    if (value->cam.generationDeltaTime == oneMilliSec)
	ossPrint(world, "oneMilliSec\n");
    else
	ossPrint(world, "%d\n", value->cam.generationDeltaTime);

    b_container = &value->cam.camParameters.basicContainer;
    ossPrint(world, "stationType: %s\n",
	get_stationType(b_container->stationType));

    {
	ReferencePosition *_refPos = &b_container->referencePosition;

	ossPrint(world, "referencePosition: ");
	print_Position(world, _refPos->latitude, _refPos->longitude,
				_refPos->altitude.altitudeValue);
    }

    h_container = &value->cam.camParameters.highFrequencyContainer;
    if (h_container->choice == basicVehicleContainerHighFrequency_chosen)
	ossPrint(world, "speedValue: %s\n",
	    get_SpeedValue(h_container->u.basicVehicleContainerHighFrequency.speed.speedValue));
    else
	ossPrint(world, "printing of emptyRSUContainerHighFrequency is not supported\n");
}

static void print_some_DENM_fields(OssGlobal *world, DENM *value)
{
    ossPrint(world, "messageID: %s\n", get_messageID(value->header.messageID));
    ossPrint(world, "stationID: %u\n", value->header.stationID);

    {
	ReferencePosition *_refPos = &value->denm.management.eventPosition;

        ossPrint(world, "eventPosition: ");
	print_Position(world, _refPos->latitude, _refPos->longitude,
				_refPos->altitude.altitudeValue);
    }

    if (value->denm.bit_mask & situation_present)
	ossPrint(world, "causeCode: %s\n",
		get_CauseCode(value->denm.situation.eventType.causeCode));
    else
        ossPrint(world, "causeCode omitted.\n");
}

Expected Output (Excerpt)

This is the expected output when running the sample:

Created CAM message
Encoding successful
Decoding successful
... printed CAM message ...

Created DENM message
Encoding successful
Decoding successful
... printed DENM message ...

ASN.1 Schema Excerpt (CAM.asn)

Show excerpt from CAM.asn
-- Excerpt from ETSI EN 302 637-2 V1.4.1 (2019-04)

CAM-PDU-Descriptions {itu-t (0) identified-organization (4) etsi (0) itsDomain (5) 
 wg1 (1) en (302637) cam (2) version (2)}
DEFINITIONS AUTOMATIC TAGS ::=
BEGIN

    IMPORTS
        ItsPduHeader, CauseCode, ReferencePosition, AccelerationControl, Curvature,
        CurvatureCalculationMode, Heading, LanePosition, EmergencyPriority, EmbarkationStatus, Speed,
        DriveDirection, LongitudinalAcceleration, LateralAcceleration, VerticalAcceleration, StationType,
        ExteriorLights, DangerousGoodsBasic, SpecialTransportType, LightBarSirenInUse, VehicleRole,
        VehicleLength, VehicleWidth, PathHistory, RoadworksSubCauseCode, ClosedLanes, TrafficRule,
        SpeedLimit, SteeringWheelAngle, PerformanceClass, YawRate, ProtectedCommunicationZone, PtActivation,
        Latitude, Longitude, ProtectedCommunicationZonesRSU, CenDsrcTollingZone 
    FROM ITS-Container {itu-t (0) identified-organization (4) etsi (0) 
        itsDomain (5) wg1 (1) ts (102894) cdd (2) version (2)};

    -- The root data frame for cooperative awareness messages
    CAM ::= SEQUENCE {
        header ItsPduHeader,
        cam CoopAwareness
    }

    CoopAwareness ::= SEQUENCE {
        generationDeltaTime GenerationDeltaTime,
        camParameters CamParameters
    }

    CamParameters ::= SEQUENCE {
        basicContainer BasicContainer,
        highFrequencyContainer HighFrequencyContainer,
        lowFrequencyContainer LowFrequencyContainer OPTIONAL,
        specialVehicleContainer SpecialVehicleContainer OPTIONAL,
        ...
    }

    HighFrequencyContainer ::= CHOICE {
        basicVehicleContainerHighFrequency BasicVehicleContainerHighFrequency,
        rsuContainerHighFrequency RSUContainerHighFrequency,
        ...
    }

    LowFrequencyContainer ::= CHOICE {
        basicVehicleContainerLowFrequency BasicVehicleContainerLowFrequency,
        ...
    }

    SpecialVehicleContainer ::= CHOICE {
        publicTransportContainer PublicTransportContainer,
        specialTransportContainer SpecialTransportContainer,
        dangerousGoodsContainer DangerousGoodsContainer,
        roadWorksContainerBasic RoadWorksContainerBasic,
        rescueContainer RescueContainer,
        emergencyContainer EmergencyContainer,
        safetyCarContainer SafetyCarContainer,
        ...
    }

    BasicContainer ::= SEQUENCE {
        stationType StationType,
        referencePosition ReferencePosition,
        ... 
    }

    BasicVehicleContainerHighFrequency ::= SEQUENCE {
        heading Heading,
        speed Speed,
        driveDirection DriveDirection,
        vehicleLength VehicleLength,
        vehicleWidth VehicleWidth,
        longitudinalAcceleration LongitudinalAcceleration,
        curvature Curvature,
        curvatureCalculationMode CurvatureCalculationMode,
        yawRate YawRate,
        accelerationControl AccelerationControl OPTIONAL,
        lanePosition LanePosition OPTIONAL,
        steeringWheelAngle SteeringWheelAngle OPTIONAL,
        lateralAcceleration LateralAcceleration OPTIONAL,
        verticalAcceleration VerticalAcceleration OPTIONAL,
        performanceClass PerformanceClass OPTIONAL,
        cenDsrcTollingZone CenDsrcTollingZone OPTIONAL
    }

Using ASN.1 Studio (Optional)

The gui/ subdirectory contains an ASN.1 Studio project for this sample. With ASN.1 Studio you can:

  • Open the CAM and DENM ASN.1 modules.
  • Generate code from the ASN.1 schema.
  • Create and edit sample encoded CAM and DENM messages.
  • Export projects to gmake makefiles.

Related Samples

Disclaimer

This sample is provided solely for illustration purposes, for example to demonstrate usage of the OSS ASN.1 Tools API with CAM and DENM messages. It does not represent a complete application. To build and run this sample, you must use a trial or licensed version of the appropriate OSS ASN.1 Tools. The copyright and license statements included in each source file remain fully applicable.

Need Help?

If you have questions about using this sample, contact OSS Nokalva Support.

See Also