Index: /trunk/ppViz/src/ppVizPSF/ppVizPSF.c
===================================================================
--- /trunk/ppViz/src/ppVizPSF/ppVizPSF.c	(revision 26366)
+++ /trunk/ppViz/src/ppVizPSF/ppVizPSF.c	(revision 26367)
@@ -11,4 +11,6 @@
 int main(int argc, char *argv[])
 {
+    ppVizPSFVersionPrint();
+
     ppVizPSFData *data = ppVizPSFDataInit(&argc, argv);
     if (!data) {
Index: /trunk/ppViz/src/ppVizPSF/ppVizPSF.h
===================================================================
--- /trunk/ppViz/src/ppVizPSF/ppVizPSF.h	(revision 26366)
+++ /trunk/ppViz/src/ppVizPSF/ppVizPSF.h	(revision 26367)
@@ -13,4 +13,6 @@
     psString outRoot;                   // Output root name
     float minFlux;                      // Minimum flux for sources
+    int size;                           // Size of PSF image
+    float x, y;                         // Position of fake source
     pmConfig *config;                   // Configuration
 } ppVizPSFData;
Index: /trunk/ppViz/src/ppVizPSF/ppVizPSFArguments.c
===================================================================
--- /trunk/ppViz/src/ppVizPSF/ppVizPSFArguments.c	(revision 26366)
+++ /trunk/ppViz/src/ppVizPSF/ppVizPSFArguments.c	(revision 26367)
@@ -50,11 +50,16 @@
     psMetadataAddStr(arguments, PS_LIST_TAIL, "-psf", 0, "Filename of PSF", NULL);
     psMetadataAddStr(arguments, PS_LIST_TAIL, "-sources", 0, "Filename of sources", NULL);
+    psMetadataAddF32(arguments, PS_LIST_TAIL, "-x", 0, "x position of fake source [0..1]", NAN);
+    psMetadataAddF32(arguments, PS_LIST_TAIL, "-y", 0, "y position of fake source [0..1]", NAN);
     if (argc == 1 || !psArgumentParse(arguments, &argc, argv) || argc != 2) {
         usage(argv[0], arguments, data);
     }
 
+    bool mdok;                          // Status of MD lookup
     data->psfName = psMemIncrRefCounter(psMetadataLookupStr(NULL, arguments, "-psf"));
-    data->sourcesName = psMemIncrRefCounter(psMetadataLookupStr(NULL, arguments, "-sources"));
+    data->sourcesName = psMemIncrRefCounter(psMetadataLookupStr(&mdok, arguments, "-sources"));
     data->outRoot = psStringCopy(argv[1]);
+    data->x = psMetadataLookupF32(NULL, arguments, "-x");
+    data->y = psMetadataLookupF32(NULL, arguments, "-y");
     psMetadataAddStr(data->config->arguments, PS_LIST_TAIL, "OUTPUT", 0, "Output root name", data->outRoot);
 
@@ -63,5 +68,4 @@
 
     PS_ASSERT_STRING_NON_EMPTY(data->psfName, false);
-    PS_ASSERT_STRING_NON_EMPTY(data->sourcesName, false);
     PS_ASSERT_STRING_NON_EMPTY(data->outRoot, false);
 
Index: /trunk/ppViz/src/ppVizPSF/ppVizPSFCamera.c
===================================================================
--- /trunk/ppViz/src/ppVizPSF/ppVizPSFCamera.c	(revision 26366)
+++ /trunk/ppViz/src/ppVizPSF/ppVizPSFCamera.c	(revision 26367)
@@ -39,10 +39,12 @@
     }
 
-    fileArguments("SOURCES", data->sourcesName, "Input sources", data->config);
-    pmFPAfile *srcs = pmFPAfileBindFromArgs(&status, psf, data->config,
-                                            "PSPHOT.INPUT.CMF", "SOURCES"); // File
-    if (!status || !srcs) {
-        psError(PS_ERR_IO, false, "Failed to build file from PSPHOT.INPUT.CMF");
-        return false;
+    if (data->sourcesName) {
+        fileArguments("SOURCES", data->sourcesName, "Input sources", data->config);
+        pmFPAfile *srcs = pmFPAfileBindFromArgs(&status, psf, data->config,
+                                                "PSPHOT.INPUT.CMF", "SOURCES"); // File
+        if (!status || !srcs) {
+            psError(PS_ERR_IO, false, "Failed to build file from PSPHOT.INPUT.CMF");
+            return false;
+        }
     }
 
@@ -67,4 +69,12 @@
     }
 
+    if (!data->sourcesName) {
+        data->size = psMetadataLookupS32(NULL, recipe, "SIZE"); // Size of PSF
+        if (data->size <= 0) {
+            psError(PS_ERR_BAD_PARAMETER_VALUE, true, "Unable to find legitimate value for SIZE");
+            return false;
+        }
+    }
+
     return true;
 }
Index: /trunk/ppViz/src/ppVizPSF/ppVizPSFData.c
===================================================================
--- /trunk/ppViz/src/ppVizPSF/ppVizPSFData.c	(revision 26366)
+++ /trunk/ppViz/src/ppVizPSF/ppVizPSFData.c	(revision 26367)
@@ -31,4 +31,7 @@
     data->config = NULL;
     data->minFlux = NAN;
+    data->size = 0;
+    data->x = NAN;
+    data->y = NAN;
 
     return data;
Index: /trunk/ppViz/src/ppVizPSF/ppVizPSFLoop.c
===================================================================
--- /trunk/ppViz/src/ppVizPSF/ppVizPSFLoop.c	(revision 26366)
+++ /trunk/ppViz/src/ppVizPSF/ppVizPSFLoop.c	(revision 26367)
@@ -13,6 +13,4 @@
     )
 {
-    ppVizPSFVersionPrint();
-
     pmConfig *config = data->config;                                        // Configuration data
     pmFPAfile *psfFile = pmFPAfileSelectSingle(config->files, "PSPHOT.PSF.LOAD", 0); // File with PSF
@@ -47,5 +45,13 @@
             }
 
-            if (cell->readouts->n != 1) {
+            if (cell->readouts->n == 0) {
+                pmReadout *ro = pmReadoutAlloc(cell);
+                ro->data_exists = true;
+                cell->data_exists = true;
+                chip->data_exists = true;
+                psFree(ro);             // Drop reference
+            }
+
+            if (cell->readouts->n > 1) {
                 psWarning("More than one readout present for chip %d, cell %d", view->chip, view->cell);
             }
@@ -61,8 +67,39 @@
                 }
 
-                psArray *sources = psMetadataLookupPtr(NULL, readout->analysis, "PSPHOT.SOURCES"); // Sources
                 pmPSF *psf = psMetadataLookupPtr(NULL, chip->analysis, "PSPHOT.PSF");              // PSF
+                assert(psf);
 
-                int numCols = psf->fieldNx, numRows = psf->fieldNy; // Size of image
+                bool mdok;              // Status of MD lookup
+                psArray *sources = psMetadataLookupPtr(&mdok, readout->analysis, "PSPHOT.SOURCES"); // Sources
+                int numCols = 0, numRows = 0;              // Size of image
+                psVector *xOffset = NULL, *yOffset = NULL; // Offset from source to true position
+                if (sources) {
+                    psMemIncrRefCounter(sources);
+                    numCols = psf->fieldNx;
+                    numRows = psf->fieldNy;
+                    psLogMsg("ppVizPSF", PS_LOG_INFO, "Generating %dx%d image from %ld input sources",
+                             numCols, numRows, sources->n);
+                } else {
+                    sources = psArrayAlloc(1);
+                    pmSource *source = pmSourceAlloc(); // Fake source
+                    sources->data[0] = source;
+                    float xRel = isfinite(data->x) ? data->x : 0.5; // Relative position in x
+                    float yRel = isfinite(data->y) ? data->y : 0.5; // Relative position in y
+                    float xSrc = xRel * psf->fieldNx, ySrc = yRel * psf->fieldNy; // Position of source
+                    source->peak = pmPeakAlloc(xSrc, ySrc, 1.0, PM_PEAK_LONE);
+                    pmModel *model = pmModelFromPSFforXY(psf, xSrc, ySrc, 1.0); // Model for normalisation
+                    float flux = model->modelFlux(model->params);               // Flux for peak=1
+                    psFree(model);
+                    source->psfMag = -2.5 * log10(flux);
+                    xOffset = psVectorAlloc(1, PS_TYPE_S32);
+                    yOffset = psVectorAlloc(1, PS_TYPE_S32);
+                    xOffset->data.S32[0] = data->size - xSrc;
+                    yOffset->data.S32[0] = data->size - ySrc;
+                    numCols = 2 * data->size + 1;
+                    numRows = 2 * data->size + 1;
+                    psLogMsg("ppVizPSF", PS_LOG_INFO, "Generating %dx%d image with single PSF",
+                             numCols, numRows);
+                }
+
                 if (numCols <= 0 || numRows <= 0) {
                     psError(PS_ERR_UNKNOWN, true, "Image size isn't set: %dx%d.", numCols, numRows);
@@ -70,9 +107,13 @@
                 }
 
-                if (!pmReadoutFakeFromSources(readout, numCols, numRows, sources, 0, NULL, NULL, psf,
+                if (!pmReadoutFakeFromSources(readout, numCols, numRows, sources, 0, xOffset, yOffset, psf,
                                               data->minFlux, 0, false, true)) {
                     psError(PS_ERR_UNKNOWN, false, "Unable to generate fake readout.");
                     return false;
                 }
+
+                psFree(sources);
+                psFree(xOffset);
+                psFree(yOffset);
 
                 pmHDU *hdu = pmHDUGetLowest(psfFile->fpa, chip, cell); // HDU for readout
