MX Foundation 4
ar429_embedded_recorder.c
/*******************************************************************************
//
// File:
// ar429_embedded_recorder.c
//
// Copyright (c) MAX Technologies Inc. 1988-2015, All Rights Reserved.
// CONFIDENTIAL AND PROPRIETARY INFORMATION WHICH IS THE
// PROPERTY OF MAX TECHNOLOGIES INC.
//
// This example shows how to use the flash storage onboard the FlexMulti.
//
// - Format the onboard flash file(s) if necessary.
// - Automatic recording of ARINC 429 incoming RX data and write it on the flash.
// - Transmit ARINC 429 records.
// - Read the data stored on the flash.
//
// Hardware Requirements:
// - MAXT FlexMulti with loopback between first TX and RX ARINC 429 Multi Enhanced channel.
//
*******************************************************************************/
#include "example.h"
#define LOOPBACK
#define LOCAL
#define BUFFER_SIZE 4096 // 4KB
#define FILE_SIZE 256*1024*1024 // 256 MB
uint32 TX429PeriodicScheduling(HMXF_CHANNEL txChannel);
uint32 read429RecordsFromEmbeddedFlash(HMXF_NVSTORAGE flash, uint64 fileIdx);
/******************************************************************************/
// Main
/******************************************************************************/
int main(void)
{
uint32 rc;
HMXF_SERVER server;
HMXF_DEVICE device=0;
HMXF_MODULE module=0;
uint64 count=0;
HMXF_CHANNEL tx429=0, rx429=0;
HMXF_NVSTORAGE flash=0;
uint64 fileIdx=0, fileSize;
uint32 needUnmount=FALSE;
// Connect to services and initialize environment
#ifdef LOCAL
rc = mxfServerConnect("0.0.0.0", "", "", FALSE, &server);
#else
rc = mxfServerConnect("192.168.0.1", "admin", "admin", FALSE, &server);
#endif
if(rc!=MAXT_SUCCESS)
{
printf("Failed to connect; rc=0x%08x", rc);
printf("\nPress a key to terminate\n");
getchar();
return 0;
}
memset(&nvInfo, 0, sizeof(nvInfo));
// Initialize the server
printf("\nStarting\n");
rc = mxfSystemInit(server);
// Get the device handle
if (!rc)
rc = mxfSystemDeviceAllGet(server, MXF_DEVICE_ALL, 1, &count, &device);
// Get first module A429 Enhanced
if(!rc && count)
rc = mxfDeviceModuleAllGet(device, MXF_MODULE_MULTI_EH, 1, &count, &module);
// Get the first ARINC 429 TX channel
if (!rc && count)
rc = mxfModuleChannelAllGet(module, MXF_CLASS_A429, MXF_SCLASS_TX_CHANNEL, 1, &count, &tx429);
// Get the first ARINC 429 RX channel
if (!rc && count)
rc = mxfModuleChannelAllGet(module, MXF_CLASS_A429, MXF_SCLASS_RX_CHANNEL, 1, &count, &rx429);
// If A429 channel not found, return an error
if(!rc && !count)
rc = MAXT_ERROR_NOT_FOUND;
// Set timebase to 64-bit microseconds
if(!rc)
rc = mxfSystemTimeBaseSet(server, MXF_TIMEBASE_DEVICE_USEC);
//Activate loopback before transmission and reception
#ifdef LOOPBACK
if (!rc)
rc = mxfAttributeUint64Set(rx429, KMXF_A429_TX_RX_TEST_LB, VMXF_ENABLE);
#endif
// Set channel speed
if(!rc)
{
rc = mxfAttributeUint64Set(rx429, KMXF_A429_SPEED, 100000);
if(!rc)
rc = mxfAttributeUint64Set(tx429, KMXF_A429_SPEED, 100000);
}
/******************************************************************************/
// Initialize the flash
/******************************************************************************/
// Get flash handle
if(!rc)
rc = mxfEmbeddedNVStorageHandleGet(device, &flash);
if (!rc)
rc = mxfEmbeddedNVStorageInfoGet(flash, &nvInfo);
if (!rc && (nvInfo.type == MXF_EMBEDDED_NVSTORAGE_TYPE_EMMC))
{
needUnmount = TRUE;
}
// Check if a file is already created in flash
if(!rc)
{
rc = mxfEmbeddedNVStorageFileCountGet(flash, &count);
if (rc == MAXT_ERROR_NVSTORAGE_NOT_FORMATTED)
{
printf("Formatting...\n");
printf("Done\n");
count = 0;
}
}
if(!rc)
{
if(count)
{
// Verify if first file is right size
rc = mxfEmbeddedNVStorageFileSizeGet(flash, 0, &fileSize);
if(!rc)
{
// This file is ok, reset it
if(fileSize == FILE_SIZE/nvInfo.sectorSize)
{
fileIdx = 0;
rc = mxfEmbeddedNVStorageFileReset(flash, fileIdx);
if(!rc)
printf("File #%llu reset\n", fileIdx);
}
// Format and create a new file
else
{
printf("Formatting...\n");
printf("Done\n");
if(!rc)
rc = mxfEmbeddedNVStorageFileCreate(flash, FILE_SIZE/nvInfo.sectorSize, &fileIdx);
if(!rc)
printf("File #%llu allocated\n", fileIdx);
}
}
}
else
{
// Allocate file
rc = mxfEmbeddedNVStorageInfoGet(flash, &nvInfo);
if(!rc)
rc = mxfEmbeddedNVStorageFileCreate(flash, FILE_SIZE/nvInfo.sectorSize, &fileIdx);
if(!rc)
printf("File #%llu allocated\n", fileIdx);
}
}
// Enable the recording of all messages
if(!rc)
{
rc = mxfA429EmbeddedNVStorageFileMsgSelectSet(rx429, MXF_MSG_SELECT_ONLY, 0, NULL);
if (!rc)
rc = mxfEmbeddedNVStorageFileMsgSelectEnableSet(rx429, fileIdx, 0, TRUE);
}
if(!rc)
rc = TX429PeriodicScheduling(tx429);
if (!rc)
rc = read429RecordsFromEmbeddedFlash(flash, fileIdx);
if(!rc)
rc = mxfEmbeddedNVStorageFileMsgSelectEnableSet(rx429, fileIdx, 0, FALSE);
if(needUnmount)
// Catch any previous failing function
if(rc)
{
char buffer[256];
if(mxfSystemErrorStringGet(server, rc, sizeof(buffer), buffer))
sprintf (buffer,"ERROR # 0x%08X", rc);
printf("%s\n\r", buffer);
}
// Terminate
printf("\nPress enter to terminate\n");
getchar();
return rc;
}
uint32 TX429PeriodicScheduling(HMXF_CHANNEL txChannel)
{
HMXF_SCHED schedule;
HMXF_SCHED_MSG msg[3]={0,0,0};
uint32 data, ssm=0;
uint32 rc;
uint64 labelMsg0[6]={004,077,0103,0104,0251,0252};
uint64 labelMsg1[2]={0100,0110};
uint64 labelMsg2[3]={0140,0141,0143};
HMXF_BUFFER bufferMsg0[6];
HMXF_BUFFER bufferMsg1[2];
HMXF_BUFFER bufferMsg2[3];
uint32 rec, indexBuffer;
double bnr;
// Set the A429 records and create the schedule
// Create the periodic scheduler
rc = mxfTxPeriodicScheduleNew(txChannel, &schedule);
// Set scheduling values for msg0: Rate=150 ms, Phase=0 us
if(!rc)
rc = mxfTxPeriodicScheduleMsgAdd(schedule, 150000LL, 0, &msg[0]);
// Set record for the buffers of Msg0
for(rec=0; rec<6 && !rc; rec++)
{
rc = mxfTxPeriodicUpdateMsgBufferAlloc(txChannel, labelMsg0[rec], BUFFER_SIZE, &bufferMsg0[rec], NULL);
if(!rc)
{
rec429.timeTag = 0;
rec429.control = 0;
rec429.repeatCount = 1;
rec429.reserved = 0;
data=0;
switch(rec)
{
case 0:
data = 0x40000;
ssm = 0;
break;
case 1:
bnr = 100.123;
mxfA429ArwRealToBnr(128, 18, &bnr, &data);
data >>= 10;
ssm = 3;
break;
case 2:
bnr = 400.126;
mxfA429ArwRealToBnr(512, 12, &bnr, &data);
data >>= 10;
ssm = 3;
break;
case 3:
bnr = -25;
mxfA429ArwRealToBnr(16384, 11, &bnr, &data);
data >>= 10;
ssm = 3;
break;
case 4:
bnr = 2222.2;
mxfA429ArwRealToBnr(4096, 16, &bnr, &data);
data >>= 10;
ssm = 3;
break;
case 5:
bnr = 333.3;
mxfA429ArwRealToBnr(512, 10, &bnr, &data);
data >>= 10;
ssm = 3;
break;
}
rc = mxfA429ArwCompose(labelMsg0[rec], 0, data, ssm, VMXF_A429_PARITY_ODD, &rec429.data);
if(!rc)
rc = mxfA429TxPeriodicUpdateMsgWrite(bufferMsg0[rec], 1, &rec429);
}
}
// Define the number of buffer for the list and link to it
if(!rc)
rc = mxfTxPeriodicScheduleBufferListAdd(msg[0], rec, 0, bufferMsg0);
// Set scheduling values for msg1: Rate=200 ms, Phase=2000 us
if(!rc)
rc = mxfTxPeriodicScheduleMsgAdd(schedule, 200000LL, 2000, &msg[1]);
// Set record for the buffers of Msg1
for(rec=0; rec<2 && !rc; rec++)
{
rc = mxfTxPeriodicUpdateMsgBufferAlloc(txChannel, labelMsg1[rec], BUFFER_SIZE, &bufferMsg1[rec], NULL);
if(!rc)
{
rec429.timeTag = 0;
rec429.control = 0;
rec429.repeatCount = 1;
rec429.reserved = 0;
data = 0;
switch(rec)
{
case 0:
bnr = 45.08;
mxfA429ArwRealToBnr(180, 13, &bnr, &data);
data >>= 10;
ssm = 3;
break;
case 1:
bnr = 44;
mxfA429ArwRealToBnr(180, 13, &bnr, &data);
data >>= 10;
ssm = 3;
break;
}
rc = mxfA429ArwCompose(labelMsg1[rec], 0, data, ssm, VMXF_A429_PARITY_ODD, &rec429.data);
if(!rc)
rc = mxfA429TxPeriodicUpdateMsgWrite(bufferMsg1[rec], 1, &rec429);
}
}
// Define the number of buffer for the list and link to it
if(!rc)
rc = mxfTxPeriodicScheduleBufferListAdd(msg[1], rec, 0, bufferMsg1);
// Set scheduling values for msg2: Rate=75 ms, Phase=3000 us
if(!rc)
rc = mxfTxPeriodicScheduleMsgAdd(schedule, 75000LL, 3000, &msg[2]);
// Set record for the buffers of Msg2
for(rec=0; rec<3 && !rc; rec++)
{
rc = mxfTxPeriodicUpdateMsgBufferAlloc(txChannel, labelMsg2[rec], BUFFER_SIZE, &bufferMsg2[rec], NULL);
if(!rc)
{
rec429.timeTag = 0;
rec429.control = 0;
rec429.repeatCount = 1;
rec429.reserved = 0;
data = 0;
switch(rec)
{
case 0:
bnr = 5;
mxfA429ArwRealToBnr(180, 13, &bnr, &data);
data >>= 10;
ssm = 3;
break;
case 1:
bnr = -2;
mxfA429ArwRealToBnr(180, 13, &bnr, &data);
data >>= 10;
ssm = 3;
break;
case 2:
bnr = 0;
mxfA429ArwRealToBnr(180, 13, &bnr, &data);
data >>= 10;
ssm = 3;
break;
}
rc = mxfA429ArwCompose(labelMsg2[rec], 0, data, ssm, VMXF_A429_PARITY_ODD, &rec429.data);
if(!rc)
rc = mxfA429TxPeriodicUpdateMsgWrite(bufferMsg2[rec], 1, &rec429);
}
}
// Define the number of buffer for the list and link to it
if(!rc)
rc = mxfTxPeriodicScheduleBufferListAdd(msg[2], rec, 0, bufferMsg2);
// Run the scheduler, update records
if(!rc)
{
printf("Running periodic transmission, please wait...\n\r");
// Run the schedule now
rc = mxfTxPeriodicScheduleRun(schedule);
}
// Wait 10 seconds
if(!rc)
{
mxfSleep(10000);
rc = mxfTxPeriodicScheduleFree(schedule);
}
if(!rc)
printf("\n\rTransmission stopped\n\r");
for (indexBuffer = 0; indexBuffer < 6; indexBuffer++)
{
if (bufferMsg0[indexBuffer])
mxfTxPeriodicUpdateMsgBufferFree(bufferMsg0[indexBuffer]);
}
for (indexBuffer = 0; indexBuffer < 2; indexBuffer++)
{
if (bufferMsg1[indexBuffer])
mxfTxPeriodicUpdateMsgBufferFree(bufferMsg1[indexBuffer]);
}
for (indexBuffer = 0; indexBuffer < 3; indexBuffer++)
{
if (bufferMsg2[indexBuffer])
mxfTxPeriodicUpdateMsgBufferFree(bufferMsg2[indexBuffer]);
}
return rc;
}
uint32 read429RecordsFromEmbeddedFlash(HMXF_NVSTORAGE flash, uint64 fileIdx)
{
MXF_A429_DATAREC* rec429, *p;
uint64 msgReadCnt, byteReadCnt;
uint64 iRec, iMsg=0, label, sdi, ssm, parity, data;
uint32 rc=0;
uint64 result;
double bnr;
// Allocate host buffer
rec429 = (MXF_A429_DATAREC*)malloc(BUFFER_SIZE);
if (!rec429)
rc = MAXT_ERROR_MEM;
// Read all the messages on flash in the buffer allocated previously
printf("Reading from flash\n\n");
if(!rc)
{
do
{
rc = mxfA429EmbeddedNVStorageFileMsgRead(flash, fileIdx, 0, BUFFER_SIZE, &msgReadCnt, &byteReadCnt, rec429);
// Display all records written by the embedded application
for (iRec=0, p=rec429; iRec < msgReadCnt; iRec++, iMsg++)
{
// Extract fields from Arinc Word
mxfA429ArwDecompose(p->data, &label, &sdi, &data, &ssm, &parity);
switch(label)
{
case 004:
data >>= 8;
result = (data & 0xf) + (((data >> 4) & 0xf)*10) + (((data >> 8) & 0xf)*100);
result *= 100;
printf("%02llu - time tag=%llu (arw=0x%08x): label=%03llo, sdi=%llu, data=%llu Feet\n\r", iMsg, p->timeTag, p->data, label, sdi, result);
break;
case 077:
mxfA429ArwBnrToReal(p->data, 128, 18, &bnr);
printf("%02llu - time tag=%llu (arw=0x%08x): label=%03llo, sdi=%llu, data=%0.03f MLb-in\n\r", iMsg, p->timeTag, p->data, label, sdi, bnr);
break;
case 0100:
case 0110:
case 0140:
case 0141:
case 0143:
mxfA429ArwBnrToReal(p->data, 180, 13, &bnr);
printf("%02llu - time tag=%llu (arw=0x%08x): label=%03llo, sdi=%llu, data=%0.02f Deg/180\n\r", iMsg, p->timeTag, p->data, label, sdi, bnr);
break;
case 0103:
mxfA429ArwBnrToReal(p->data, 512, 12, &bnr);
printf("%02llu - time tag=%llu (arw=0x%08x): label=%03llo, sdi=%llu, data=%0.02f Knots\n\r", iMsg, p->timeTag, p->data, label, sdi, bnr);
break;
case 0104:
mxfA429ArwBnrToReal(p->data, 16384, 11, &bnr);
printf("%02llu - time tag=%llu (arw=0x%08x): label=%03llo, sdi=%llu, data=%0.02f Ft/Min\n\r", iMsg, p->timeTag, p->data, label, sdi, bnr);
break;
case 0251:
mxfA429ArwBnrToReal(p->data, 4096, 16, &bnr);
printf("%02llu - time tag=%llu (arw=0x%08x): label=%03llo, sdi=%llu, data=%0.03f N.M.\n\r", iMsg, p->timeTag, p->data, label, sdi, bnr);
break;
case 0252:
mxfA429ArwBnrToReal(p->data, 512, 10, &bnr);
printf("%02llu - time tag=%llu (arw=0x%08x): label=%03llo, sdi=%llu, data=%0.01f Min.\n\r", iMsg, p->timeTag, p->data, label, sdi, bnr);
break;
default:
printf("%02llu - time tag=%llu (arw=0x%08x): label=%03llo, sdi=%llu, data=%05llX\n\r", iMsg, p->timeTag, p->data, label, sdi, data);
}
// Get next message pointer in array of records
}
}while(!rc && msgReadCnt);
}
if (rec429)
free(rec429);
return rc;
}
Updated 10/23/2023