Index: trunk/ppMerge/src/ppMerge.h
===================================================================
--- trunk/ppMerge/src/ppMerge.h	(revision 23165)
+++ trunk/ppMerge/src/ppMerge.h	(revision 23166)
@@ -28,7 +28,7 @@
 /// @{
 
-#define TIMERNAME "ppMerge"             ///< Name for timer 
-#define PPMERGE_RECIPE "PPMERGE"        ///< Recipe name 
-#define THREADED 1                      ///< Compile with threads? 
+#define TIMERNAME "ppMerge"             ///< Name for timer
+#define PPMERGE_RECIPE "PPMERGE"        ///< Recipe name
+#define THREADED 1                      ///< Compile with threads?
 
 /**
@@ -36,11 +36,11 @@
  */
 typedef enum {
-    PPMERGE_TYPE_UNKNOWN,               ///< Unknown type 
-    PPMERGE_TYPE_BIAS,                  ///< Bias frame 
-    PPMERGE_TYPE_DARK,                  ///< (Multi-)Dark frame 
-    PPMERGE_TYPE_MASK,                  ///< Mask frame 
-    PPMERGE_TYPE_SHUTTER,               ///< Shutter frame 
+    PPMERGE_TYPE_UNKNOWN,               ///< Unknown type
+    PPMERGE_TYPE_BIAS,                  ///< Bias frame
+    PPMERGE_TYPE_DARK,                  ///< (Multi-)Dark frame
+    PPMERGE_TYPE_MASK,                  ///< Mask frame
+    PPMERGE_TYPE_SHUTTER,               ///< Shutter frame
     PPMERGE_TYPE_FLAT,                  ///< Flat-field frame (dome or sky)
-    PPMERGE_TYPE_FRINGE,                ///< Fringe frame 
+    PPMERGE_TYPE_FRINGE,                ///< Fringe frame
 } ppMergeType;
 
@@ -49,7 +49,7 @@
  */
 typedef enum {
-    PPMERGE_FILES_ALL,                  ///< All files 
-    PPMERGE_FILES_INPUT,                ///< Input files 
-    PPMERGE_FILES_OUTPUT                ///< Output files 
+    PPMERGE_FILES_ALL,                  ///< All files
+    PPMERGE_FILES_INPUT,                ///< Input files
+    PPMERGE_FILES_OUTPUT                ///< Output files
 } ppMergeFiles;
 
@@ -60,9 +60,9 @@
  */
 typedef struct {
-    psArray *readouts;                  ///< Input readouts 
-    bool read;                          ///< Has the scan been read? 
-    bool busy;                          ///< Is the scan being processed? 
-    int firstScan;                      ///< First row of the chunk to be read for this group 
-    int lastScan;                       ///< Last row of the chunk to be read for this group 
+    psArray *readouts;                  ///< Input readouts
+    bool read;                          ///< Has the scan been read?
+    bool busy;                          ///< Is the scan being processed?
+    int firstScan;                      ///< First row of the chunk to be read for this group
+    int lastScan;                       ///< Last row of the chunk to be read for this group
 } ppMergeFileGroup;
 
@@ -71,5 +71,5 @@
  */
 bool ppMergeArguments(int argc, char *argv[], ///< Command-line arguments
-                      pmConfig *config  ///< Configuration 
+                      pmConfig *config  ///< Configuration
     );
 
@@ -77,5 +77,5 @@
  * Set up camera files
  */
-bool ppMergeCamera(pmConfig *config     ///< Configuration 
+bool ppMergeCamera(pmConfig *config     ///< Configuration
     );
 
@@ -83,5 +83,5 @@
  * Measure scale and zero-points
  */
-bool ppMergeScaleZero(pmConfig *config  ///< Configuration 
+bool ppMergeScaleZero(pmConfig *config  ///< Configuration
     );
 
@@ -89,5 +89,5 @@
  * Main loop to do the merging
  */
-bool ppMergeLoop(pmConfig *config       ///< Configuration 
+bool ppMergeLoop(pmConfig *config       ///< Configuration
     );
 
@@ -95,5 +95,5 @@
  * Main loop for masks
  */
-bool ppMergeMask(pmConfig *config       ///< Configuration 
+bool ppMergeMask(pmConfig *config       ///< Configuration
     );
 
@@ -101,8 +101,8 @@
  * Read nominated input file
  */
-bool ppMergeFileReadInput(pmConfig *config, ///< Configuration 
-                          pmReadout *readout, ///< Readout into which to read 
-                          int num,      ///< Number of file in sequence 
-                          int rows      ///< Number of rows to read at once 
+bool ppMergeFileReadInput(pmConfig *config, ///< Configuration
+                          pmReadout *readout, ///< Readout into which to read
+                          int num,      ///< Number of file in sequence
+                          int rows      ///< Number of rows to read at once
     );
 
@@ -110,7 +110,7 @@
  * Open nominated input file
  */
-bool ppMergeFileOpenInput(pmConfig *config, ///< Configuration 
-                          const pmFPAview *view, ///< View to open 
-                          int num       ///< Number of file in sequence 
+bool ppMergeFileOpenInput(pmConfig *config, ///< Configuration
+                          const pmFPAview *view, ///< View to open
+                          int num       ///< Number of file in sequence
     );
 
@@ -166,4 +166,19 @@
 bool ppMergeSetThreads(void);
 
+
+/// Return software version
+psString ppMergeVersion(void);
+
+/// Return software source
+psString ppMergeSource(void);
+
+/// Return detailed software version information
+psString ppMergeVersionLong(void);
+
+/// Populate a FITS header with version information
+bool ppMergeVersionHeader(
+    psMetadata *header                  ///< Header to populate
+    );
+
 ///@}
 #endif
Index: trunk/ppMerge/src/ppMergeLoop_Threaded.c
===================================================================
--- trunk/ppMerge/src/ppMergeLoop_Threaded.c	(revision 23165)
+++ trunk/ppMerge/src/ppMergeLoop_Threaded.c	(revision 23166)
@@ -137,4 +137,5 @@
     assert(output && output->fpa);
     pmFPA *outFPA = output->fpa;        ///< Output FPA
+    pmHDU *lastHDU = NULL;              // Last HDU that was updated
     int cellNum = 0;                    ///< Index of cell
     if (!pmFPAfileIOChecks(config, view, PM_FPA_BEFORE)) {
@@ -156,4 +157,13 @@
                 // No data here
                 continue;
+            }
+
+            // Update the header
+            {
+                pmHDU *hdu = pmHDUGetHighest(outFPA, outChip, outCell); // HDU for file
+                if (hdu && hdu != lastHDU) {
+                    ppMergeVersionHeader(hdu->header);
+                    lastHDU = hdu;
+                }
             }
 
Index: trunk/ppMerge/src/ppMergeMask.c
===================================================================
--- trunk/ppMerge/src/ppMergeMask.c	(revision 23165)
+++ trunk/ppMerge/src/ppMergeMask.c	(revision 23166)
@@ -16,4 +16,5 @@
                       const pmFPAview *view, ///< View to chip
                       bool writeOut,     ///< Write output?
+                      pmHDU **lastHDU,   ///< HDU last updated
                       psRandom *rng,    ///< Random number generator
                       psMetadata *stats ///< Statistics output
@@ -96,4 +97,13 @@
                     i, inView->chip, inView->cell);
 
+            // Update the header
+            {
+                pmHDU *hdu = pmHDUGetHighest(outCell->parent->parent, outCell->parent, outCell); // File HDU
+                if (hdu && hdu != *lastHDU) {
+                    ppMergeVersionHeader(hdu->header);
+                    *lastHDU = hdu;
+                }
+            }
+
             if (!pmFPAfileIOChecks(config, inView, PM_FPA_BEFORE)) {
                 psFree(inView);
@@ -395,4 +405,5 @@
     assert(output && output->fpa);
     pmFPA *outFPA = output->fpa;        ///< Output FPA
+    pmHDU *lastHDU = NULL;              // Last HDU updated
 
     if (!pmFPAfileIOChecks(config, view, PM_FPA_BEFORE)) {
@@ -409,5 +420,5 @@
 
         for (int i = 0; i < iter; i++) {
-            if (!mergeMask(config, view, (i == iter - 1), rng, stats)) {
+            if (!mergeMask(config, view, (i == iter - 1), &lastHDU, rng, stats)) {
                 psError(PS_ERR_UNKNOWN, false, "Unable to merge chip %d", view->chip);
                 goto PPMERGE_MASK_ERROR;
Index: trunk/ppMerge/src/ppMergeVersion.c
===================================================================
--- trunk/ppMerge/src/ppMergeVersion.c	(revision 23165)
+++ trunk/ppMerge/src/ppMergeVersion.c	(revision 23166)
@@ -59,32 +59,33 @@
 };
 
-void ppMergeVersionMetadata(psMetadata *metadata)
+bool ppMergeVersionHeader(psMetadata *header)
 {
-    PS_ASSERT_METADATA_NON_NULL(metadata,);
+    PS_ASSERT_METADATA_NON_NULL(header, false);
 
-    psString pslib = psLibVersionLong();///< psLib version
-    psString psmodules = psModulesVersionLong(); ///< psModules version
-    psString ppStats = ppStatsVersionLong(); ///< ppStats version
-    psString ppMerge = ppMergeVersionLong(); ///< ppMerge version
+    psTime *time = psTimeGetNow(PS_TIME_TAI); // The time now
+    psString timeString = psTimeToISO(time); // The time in an ISO string
+    psFree(time);
+    psString history = NULL;               // History string
+    psStringAppend(&history, "ppMerge at %s", timeString);
+    psFree(timeString);
+    psMetadataAddStr(header, PS_LIST_TAIL, "HISTORY", PS_META_DUPLICATE_OK, NULL, history);
+    psFree(history);
 
-    psTime *time = psTimeGetNow(PS_TIME_TAI); ///< The time now
-    psString timeString = psTimeToISO(time); ///< The time in an ISO string
-    psFree(time);
-    psString head = NULL;               ///< Head string
-    psStringAppend(&head, "ppMerge processing at %s. Component information:", timeString);
-    psFree(timeString);
+    psLibVersionHeader(header);
+    psModulesVersionHeader(header);
+    ppStatsVersionHeader(header);
 
-    psMetadataAddStr(metadata, PS_LIST_TAIL, "HISTORY", PS_META_DUPLICATE_OK, head, "");
-    psMetadataAddStr(metadata, PS_LIST_TAIL, "HISTORY", PS_META_DUPLICATE_OK, pslib, "");
-    psMetadataAddStr(metadata, PS_LIST_TAIL, "HISTORY", PS_META_DUPLICATE_OK, psmodules, "");
-    psMetadataAddStr(metadata, PS_LIST_TAIL, "HISTORY", PS_META_DUPLICATE_OK, ppStats, "");
-    psMetadataAddStr(metadata, PS_LIST_TAIL, "HISTORY", PS_META_DUPLICATE_OK, ppMerge, "");
+    psString version = ppMergeVersion(); // Software version
+    psString source  = ppMergeSource();  // Software source
 
-    psFree(head);
-    psFree(pslib);
-    psFree(psmodules);
-    psFree(ppStats);
-    psFree(ppMerge);
+    psMetadataAddStr(header, PS_LIST_TAIL, "IPP.PPMERGE.VERSION", PS_META_REPLACE,
+                     "Software version", version);
+    psMetadataAddStr(header, PS_LIST_TAIL, "IPP.PPMERGE.SOURCE", PS_META_REPLACE,
+                     "S/W source", source);
 
-    return;
+    psFree(version);
+    psFree(source);
+
+    return true;
 }
+
